/* * This file is part of the DXX-Rebirth project . * It is copyright by its individual contributors, as recorded in the * project's Git history. See COPYING.txt at the top level for license * terms and a link to the Git history. */ #pragma once #include #include namespace detail { template struct xrange_is_unsigned : std::is_unsigned { /* For the general case, delegate to std::is_unsigned. * xrange requires that the type not be a reference, so there is no * need to use std::remove_reference here. */ }; template struct xrange_is_unsigned> : std::is_unsigned { /* For the special case that the value is a std::integral_constant, * examine the underlying integer type. * std::is_unsigned> is * std::false_type, but xrange_is_unsigned should yield * std::true_type in that case. */ }; template struct xrange_check_constant_endpoints : std::true_type { /* By default, endpoints are not constant and are not checked. */ }; template struct xrange_check_constant_endpoints, std::integral_constant> : std::true_type { /* If both endpoints are constant, a static_assert can validate that * the range is not empty. */ static_assert(b < e, "range is always empty due to value of `b` versus value of `e`"); }; } /* For the general case, store a `const`-qualified copy of the value, * and provide an implicit conversion. */ template class xrange_endpoint { public: const T value; constexpr xrange_endpoint(T v) : value(v) { } constexpr operator T() const { return value; } }; /* For the special case that the value is a std::integral_constant, * inherit from it so that the Empty Base Optimization can apply. */ template class xrange_endpoint, begin> : public std::integral_constant { public: constexpr xrange_endpoint() = default; constexpr xrange_endpoint(const std::integral_constant &) { } }; template class xrange_iterator { index_type m_idx; public: constexpr xrange_iterator(const index_type i) : m_idx(i) { } index_type operator*() const { return m_idx; } xrange_iterator &operator++() { ++ m_idx; return *this; } constexpr bool operator!=(const xrange_iterator &i) const { return m_idx != i.m_idx; } }; /* This provides an approximation of the functionality of the Python2 * xrange object. Python3 renamed it to `range`. The older name is * kept because it is easier to find with grep. */ template class xrange : public xrange_endpoint, public xrange_endpoint { using begin_type = xrange_endpoint; using end_type = xrange_endpoint; using iterator = xrange_iterator; /* This static_assert has no message, since the value is always * true. Use of static_assert forces instantiation of the type, * which has a static_assert that checks the values and displays a * message on failure. */ static_assert(detail::xrange_check_constant_endpoints::value); static_assert(!std::is_reference::value, "xrange must be a value, not a reference"); static begin_type init_begin(B b, E e) { if constexpr (std::is_convertible::value) { #ifdef DXX_CONSTANT_TRUE (DXX_CONSTANT_TRUE(!(b < e)) && (DXX_ALWAYS_ERROR_FUNCTION(xrange_is_always_empty, "begin never less than end"), 0)); #endif if (!(b < e)) return e; } else (void)e; return b; } public: using range_owns_iterated_storage = std::false_type; xrange(B b, E e) : begin_type(init_begin(std::move(b), e)), end_type(std::move(e)) { } xrange(E e) : begin_type(), end_type(e) { static_assert(detail::xrange_is_unsigned::value, "xrange(E) requires unsigned E; use xrange(B, E) if E must be signed"); } iterator begin() const { return iterator(static_cast(*this)); } iterator end() const { return iterator(static_cast(*this)); } }; /* Disallow building an `xrange` with a reference to mutable `e` as the * end term. When `e` is mutable, the loop might change `e` during * iteration, so using `xrange` could be wrong. If the loop does not * change `e`, store it in a const qualified variable, which will select * the next overload down instead. */ template ::value, int>::type = 0> xrange(Tb &&b, Te &e) -> xrange; // provokes a static_assert failure in the constructor template xrange(Tb &&b, Te &&e) -> xrange< typename std::common_type::type, typename std::remove_const::type>::type, typename std::remove_const::type>::type >; template xrange(const Te &) -> xrange::type, 0u>>; template xrange(Te &) -> xrange< Te, std::integral_constant::type, unsigned>::type, 0u>, Te & >; template xrange(std::integral_constant) -> xrange, std::integral_constant>;