/* * 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 #include "ephemeral_range.h" template class xrange_endpoint { public: const T value; constexpr xrange_endpoint(T v) : value(v) { } constexpr operator T() const { return value; } }; 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_extent : public xrange_endpoint, public xrange_endpoint { using begin_type = xrange_endpoint; using end_type = xrange_endpoint; using iterator = xrange_iterator; 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_extent(B b, E e) : begin_type(init_begin(std::move(b), e)), end_type(std::move(e)) { } 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 typename std::enable_if::value, xrange_extent>::type xrange(Tb &&, Te &) = delete; // mutable `e` might change during range template static xrange_extent< typename std::common_type::type, typename std::remove_const::type>::type, typename std::remove_const::type>::type > xrange(Tb &&b, Te &&e) { return {std::forward(b), std::forward(e)}; } /* Disallow building an `xrange` with a default-constructed start term * and a non-unsigned end term. This overload only restricts the * one-argument form of `xrange`. Callers that need a non-unsigned end * term can use the two-argument form to bypass this check. */ template < typename Te, typename Te_rr_rc = typename std::remove_const< typename std::remove_reference::type >::type, typename B = typename std::enable_if< std::is_unsigned::value, std::integral_constant::type, 0u> >::type > static typename std::enable_if::value, xrange_extent>::type xrange(Te &&e) { return {B(), std::forward(e)}; } template typename std::enable_if>::type xrange(std::integral_constant) = delete; // range is invalid or always empty due to value of `e` template , typename E = std::integral_constant> static typename std::enable_if<(Te(0) < e), xrange_extent>::type xrange(std::integral_constant) { return {B(), E()}; }