Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7. #pragma once
  8.  
  9. #include <type_traits>
  10. #include <utility>
  11.  
  12. namespace detail {
  13.  
  14. template <typename T>
  15. struct xrange_is_unsigned : std::is_unsigned<T>
  16. {
  17.         /* For the general case, delegate to std::is_unsigned.
  18.          * xrange requires that the type not be a reference, so there is no
  19.          * need to use std::remove_reference<T> here.
  20.          */
  21. };
  22.  
  23. template <typename T, T v>
  24. struct xrange_is_unsigned<std::integral_constant<T, v>> : std::is_unsigned<T>
  25. {
  26.         /* For the special case that the value is a std::integral_constant,
  27.          * examine the underlying integer type.
  28.          * std::is_unsigned<std::integral_constant<unsigned, N>> is
  29.          * std::false_type, but xrange_is_unsigned should yield
  30.          * std::true_type in that case.
  31.          */
  32. };
  33.  
  34. template <typename B, typename E>
  35. struct xrange_check_constant_endpoints : std::true_type
  36. {
  37.         /* By default, endpoints are not constant and are not checked. */
  38. };
  39.  
  40. template <typename Tb, Tb b, typename Te, Te e>
  41. struct xrange_check_constant_endpoints<std::integral_constant<Tb, b>, std::integral_constant<Te, e>> : std::true_type
  42. {
  43.         /* If both endpoints are constant, a static_assert can validate that
  44.          * the range is not empty.
  45.          */
  46.         static_assert(b < e, "range is always empty due to value of `b` versus value of `e`");
  47. };
  48.  
  49. }
  50.  
  51. /* For the general case, store a `const`-qualified copy of the value,
  52.  * and provide an implicit conversion.
  53.  */
  54. template <typename T, bool begin>
  55. class xrange_endpoint
  56. {
  57. public:
  58.         const T value;
  59.         constexpr xrange_endpoint(T v) :
  60.                 value(v)
  61.         {
  62.         }
  63.         constexpr operator T() const
  64.         {
  65.                 return value;
  66.         }
  67. };
  68.  
  69. /* For the special case that the value is a std::integral_constant,
  70.  * inherit from it so that the Empty Base Optimization can apply.
  71.  */
  72. template <typename T, T V, bool begin>
  73. class xrange_endpoint<std::integral_constant<T, V>, begin> : public std::integral_constant<T, V>
  74. {
  75. public:
  76.         constexpr xrange_endpoint() = default;
  77.         constexpr xrange_endpoint(const std::integral_constant<T, V> &)
  78.         {
  79.         }
  80. };
  81.  
  82. template <typename index_type>
  83. class xrange_iterator
  84. {
  85.         index_type m_idx;
  86. public:
  87.         constexpr xrange_iterator(const index_type i) :
  88.                 m_idx(i)
  89.         {
  90.         }
  91.         index_type operator*() const
  92.         {
  93.                 return m_idx;
  94.         }
  95.         xrange_iterator &operator++()
  96.         {
  97.                 ++ m_idx;
  98.                 return *this;
  99.         }
  100.         constexpr bool operator!=(const xrange_iterator &i) const
  101.         {
  102.                 return m_idx != i.m_idx;
  103.         }
  104. };
  105.  
  106. /* This provides an approximation of the functionality of the Python2
  107.  * xrange object.  Python3 renamed it to `range`.  The older name is
  108.  * kept because it is easier to find with grep.
  109.  */
  110. template <typename index_type, typename B = index_type, typename E = index_type>
  111. class xrange :
  112.         public xrange_endpoint<B, true>,
  113.         public xrange_endpoint<E, false>
  114. {
  115.         using begin_type = xrange_endpoint<B, true>;
  116.         using end_type = xrange_endpoint<E, false>;
  117.         using iterator = xrange_iterator<index_type>;
  118.         /* This static_assert has no message, since the value is always
  119.          * true.  Use of static_assert forces instantiation of the type,
  120.          * which has a static_assert that checks the values and displays a
  121.          * message on failure.
  122.          */
  123.         static_assert(detail::xrange_check_constant_endpoints<B, E>::value);
  124.         static_assert(!std::is_reference<E>::value, "xrange<E> must be a value, not a reference");
  125.         static begin_type init_begin(B b, E e)
  126.         {
  127.                 if constexpr (std::is_convertible<E, B>::value)
  128.                 {
  129. #ifdef DXX_CONSTANT_TRUE
  130.                         (DXX_CONSTANT_TRUE(!(b < e)) && (DXX_ALWAYS_ERROR_FUNCTION(xrange_is_always_empty, "begin never less than end"), 0));
  131. #endif
  132.                         if (!(b < e))
  133.                                 return e;
  134.                 }
  135.                 else
  136.                         (void)e;
  137.                 return b;
  138.         }
  139. public:
  140.         using range_owns_iterated_storage = std::false_type;
  141.         xrange(B b, E e) :
  142.                 begin_type(init_begin(std::move(b), e)), end_type(std::move(e))
  143.         {
  144.         }
  145.         xrange(E e) :
  146.                 begin_type(), end_type(e)
  147.         {
  148.                 static_assert(detail::xrange_is_unsigned<E>::value, "xrange(E) requires unsigned E; use xrange(B, E) if E must be signed");
  149.         }
  150.         iterator begin() const
  151.         {
  152.                 return iterator(static_cast<const begin_type &>(*this));
  153.         }
  154.         iterator end() const
  155.         {
  156.                 return iterator(static_cast<const end_type &>(*this));
  157.         }
  158. };
  159.  
  160. /* Disallow building an `xrange` with a reference to mutable `e` as the
  161.  * end term.  When `e` is mutable, the loop might change `e` during
  162.  * iteration, so using `xrange` could be wrong.  If the loop does not
  163.  * change `e`, store it in a const qualified variable, which will select
  164.  * the next overload down instead.
  165.  */
  166. template <typename Tb, typename Te, typename std::enable_if<!std::is_const<Te>::value, int>::type = 0>
  167. xrange(Tb &&b, Te &e) -> xrange<Tb, Tb, Te &>;  // provokes a static_assert failure in the constructor
  168.  
  169. template <typename Tb, typename Te>
  170. xrange(Tb &&b, Te &&e) -> xrange<
  171.         typename std::common_type<Tb, Te>::type,
  172.         typename std::remove_const<typename std::remove_reference<Tb>::type>::type,
  173.         typename std::remove_const<typename std::remove_reference<Te>::type>::type
  174.         >;
  175.  
  176. template <typename Te>
  177. xrange(const Te &) -> xrange<Te, std::integral_constant<typename std::common_type<Te, unsigned>::type, 0u>>;
  178.  
  179. template <typename Te>
  180. xrange(Te &) -> xrange<
  181.         Te,
  182.         std::integral_constant<typename std::common_type<typename std::remove_const<Te>::type, unsigned>::type, 0u>,
  183.         Te &
  184.         >;
  185.  
  186. template <typename Te, Te e>
  187. xrange(std::integral_constant<Te, e>) -> xrange<Te, std::integral_constant<Te, Te(0)>, std::integral_constant<Te, e>>;
  188.