NcEngine
EnumerateDetail.h
1#pragma once
2
3// Implementation of c++23 std::enumerate based on: https://github.com/cor3ntin/rangesnext
4
5#include <ranges>
6
7namespace nc::algo::detail {
8template<class... V>
9consteval auto iter_cat()
10{
11 if constexpr ((std::ranges::random_access_range<V> && ...))
12 {
13 return std::random_access_iterator_tag{};
14 }
15 else if constexpr ((std::ranges::bidirectional_range<V> && ...))
16 {
17 return std::bidirectional_iterator_tag{};
18 }
19 else if constexpr ((std::ranges::forward_range<V> && ...))
20 {
21 return std::forward_iterator_tag{};
22 }
23 else if constexpr ((std::ranges::input_range<V> && ...))
24 {
25 return std::input_iterator_tag{};
26 }
27 else
28 {
29 return std::output_iterator_tag{};
30 }
31}
32
33template<class R>
34concept simple_view =
35 std::ranges::view<R> &&
36 std::ranges::range<const R> &&
37 std::same_as<std::ranges::iterator_t<R>, std::ranges::iterator_t<const R>> &&
38 std::same_as<std::ranges::sentinel_t<R>, std::ranges::sentinel_t<const R>>;
39
40template<std::ranges::input_range V>
41 requires std::ranges::view<V>
42class enumerate_view : public std::ranges::view_interface<enumerate_view<V>>
43{
44
45 V m_base = {};
46
47 template<bool>
48 struct sentinel;
49
50 template<bool Const>
51 struct iterator
52 {
53 private:
54 using Base = std::conditional_t<Const, const V, V>;
55 using count_type = decltype([] {
56 if constexpr (std::ranges::sized_range<Base>)
57 return std::ranges::range_size_t<Base>();
58 else {
59 return std::make_unsigned_t<std::ranges::range_difference_t<Base>>();
60 }
61 }());
62
63 template<typename T>
64 struct result
65 {
66 const count_type index;
67 T value;
68
69 constexpr bool operator==(const result& other) const = default;
70 };
71
72 std::ranges::iterator_t<Base> m_it = std::ranges::iterator_t<Base>();
73 count_type m_pos = 0;
74
75 template <bool>
76 friend struct iterator;
77 template <bool>
78 friend struct sentinel;
79
80 public:
81 using iterator_category = decltype(iter_cat<Base>());
82 using reference = result<std::ranges::range_reference_t<Base>>;
83 using value_type = result<std::ranges::range_reference_t<Base>>;
84 using difference_type = std::ranges::range_difference_t<Base>;
85
86 iterator() = default;
87
88 constexpr explicit iterator(std::ranges::iterator_t<Base> current, std::ranges::range_difference_t<Base> pos)
89 : m_it(std::move(current)),
90 m_pos(static_cast<count_type>(pos))
91 {
92 }
93
94 constexpr iterator(iterator<!Const> i)
95 requires Const &&
96 std::convertible_to<std::ranges::iterator_t<V>, std::ranges::iterator_t<Base>>
97 : m_it(std::move(i.m_it)),
98 m_pos(i.m_pos)
99 {
100 }
101
102 constexpr auto base() const & -> const std::ranges::iterator_t<V>&
103 requires std::copyable<std::ranges::iterator_t<Base>>
104 {
105 return m_it;
106 }
107
108 constexpr auto base() && -> std::ranges::iterator_t<V>
109 {
110 return std::move(m_it);
111 }
112
113 constexpr auto operator*() const
114 {
115 return reference{static_cast<count_type>(m_pos), *m_it};
116 }
117
118 constexpr auto operator++() -> iterator&
119 {
120 ++m_pos;
121 ++m_it;
122 return *this;
123 }
124
125 constexpr auto operator++(int)
126 {
127 ++m_pos;
128 if constexpr (std::ranges::forward_range<V>)
129 {
130 auto tmp = *this;
131 ++*this;
132 return tmp;
133 }
134 else
135 {
136 ++m_it;
137 }
138 }
139
140 constexpr auto operator--() -> iterator&
141 requires std::ranges::bidirectional_range<V>
142 {
143 --m_pos;
144 --m_it;
145 return *this;
146 }
147
148 constexpr auto operator--(int)
149 requires std::ranges::bidirectional_range<V>
150 {
151 auto tmp = *this;
152 --*this;
153 return tmp;
154 }
155
156 constexpr auto operator+=(difference_type n) -> iterator&
157 requires std::ranges::random_access_range<V>
158 {
159 m_it += n;
160 m_pos += n;
161 return *this;
162 }
163
164 constexpr auto operator-=(difference_type n) -> iterator&
165 requires std::ranges::random_access_range<V>
166 {
167 m_it -= n;
168 m_pos -= n;
169 return *this;
170 }
171
172 friend constexpr auto operator+(const iterator& i, difference_type n) -> iterator
173 requires std::ranges::random_access_range<V>
174 {
175 return iterator{i.m_it + n, static_cast<difference_type>(i.m_pos + n)};
176 }
177
178 friend constexpr auto operator+(difference_type n, const iterator &i) -> iterator
179 requires std::ranges::random_access_range<V>
180 {
181 return iterator{i.m_it + n, static_cast<difference_type>(i.m_pos + n)};
182 }
183
184 friend constexpr auto operator-(iterator i, difference_type n)
185 requires std::ranges::random_access_range<V>
186 {
187 return iterator{i.m_it - n, static_cast<difference_type>(i.m_pos - n)};
188 }
189
190 friend constexpr auto operator-(difference_type n, iterator i)
191 requires std::ranges::random_access_range<V>
192 {
193 return iterator{i.m_it - n, static_cast<difference_type>(i.m_pos - n)};
194 }
195
196 constexpr decltype(auto) operator[](difference_type n) const
197 requires std::ranges::random_access_range<Base>
198 {
199 return reference{static_cast<count_type>(m_pos + n), *(m_it + n)};
200 }
201
202 friend constexpr auto operator==(const iterator& x, const iterator& y) -> bool
203 requires std::equality_comparable<std::ranges::iterator_t<Base>>
204 {
205 return x.m_it == y.m_it;
206 }
207
208 template <bool ConstS>
209 friend constexpr auto operator==(const iterator<Const>& i, const sentinel<ConstS>& s) -> bool
210 {
211 return i.m_it == s.base();
212 }
213
214 friend constexpr auto operator<(const iterator& x, const iterator& y) -> bool
215 requires std::ranges::random_access_range<Base>
216 {
217 return x.m_it < y.m_it;
218 }
219
220 friend constexpr auto operator>(const iterator& x, const iterator& y) -> bool
221 requires std::ranges::random_access_range<Base>
222 {
223 return x.m_it > y.m_it;
224 }
225
226 friend constexpr auto operator<=(const iterator& x, const iterator& y) -> bool
227 requires std::ranges::random_access_range<Base>
228 {
229 return x.m_it <= y.m_it;
230 }
231
232 friend constexpr auto operator>=(const iterator& x, const iterator& y) -> bool
233 requires std::ranges::random_access_range<Base>
234 {
235 return x.m_it >= y.m_it;
236 }
237
238 friend constexpr auto operator<=>(const iterator& x, const iterator& y)
239 requires std::ranges::random_access_range<Base> &&
240 std::three_way_comparable<std::ranges::iterator_t<Base>>
241 {
242 return x.m_it <=> y.m_it;
243 }
244
245 friend constexpr auto operator-(const iterator& x, const iterator& y) -> difference_type
246 requires std::ranges::random_access_range<Base>
247 {
248 return x.m_it - y.m_it;
249 }
250 };
251
252 template<bool Const>
253 struct sentinel {
254 private:
255 friend iterator<false>;
256 friend iterator<true>;
257
258 using Base = std::conditional_t<Const, const V, V>;
259
260 std::ranges::sentinel_t<V> end_;
261
262 public:
263 sentinel() = default;
264
265 constexpr explicit sentinel(std::ranges::sentinel_t<V> end)
266 : end_(std::move(end))
267 {
268 }
269
270 constexpr auto base() const
271 {
272 return end_;
273 }
274
275 friend constexpr auto operator-(const iterator<Const> &x, const sentinel &y) -> std::ranges::range_difference_t<Base>
276 requires std::sized_sentinel_for<std::ranges::sentinel_t<Base>, std::ranges::iterator_t<Base>> {
277 return x.m_it - y.end_;
278 }
279
280 friend constexpr auto operator-(const sentinel &x, const iterator<Const> &y) -> std::ranges::range_difference_t<Base>
281 requires std::sized_sentinel_for<std::ranges::sentinel_t<Base>, std::ranges::iterator_t<Base>>
282 {
283 return x.end_ - y.m_it;
284 }
285 };
286
287 public:
288 constexpr enumerate_view() = default;
289 constexpr enumerate_view(V base)
290 : m_base(std::move(base))
291 {
292 }
293
294 constexpr auto begin() requires(!simple_view<V>)
295 {
296 return iterator<false>(std::ranges::begin(m_base), 0);
297 }
298
299 constexpr auto begin() const requires simple_view<V>
300 {
301 return iterator<true>(std::ranges::begin(m_base), 0);
302 }
303
304 constexpr auto end()
305 {
306 return sentinel<false>{std::ranges::end(m_base)};
307 }
308
309 constexpr auto end()
310 requires std::ranges::common_range<V>
311 {
312 return iterator<false>{std::ranges::end(m_base), static_cast<std::ranges::range_difference_t<V>>(size())};
313 }
314
315 constexpr auto end() const
316 requires std::ranges::range<const V>
317 {
318 return sentinel<true>{std::ranges::end(m_base)};
319 }
320
321 constexpr auto end() const
322 requires std::ranges::common_range<const V>
323 {
324 return iterator<true>{std::ranges::end(m_base), static_cast<std::ranges::range_difference_t<V>>(size())};
325 }
326
327 constexpr auto size() requires std::ranges::sized_range<V>
328 {
329 return std::ranges::size(m_base);
330 }
331
332 constexpr auto size() const
333 requires std::ranges::sized_range<const V>
334 {
335 return std::ranges::size(m_base);
336 }
337
338 constexpr auto base() const & -> V
339 requires std::copyable<V>
340 {
341 return m_base;
342 }
343
344 constexpr V base() &&
345 {
346 return std::move(m_base);
347 }
348};
349
350template<typename R>
351requires std::ranges::input_range<R> enumerate_view(R &&r)
353
355 template<typename R>
356 constexpr auto operator()(R &&r) const {
357 return enumerate_view{std::forward<R>(r)};
358 }
359
360 template<std::ranges::input_range R>
361 constexpr friend auto operator|(R &&rng, const enumerate_view_fn &) {
362 return enumerate_view{std::forward<R>(rng)};
363 }
364};
365} // namespace nc::algo::detail
Definition: EnumerateDetail.h:43
Definition: EnumerateDetail.h:354