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

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: rangelib/integral_range.hpp
  3. *
  4. * Purpose: Integral range class.
  5. *
  6. * Created: 4th November 2003
  7. * Updated: 5th March 2011
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2003-2011, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file rangelib/integral_range.hpp Integral range class */
  40. #ifndef RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE
  41. #define RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE
  42. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  43. # define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_MAJOR 2
  44. # define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_MINOR 6
  45. # define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_REVISION 5
  46. # define RANGELIB_VER_RANGELIB_HPP_INTEGRAL_RANGE_EDIT 56
  47. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  48. /* /////////////////////////////////////////////////////////////////////////
  49. * Auto-generation and compatibility
  50. */
  51. /*
  52. [Incompatibilies-start]
  53. STLSOFT_COMPILER_IS_MSVC: _MSC_VER < 1200
  54. STLSOFT_COMPILER_IS_MWERKS: (__MWERKS__ & 0xFF00) < 0x3000
  55. [Incompatibilies-end]
  56. */
  57. /* /////////////////////////////////////////////////////////////////////////
  58. * Includes
  59. */
  60. #ifndef RANGELIB_INCL_RANGELIB_HPP_RANGELIB
  61. # include <rangelib/rangelib.hpp>
  62. #endif /* !RANGELIB_INCL_RANGELIB_HPP_RANGELIB */
  63. #ifndef RANGELIB_INCL_RANGELIB_HPP_RANGE_CATEGORIES
  64. # include <rangelib/range_categories.hpp>
  65. #endif /* !RANGELIB_INCL_RANGELIB_HPP_RANGE_CATEGORIES */
  66. #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL
  67. # include <stlsoft/util/operator_bool.hpp>
  68. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_OPERATOR_BOOL */
  69. #ifndef STLSOFT_INCL_STLSOFT_ERROR_HPP_EXCEPTIONS
  70. # include <stlsoft/error/exceptions.hpp> // for null_exception_policy
  71. #endif /* !STLSOFT_INCL_STLSOFT_ERROR_HPP_EXCEPTIONS */
  72. #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS
  73. # include <stlsoft/util/constraints.hpp>
  74. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS */
  75. #ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_CHARACTER_TYPE
  76. # include <stlsoft/meta/is_character_type.hpp>
  77. #endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_CHARACTER_TYPE */
  78. #ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_INTEGRAL_TYPE
  79. # include <stlsoft/meta/is_integral_type.hpp>
  80. #endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_INTEGRAL_TYPE */
  81. #ifndef STLSOFT_INCL_STLSOFT_META_HPP_IS_NUMERIC_TYPE
  82. # include <stlsoft/meta/is_numeric_type.hpp>
  83. #endif /* !STLSOFT_INCL_STLSOFT_META_HPP_IS_NUMERIC_TYPE */
  84. #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_PRINTF_TRAITS
  85. # include <stlsoft/util/printf_traits.hpp>
  86. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_PRINTF_TRAITS */
  87. #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP
  88. # include <stlsoft/util/std_swap.hpp>
  89. #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP */
  90. #ifndef STLSOFT_INCL_STDEXCEPT
  91. # define STLSOFT_INCL_STDEXCEPT
  92. # include <stdexcept> // for std::out_of_range
  93. #endif /* !STLSOFT_INCL_STDEXCEPT */
  94. #ifndef STLSOFT_INCL_H_STDIO
  95. # define STLSOFT_INCL_H_STDIO
  96. # include <stdio.h> // for sprintf
  97. #endif /* !STLSOFT_INCL_H_STDIO */
  98. /* /////////////////////////////////////////////////////////////////////////
  99. * Namespace
  100. */
  101. #ifndef RANGELIB_NO_NAMESPACE
  102. # if defined(_STLSOFT_NO_NAMESPACE) || \
  103. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  104. /* There is no stlsoft namespace, so must define ::rangelib */
  105. namespace rangelib
  106. {
  107. # else
  108. /* Define stlsoft::rangelib_project */
  109. namespace stlsoft
  110. {
  111. namespace rangelib_project
  112. {
  113. # endif /* _STLSOFT_NO_NAMESPACE */
  114. #endif /* !RANGELIB_NO_NAMESPACE */
  115. /* /////////////////////////////////////////////////////////////////////////
  116. * Classes
  117. */
  118. /** \brief Error policy class for integral_range
  119. *
  120. * \ingroup group__library__rangelib
  121. */
  122. struct invalid_integral_range_policy
  123. {
  124. public:
  125. /// The thrown type
  126. typedef std::out_of_range thrown_type;
  127. public:
  128. /// Function call operator, taking three 32-bit signed integer parameters
  129. void operator ()(ss_sint32_t first, ss_sint32_t last, ss_sint32_t increment) const
  130. {
  131. static const char s_format[] = "Invalid integral range [%ld, %ld), %ld";
  132. char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_format)];
  133. const ss_size_t cch = static_cast<ss_size_t>(::sprintf(message, s_format, long(first), long(last), long(increment)));
  134. STLSOFT_ASSERT(cch < STLSOFT_NUM_ELEMENTS(message));
  135. STLSOFT_SUPPRESS_UNUSED(cch);
  136. STLSOFT_THROW_X(thrown_type(message));
  137. }
  138. /// Function call operator, taking three 32-bit unsigned integer parameters
  139. void operator ()(ss_uint32_t first, ss_uint32_t last, ss_uint32_t increment) const
  140. {
  141. static const char s_format[] = "Invalid integral range [%lu, %lu), %lu";
  142. char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_format)];
  143. 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)));
  144. STLSOFT_ASSERT(cch < STLSOFT_NUM_ELEMENTS(message));
  145. STLSOFT_SUPPRESS_UNUSED(cch);
  146. #if defined(STLSOFT_COMPILER_IS_COMO)
  147. STLSOFT_SUPPRESS_UNUSED(s_format);
  148. #endif /* compiler */
  149. STLSOFT_THROW_X(thrown_type(message));
  150. }
  151. #ifdef STLSOFT_CF_INT_DISTINCT_INT_TYPE
  152. /// Function call operator, taking three 32-bit signed integer parameters
  153. void operator ()(int first, int last, int increment) const
  154. {
  155. operator ()(ss_sint32_t(first), ss_sint32_t(last), ss_sint32_t(increment));
  156. }
  157. /// Function call operator, taking three 32-bit unsigned integer parameters
  158. void operator ()(unsigned int first, unsigned int last, unsigned int increment) const
  159. {
  160. operator ()(ss_uint32_t(first), ss_uint32_t(last), ss_uint32_t(increment));
  161. }
  162. #endif /* STLSOFT_CF_INT_DISTINCT_INT_TYPE */
  163. #ifdef STLSOFT_CF_64BIT_INT_SUPPORT
  164. private:
  165. static char const* format_sint64()
  166. {
  167. return printf_traits<ss_sint64_t>::format_a();
  168. }
  169. static char const* format_uint64()
  170. {
  171. return printf_traits<ss_uint64_t>::format_a();
  172. }
  173. public:
  174. /// Function call operator, taking three 64-bit signed integer parameters
  175. void operator ()(ss_sint64_t first, ss_sint64_t last, ss_sint64_t increment) const
  176. {
  177. static const char s_fmtfmt[] = "Invalid integral range [%s, %s), %s";
  178. char format[3 * 4 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
  179. char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
  180. const ss_size_t cch1 = static_cast<ss_size_t>(::sprintf(format, s_fmtfmt, format_sint64(), format_sint64(), format_sint64()));
  181. const ss_size_t cch2 = static_cast<ss_size_t>(::sprintf(message, format, first, last, increment));
  182. STLSOFT_ASSERT(cch1 < STLSOFT_NUM_ELEMENTS(format));
  183. STLSOFT_SUPPRESS_UNUSED(cch1);
  184. STLSOFT_ASSERT(cch2 < STLSOFT_NUM_ELEMENTS(message));
  185. STLSOFT_SUPPRESS_UNUSED(cch2);
  186. STLSOFT_THROW_X(thrown_type(message));
  187. }
  188. /// Function call operator, taking three 64-bit unsigned integer parameters
  189. void operator ()(ss_uint64_t first, ss_uint64_t last, ss_uint64_t increment) const
  190. {
  191. static const char s_fmtfmt[] = "Invalid integral range [%s, %s), %s";
  192. char format[3 * 4 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
  193. char message[3 * 21 + STLSOFT_NUM_ELEMENTS(s_fmtfmt)];
  194. const ss_size_t cch1 = static_cast<ss_size_t>(::sprintf(format, s_fmtfmt, format_uint64(), format_uint64(), format_uint64()));
  195. const ss_size_t cch2 = static_cast<ss_size_t>(::sprintf(message, format, first, last, increment));
  196. STLSOFT_ASSERT(cch1 < STLSOFT_NUM_ELEMENTS(format));
  197. STLSOFT_SUPPRESS_UNUSED(cch1);
  198. STLSOFT_ASSERT(cch2 < STLSOFT_NUM_ELEMENTS(message));
  199. STLSOFT_SUPPRESS_UNUSED(cch2);
  200. STLSOFT_THROW_X(thrown_type(message));
  201. }
  202. #endif /* STLSOFT_CF_64BIT_INT_SUPPORT */
  203. };
  204. /** \brief This range class represents an integral range.
  205. *
  206. * \ingroup group__library__rangelib
  207. *
  208. * It is categoried as a Notional Range
  209. *
  210. * It could be used as follows
  211. \code
  212. // Create a range of integer values, in the range [-100, 200), in increments of 5
  213. stlsoft::integral_range<int> r(-100, +100, 5);
  214. // Calculate the total
  215. int total = stlsoft::r_accumulate(r, 0);
  216. \endcode
  217. */
  218. template< ss_typename_param_k T
  219. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  220. , ss_typename_param_k XP = invalid_integral_range_policy
  221. #else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */
  222. , ss_typename_param_k XP = null_exception_policy
  223. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  224. >
  225. class integral_range
  226. : public notional_range_tag
  227. {
  228. /// \name Types
  229. /// @{
  230. public:
  231. typedef T value_type;
  232. typedef T const& const_reference;
  233. typedef XP exception_policy_type;
  234. typedef notional_range_tag range_tag_type;
  235. typedef integral_range<T, XP> class_type;
  236. /// @}
  237. /// \name Construction
  238. /// @{
  239. public:
  240. /// Constructs from a start and end position, and an increment
  241. integral_range(value_type first, value_type last, value_type increment = +1)
  242. : m_position(first)
  243. , m_last(last)
  244. , m_increment(increment)
  245. {
  246. if(m_last < m_position)
  247. {
  248. // std_swap(m_position, m_last);
  249. if(m_increment > 0)
  250. {
  251. m_increment = -m_increment;
  252. }
  253. }
  254. validate_range(m_position, m_last, m_increment);
  255. }
  256. /// Destructor
  257. ~integral_range() stlsoft_throw_0()
  258. {
  259. // These constraints are to ensure that this template is not used
  260. // for any other types, especially floating point types!!
  261. STLSOFT_STATIC_ASSERT(0 != is_integral_type<value_type>::value);
  262. STLSOFT_STATIC_ASSERT(0 != is_numeric_type<value_type>::value || 0 != is_character_type<value_type>::value);
  263. }
  264. /// @}
  265. /// \name Notional Range methods
  266. /// @{
  267. private:
  268. STLSOFT_DEFINE_OPERATOR_BOOL_TYPES_T(class_type, operator_bool_generator_type, operator_bool_type);
  269. public:
  270. /// Indicates whether the range is open
  271. ss_bool_t is_open() const
  272. {
  273. return m_position != m_last;
  274. }
  275. /// Returns the current value in the range
  276. const_reference current() const
  277. {
  278. STLSOFT_MESSAGE_ASSERT("Attempting to access the value of a closed range", is_open());
  279. return m_position;
  280. }
  281. /// Advances the current position in the range
  282. class_type& advance()
  283. {
  284. STLSOFT_MESSAGE_ASSERT("Attempting to advance a closed range", is_open());
  285. 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)));
  286. m_position += m_increment;
  287. return *this;
  288. }
  289. /// Indicates whether the range is open
  290. operator operator_bool_type() const
  291. {
  292. return operator_bool_generator_type::translate(is_open());
  293. }
  294. /// Returns the current value in the range
  295. const_reference operator *() const
  296. {
  297. return current();
  298. }
  299. /// Advances the current position in the range
  300. class_type& operator ++()
  301. {
  302. return advance();
  303. }
  304. /// Advances the current position in the range, returning a copy of the
  305. /// range prior to its being advanced
  306. class_type operator ++(int)
  307. {
  308. class_type ret(*this);
  309. operator ++();
  310. return ret;
  311. }
  312. /// @}
  313. /// \name Comparison
  314. /// @{
  315. public:
  316. /// Evaluates whether two ranges are equal
  317. bool operator ==(class_type const& rhs) const
  318. {
  319. STLSOFT_MESSAGE_ASSERT("Comparing unrelated ranges!", m_last == rhs.m_last);
  320. return m_position == rhs.m_position;
  321. }
  322. /// Evaluates whether two ranges are unequal
  323. bool operator !=(class_type const& rhs) const
  324. {
  325. return ! operator ==(rhs);
  326. }
  327. /// @}
  328. // Implementation
  329. private:
  330. static void validate_range(value_type first, value_type last, value_type increment)
  331. {
  332. ss_bool_t bValid = true;
  333. // Check modulus
  334. if(bValid)
  335. {
  336. if( first != last &&
  337. 0 != increment)
  338. {
  339. bValid = (0 == ((last - first) % increment));
  340. }
  341. }
  342. // Check direction
  343. if(bValid)
  344. {
  345. if( ( last < first &&
  346. increment > 0) ||
  347. ( first < last &&
  348. increment < 0))
  349. {
  350. bValid = false;
  351. }
  352. }
  353. // 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)));
  354. // STLSOFT_MESSAGE_ASSERT("The range you have specified will not close with the given increment", 0 == ((last - first) % increment));
  355. if(!bValid)
  356. {
  357. exception_policy_type()(first, last, increment);
  358. }
  359. // Assert here, in case using a null exception policy
  360. STLSOFT_MESSAGE_ASSERT("invalid integral range", bValid);
  361. }
  362. // Members
  363. private:
  364. value_type m_position;
  365. value_type m_last;
  366. value_type m_increment;
  367. };
  368. /* /////////////////////////////////////////////////////////////////////////
  369. * Creator functions
  370. */
  371. template <ss_typename_param_k T>
  372. inline integral_range<T> make_integral_range(T first, T last)
  373. {
  374. return integral_range<T>(first, last);
  375. }
  376. template <ss_typename_param_k T>
  377. inline integral_range<T> make_integral_range(T first, T last, T increment)
  378. {
  379. return integral_range<T>(first, last, increment);
  380. }
  381. ////////////////////////////////////////////////////////////////////////////
  382. // Unit-testing
  383. #ifdef STLSOFT_UNITTEST
  384. # include "./unittest/integral_range_unittest_.h"
  385. #endif /* STLSOFT_UNITTEST */
  386. /* ////////////////////////////////////////////////////////////////////// */
  387. #ifndef RANGELIB_NO_NAMESPACE
  388. # if defined(_STLSOFT_NO_NAMESPACE) || \
  389. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  390. } // namespace rangelib
  391. # else
  392. } // namespace rangelib_project
  393. } // namespace stlsoft
  394. # endif /* _STLSOFT_NO_NAMESPACE */
  395. #endif /* !RANGELIB_NO_NAMESPACE */
  396. /* ////////////////////////////////////////////////////////////////////// */
  397. #endif /* !RANGELIB_INCL_RANGELIB_HPP_INTEGRAL_RANGE */
  398. /* ///////////////////////////// end of file //////////////////////////// */