Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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>>;