You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

457 lines
16 KiB

/* /////////////////////////////////////////////////////////////////////////
* File: rangelib/integral_range.hpp
*
* Purpose: Integral range class.
*
* Created: 4th November 2003
* Updated: 5th March 2011
*
* Home: http://stlsoft.org/
*
* Copyright (c) 2003-2011, Matthew Wilson and Synesis Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
* any contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ////////////////////////////////////////////////////////////////////// */
/** \file rangelib/integral_range.hpp Integral range class */
#ifndef RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE
#define RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_MAJOR 2
# define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_MINOR 6
# define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_REVISION 5
# define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_EDIT 56
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/* /////////////////////////////////////////////////////////////////////////
* Auto-generation and compatibility
*/
/*
[Incompatibilies-start]
STLSOFT_COMPILER_IS_MSVC: _MSC_VER < 1200
STLSOFT_COMPILER_IS_MWERKS: (__MWERKS__ & 0xFF00) < 0x3000
[Incompatibilies-end]
*/
/* /////////////////////////////////////////////////////////////////////////
* Includes
*/
#ifndef RANGELIB_INCL_RANGELIB_HPP_RANGELIB
# include <rangelib/rangelib.hpp>
#endif /* !RANGELIB_INCL_RANGELIB_HPP_RANGELIB */
#ifndef RANGELIB_INCL_RANGELIB_HPP_RANGE_CATEGORIES
# include <rangelib/range_categories.hpp>
#endif /* !RANGELIB_INCL_RANGELIB_HPP_RANGE_CATEGORIES */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL
# include <stlsoft/util/operator_bool.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL */
#ifndef STLSOFT_INCL_STLSOFT_ERROR_HPP_EXCEPTIONS
# include <stlsoft/error/exceptions.hpp> // for null_exception_policy
#endif /* !STLSOFT_INCL_STLSOFT_ERROR_HPP_EXCEPTIONS */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS
# include <stlsoft/util/constraints.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS */
#ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_CHARACTER_TYPE
# include <stlsoft/meta/is_character_type.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_CHARACTER_TYPE */
#ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_INTEGRAL_TYPE
# include <stlsoft/meta/is_integral_type.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_INTEGRAL_TYPE */
#ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_NUMERIC_TYPE
# include <stlsoft/meta/is_numeric_type.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_NUMERIC_TYPE */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_PRINTF_TRAITS
# include <stlsoft/util/printf_traits.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_PRINTF_TRAITS */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP
# include <stlsoft/util/std_swap.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP */
#ifndef STLSOFT_INCL_STDEXCEPT
# define STLSOFT_INCL_STDEXCEPT
# include <stdexcept> // for std::out_of_range
#endif /* !STLSOFT_INCL_STDEXCEPT */
#ifndef STLSOFT_INCL_H_STDIO
# define STLSOFT_INCL_H_STDIO
# include <stdio.h> // for sprintf
#endif /* !STLSOFT_INCL_H_STDIO */
/* /////////////////////////////////////////////////////////////////////////
* Namespace
*/
#ifndef RANGELIB_NO_NAMESPACE
# if defined(_STLSOFT_NO_NAMESPACE) || \
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::rangelib */
namespace rangelib
{
# else
/* Define stlsoft::rangelib_project */
namespace stlsoft
{
namespace rangelib_project
{
# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !RANGELIB_NO_NAMESPACE */
/* /////////////////////////////////////////////////////////////////////////
* Classes
*/
/** \brief Error policy class for integral_range
*
* \ingroup group__library__rangelib
*/
struct invalid_integral_range_policy
{
public:
/// The thrown type
typedef std::out_of_range thrown_type;
public:
/// Function call operator, taking three 32-bit signed integer parameters
void operator ()(ss_sint32_t first, ss_sint32_t last, ss_sint32_t increment) const
{
static const char s_format[] = "Invalid integral range [%ld, %ld), %ld";
char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_format)];
const ss_size_t cch = static_cast<ss_size_t>(::sprintf(message, s_format, long(first), long(last), long(increment)));
STLSOFT_ASSERT(cch < STLSOFT_NUM_ELEMENTS(message));
STLSOFT_SUPPRESS_UNUSED(cch);
STLSOFT_THROW_X(thrown_type(message));
}
/// Function call operator, taking three 32-bit unsigned integer parameters
void operator ()(ss_uint32_t first, ss_uint32_t last, ss_uint32_t increment) const
{
static const char s_format[] = "Invalid integral range [%lu, %lu), %lu";
char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_format)];
const ss_size_t cch = static_cast<ss_size_t>(::sprintf(message, "Invalid integral range [%lu, %lu), %lu", ss_ulong_t(first), ss_ulong_t(last), ss_ulong_t(increment)));
STLSOFT_ASSERT(cch < STLSOFT_NUM_ELEMENTS(message));
STLSOFT_SUPPRESS_UNUSED(cch);
#if defined(STLSOFT_COMPILER_IS_COMO)
STLSOFT_SUPPRESS_UNUSED(s_format);
#endif /* compiler */
STLSOFT_THROW_X(thrown_type(message));
}
#ifdef STLSOFT_CF_INT_DISTINCT_INT_TYPE
/// Function call operator, taking three 32-bit signed integer parameters
void operator ()(int first, int last, int increment) const
{
operator ()(ss_sint32_t(first), ss_sint32_t(last), ss_sint32_t(increment));
}
/// Function call operator, taking three 32-bit unsigned integer parameters
void operator ()(unsigned int first, unsigned int last, unsigned int increment) const
{
operator ()(ss_uint32_t(first), ss_uint32_t(last), ss_uint32_t(increment));
}
#endif /* STLSOFT_CF_INT_DISTINCT_INT_TYPE */
#ifdef STLSOFT_CF_64BIT_INT_SUPPORT
private:
static char const* format_sint64()
{
return printf_traits<ss_sint64_t>::format_a();
}
static char const* format_uint64()
{
return printf_traits<ss_uint64_t>::format_a();
}
public:
/// Function call operator, taking three 64-bit signed integer parameters
void operator ()(ss_sint64_t first, ss_sint64_t last, ss_sint64_t increment) const
{
static const char s_fmtfmt[] = "Invalid integral range [%s, %s), %s";
char format[3 * 4 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
const ss_size_t cch1 = static_cast<ss_size_t>(::sprintf(format, s_fmtfmt, format_sint64(), format_sint64(), format_sint64()));
const ss_size_t cch2 = static_cast<ss_size_t>(::sprintf(message, format, first, last, increment));
STLSOFT_ASSERT(cch1 < STLSOFT_NUM_ELEMENTS(format));
STLSOFT_SUPPRESS_UNUSED(cch1);
STLSOFT_ASSERT(cch2 < STLSOFT_NUM_ELEMENTS(message));
STLSOFT_SUPPRESS_UNUSED(cch2);
STLSOFT_THROW_X(thrown_type(message));
}
/// Function call operator, taking three 64-bit unsigned integer parameters
void operator ()(ss_uint64_t first, ss_uint64_t last, ss_uint64_t increment) const
{
static const char s_fmtfmt[] = "Invalid integral range [%s, %s), %s";
char format[3 * 4 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
const ss_size_t cch1 = static_cast<ss_size_t>(::sprintf(format, s_fmtfmt, format_uint64(), format_uint64(), format_uint64()));
const ss_size_t cch2 = static_cast<ss_size_t>(::sprintf(message, format, first, last, increment));
STLSOFT_ASSERT(cch1 < STLSOFT_NUM_ELEMENTS(format));
STLSOFT_SUPPRESS_UNUSED(cch1);
STLSOFT_ASSERT(cch2 < STLSOFT_NUM_ELEMENTS(message));
STLSOFT_SUPPRESS_UNUSED(cch2);
STLSOFT_THROW_X(thrown_type(message));
}
#endif /* STLSOFT_CF_64BIT_INT_SUPPORT */
};
/** \brief This range class represents an integral range.
*
* \ingroup group__library__rangelib
*
* It is categoried as a Notional Range
*
* It could be used as follows
\code
// Create a range of integer values, in the range [-100, 200), in increments of 5
stlsoft::integral_range<int> r(-100, +100, 5);
// Calculate the total
int total = stlsoft::r_accumulate(r, 0);
\endcode
*/
template< ss_typename_param_k T
#ifdef STLSOFT_CF_EXCEPTION_SUPPORT
, ss_typename_param_k XP = invalid_integral_range_policy
#else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */
, ss_typename_param_k XP = null_exception_policy
#endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
>
class integral_range
: public notional_range_tag
{
/// \name Types
/// @{
public:
typedef T value_type;
typedef T const& const_reference;
typedef XP exception_policy_type;
typedef notional_range_tag range_tag_type;
typedef integral_range<T, XP> class_type;
/// @}
/// \name Construction
/// @{
public:
/// Constructs from a start and end position, and an increment
integral_range(value_type first, value_type last, value_type increment = +1)
: m_position(first)
, m_last(last)
, m_increment(increment)
{
if(m_last < m_position)
{
// std_swap(m_position, m_last);
if(m_increment > 0)
{
m_increment = -m_increment;
}
}
validate_range(m_position, m_last, m_increment);
}
/// Destructor
~integral_range() stlsoft_throw_0()
{
// These constraints are to ensure that this template is not used
// for any other types, especially floating point types!!
STLSOFT_STATIC_ASSERT(0 != is_integral_type<value_type>::value);
STLSOFT_STATIC_ASSERT(0 != is_numeric_type<value_type>::value || 0 != is_character_type<value_type>::value);
}
/// @}
/// \name Notional Range methods
/// @{
private:
STLSOFT_DEFINE_OPERATOR_BOOL_TYPES_T(class_type, operator_bool_generator_type, operator_bool_type);
public:
/// Indicates whether the range is open
ss_bool_t is_open() const
{
return m_position != m_last;
}
/// Returns the current value in the range
const_reference current() const
{
STLSOFT_MESSAGE_ASSERT("Attempting to access the value of a closed range", is_open());
return m_position;
}
/// Advances the current position in the range
class_type& advance()
{
STLSOFT_MESSAGE_ASSERT("Attempting to advance a closed range", is_open());
STLSOFT_MESSAGE_ASSERT("Attempting to increment the range past its end point", ((m_increment > 0 && m_position < m_last) || (m_increment < 0 && m_position > m_last)));
m_position += m_increment;
return *this;
}
/// Indicates whether the range is open
operator operator_bool_type() const
{
return operator_bool_generator_type::translate(is_open());
}
/// Returns the current value in the range
const_reference operator *() const
{
return current();
}
/// Advances the current position in the range
class_type& operator ++()
{
return advance();
}
/// Advances the current position in the range, returning a copy of the
/// range prior to its being advanced
class_type operator ++(int)
{
class_type ret(*this);
operator ++();
return ret;
}
/// @}
/// \name Comparison
/// @{
public:
/// Evaluates whether two ranges are equal
bool operator ==(class_type const& rhs) const
{
STLSOFT_MESSAGE_ASSERT("Comparing unrelated ranges!", m_last == rhs.m_last);
return m_position == rhs.m_position;
}
/// Evaluates whether two ranges are unequal
bool operator !=(class_type const& rhs) const
{
return ! operator ==(rhs);
}
/// @}
// Implementation
private:
static void validate_range(value_type first, value_type last, value_type increment)
{
ss_bool_t bValid = true;
// Check modulus
if(bValid)
{
if( first != last &&
0 != increment)
{
bValid = (0 == ((last - first) % increment));
}
}
// Check direction
if(bValid)
{
if( ( last < first &&
increment > 0) ||
( first < last &&
increment < 0))
{
bValid = false;
}
}
// STLSOFT_MESSAGE_ASSERT("The range you have specified will not close with the given increment", (first == last || (increment > 0 && last > first) || (increment < 0 && last < first)));
// STLSOFT_MESSAGE_ASSERT("The range you have specified will not close with the given increment", 0 == ((last - first) % increment));
if(!bValid)
{
exception_policy_type()(first, last, increment);
}
// Assert here, in case using a null exception policy
STLSOFT_MESSAGE_ASSERT("invalid integral range", bValid);
}
// Members
private:
value_type m_position;
value_type m_last;
value_type m_increment;
};
/* /////////////////////////////////////////////////////////////////////////
* Creator functions
*/
template <ss_typename_param_k T>
inline integral_range<T> make_integral_range(T first, T last)
{
return integral_range<T>(first, last);
}
template <ss_typename_param_k T>
inline integral_range<T> make_integral_range(T first, T last, T increment)
{
return integral_range<T>(first, last, increment);
}
////////////////////////////////////////////////////////////////////////////
// Unit-testing
#ifdef STLSOFT_UNITTEST
# include "./unittest/integral_range_unittest_.h"
#endif /* STLSOFT_UNITTEST */
/* ////////////////////////////////////////////////////////////////////// */
#ifndef RANGELIB_NO_NAMESPACE
# if defined(_STLSOFT_NO_NAMESPACE) || \
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace rangelib
# else
} // namespace rangelib_project
} // namespace stlsoft
# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !RANGELIB_NO_NAMESPACE */
/* ////////////////////////////////////////////////////////////////////// */
#endif /* !RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE */
/* ///////////////////////////// end of file //////////////////////////// */