AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
convert.h
1#ifndef PROBFD_CONVERT_H
2#define PROBFD_CONVERT_H
3
4#include <concepts>
5#include <ranges>
6#include <tuple>
7#include <type_traits>
8
9namespace probfd::views {
10
11namespace detail {
12template <bool Const, typename T>
13using maybe_const_t = std::conditional_t<Const, const T, T>;
14}
15
16template <std::ranges::input_range Vw, typename T>
17 requires std::ranges::view<Vw> &&
18 std::convertible_to<std::ranges::range_reference_t<Vw>, T>
19class convert_view : public std::ranges::view_interface<convert_view<Vw, T>> {
20private:
21 /* [[no_unique_address]] */
22 Vw Range{};
23
24 template <bool Const>
25 struct CategoryBase {};
26
27 template <bool Const>
28 requires std::ranges::forward_range<detail::maybe_const_t<Const, Vw>>
29 struct CategoryBase<Const> {
30 using Base = detail::maybe_const_t<Const, Vw>;
31 using iterator_category = std::conditional_t<
32 std::is_reference_v<T>,
33 std::conditional_t<
34 std::derived_from<
35 typename std::iterator_traits<
36 std::ranges::iterator_t<Base>>::iterator_category,
37 std::contiguous_iterator_tag>,
38 std::random_access_iterator_tag,
39 typename std::iterator_traits<
40 std::ranges::iterator_t<Base>>::iterator_category>,
41 std::input_iterator_tag>;
42 };
43
44 template <bool Const>
45 class Iterator : public CategoryBase<Const> {
46 private:
47 friend convert_view;
48
49 using Parent_t = detail::maybe_const_t<Const, convert_view>;
50 using Base = detail::maybe_const_t<Const, Vw>;
51
52 std::ranges::iterator_t<Base> Current{};
53 Parent_t* Parent{};
54
55 public:
56 using iterator_concept = std::conditional_t<
57 std::ranges::random_access_range<Base>,
58 std::random_access_iterator_tag,
59 std::conditional_t<
60 std::ranges::bidirectional_range<Base>,
61 std::bidirectional_iterator_tag,
62 std::conditional_t<
63 std::ranges::forward_range<Base>,
64 std::forward_iterator_tag,
65 std::input_iterator_tag>>>;
66
67 using value_type = std::remove_cvref_t<T>;
68 using difference_type = std::ranges::range_difference_t<Base>;
69
70 Iterator()
71 requires std::default_initializable<std::ranges::iterator_t<Base>>
72 = default;
73
74 constexpr Iterator(
75 Parent_t& Parent_,
76 std::ranges::iterator_t<Base>
77 Current_) noexcept(std::
78 is_nothrow_move_constructible_v<
79 std::ranges::iterator_t<Base>>)
80 : Current{std::move(Current_)}
81 , Parent{std::addressof(Parent_)}
82 {
83 }
84
85 constexpr explicit(false) Iterator(Iterator<!Const> It) noexcept(
86 std::is_nothrow_constructible_v<
87 std::ranges::iterator_t<Base>,
88 std::ranges::iterator_t<Vw>>) // strengthened
89 requires Const && std::convertible_to<
90 std::ranges::iterator_t<Vw>,
91 std::ranges::iterator_t<Base>>
92 : Current(std::move(It.Current))
93 , Parent(It.Parent)
94 {
95 }
96
97 [[nodiscard]]
98 constexpr const std::ranges::iterator_t<Base>& base() const& noexcept
99 {
100 return Current;
101 }
102 [[nodiscard]]
103 constexpr std::ranges::iterator_t<Base>
104 base() && noexcept(std::is_nothrow_move_constructible_v<
105 std::ranges::iterator_t<Base>>) /* strengthened */
106 {
107 return std::move(Current);
108 }
109
110 [[nodiscard]]
111 constexpr decltype(auto) operator*() const
112 noexcept(noexcept(static_cast<T>(*Current)))
113 {
114 return static_cast<T>(*Current);
115 }
116
117 constexpr Iterator&
118 operator++() noexcept(noexcept(++Current)) /* strengthened */
119 {
120 ++Current;
121 return *this;
122 }
123
124 constexpr decltype(auto) operator++(int) noexcept(
125 noexcept(++Current) && (!std::ranges::forward_range<Base> ||
126 std::is_nothrow_copy_constructible_v<
127 std::ranges::iterator_t<Base>>))
128 {
129 if constexpr (std::ranges::forward_range<Base>) {
130 auto Tmp = *this;
131 ++*this;
132 return Tmp;
133 } else {
134 ++*this;
135 }
136 }
137
138 constexpr Iterator&
139 operator--() noexcept(noexcept(--Current)) /* strengthened */
140 requires std::ranges::bidirectional_range<Base>
141 {
142 --Current;
143 return *this;
144 }
145
146 constexpr Iterator operator--(int) noexcept(
147 noexcept(--Current) &&
148 std::is_nothrow_copy_constructible_v<std::ranges::iterator_t<Base>>)
149 requires std::ranges::bidirectional_range<Base>
150 {
151 auto Tmp = *this;
152 --*this;
153 return Tmp;
154 }
155
156 constexpr Iterator&
157 operator+=(const difference_type Off) noexcept(noexcept(Current += Off))
158 requires std::ranges::random_access_range<Base>
159 {
160 Current += Off;
161 return *this;
162 }
163
164 constexpr Iterator&
165 operator-=(const difference_type Off) noexcept(noexcept(Current -= Off))
166 requires std::ranges::random_access_range<Base>
167 {
168 Current -= Off;
169 return *this;
170 }
171
172 [[nodiscard]]
173 constexpr decltype(auto) operator[](const difference_type Idx) const
174 noexcept(noexcept(static_cast<T>(Current[Idx])))
175 requires std::ranges::random_access_range<Base>
176 {
177 return static_cast<T>(Current[Idx]);
178 }
179
180 [[nodiscard]]
181 friend constexpr bool
182 operator==(const Iterator& Left, const Iterator& Right) noexcept(
183 noexcept(Left.Current == Right.Current))
184 requires std::equality_comparable<std::ranges::iterator_t<Base>>
185 {
186 return Left.Current == Right.Current;
187 }
188
189 [[nodiscard]]
190 friend constexpr bool
191 operator<(const Iterator& Left, const Iterator& Right) noexcept(
192 noexcept(Left.Current < Right.Current)) /* strengthened */
193 requires std::ranges::random_access_range<Base>
194 {
195 return Left.Current < Right.Current;
196 }
197
198 [[nodiscard]]
199 friend constexpr bool
200 operator>(const Iterator& Left, const Iterator& Right) noexcept(
201 noexcept(Left.Current < Right.Current)) /* strengthened */
202 requires std::ranges::random_access_range<Base>
203 {
204 return Right < Left;
205 }
206
207 [[nodiscard]]
208 friend constexpr bool
209 operator<=(const Iterator& Left, const Iterator& Right) noexcept(
210 noexcept(Left.Current < Right.Current))
211 requires std::ranges::random_access_range<Base>
212 {
213 return !(Right < Left);
214 }
215
216 [[nodiscard]]
217 friend constexpr bool
218 operator>=(const Iterator& Left, const Iterator& Right) noexcept(
219 noexcept(Left.Current < Right.Current)) /* strengthened */
220 requires std::ranges::random_access_range<Base>
221 {
222 return !(Left < Right);
223 }
224
225 [[nodiscard]]
226 friend constexpr auto
227 operator<=>(const Iterator& Left, const Iterator& Right) noexcept(
228 noexcept(Left.Current <=> Right.Current))
229 requires std::ranges::random_access_range<Base> &&
230 std::three_way_comparable<std::ranges::iterator_t<Base>>
231 {
232 return Left.Current <=> Right.Current;
233 }
234
235 [[nodiscard]]
236 friend constexpr Iterator
237 operator+(Iterator It, const difference_type Off) noexcept(
238 noexcept(It.Current += Off)) /* strengthened */
239 requires std::ranges::random_access_range<Base>
240 {
241 It.Current += Off;
242 return It;
243 }
244
245 [[nodiscard]]
246 friend constexpr Iterator
247 operator+(const difference_type Off, Iterator It) noexcept(
248 noexcept(It.Current += Off)) /* strengthened */
249 requires std::ranges::random_access_range<Base>
250 {
251 It.Current += Off;
252 return It;
253 }
254
255 [[nodiscard]]
256 friend constexpr Iterator
257 operator-(Iterator It, const difference_type Off) noexcept(
258 noexcept(It.Current -= Off)) /* strengthened */
259 requires std::ranges::random_access_range<Base>
260 {
261 It.Current -= Off;
262 return It;
263 }
264
265 [[nodiscard]]
266 friend constexpr difference_type
267 operator-(const Iterator& Left, const Iterator& Right) noexcept(
268 noexcept(Left.Current - Right.Current))
269 requires std::sized_sentinel_for<
270 std::ranges::iterator_t<Base>,
271 std::ranges::iterator_t<Base>>
272 {
273 return Left.Current - Right.Current;
274 }
275 };
276
277 template <bool Const>
278 class Sentinel {
279 private:
280 friend convert_view;
281
282 using Parent_t = detail::maybe_const_t<Const, convert_view>;
283 using Base = detail::maybe_const_t<Const, Vw>;
284
285 template <bool OtherConst>
286 using const_iter =
287 std::ranges::iterator_t<detail::maybe_const_t<OtherConst, Vw>>;
288
289 std::ranges::sentinel_t<Base> Last{};
290
291 template <bool OtherConst>
292 [[nodiscard]]
293 static constexpr const const_iter<OtherConst>&
294 GetCurrent(const Iterator<OtherConst>& It) noexcept
295 {
296 return It.Current;
297 }
298
299 public:
300 Sentinel() = default;
301
302 constexpr explicit Sentinel(
303 std::ranges::sentinel_t<Base>
304 Last_) noexcept(std::
305 is_nothrow_move_constructible_v<
306 std::ranges::sentinel_t<Base>>)
307 : Last(std::move(Last_))
308 {
309 }
310
311 constexpr explicit(false) Sentinel(Sentinel<!Const> Se) noexcept(
312 std::is_nothrow_constructible_v<
313 std::ranges::sentinel_t<Base>,
314 std::ranges::sentinel_t<Vw>>)
315 requires Const && std::convertible_to<
316 std::ranges::sentinel_t<Vw>,
317 std::ranges::sentinel_t<Base>>
318 : Last(std::move(Se.Last))
319 {
320 }
321
322 [[nodiscard]]
323 constexpr std::ranges::sentinel_t<Base> base() const noexcept(
324 std::is_nothrow_copy_constructible_v<std::ranges::sentinel_t<Base>>)
325 {
326 return Last;
327 }
328
329 template <bool OtherConst>
330 requires std::sentinel_for<
331 std::ranges::sentinel_t<Base>,
332 const_iter<OtherConst>>
333 [[nodiscard]]
334 friend constexpr bool operator==(
335 const Iterator<OtherConst>& Left,
336 const Sentinel&
337 Right) noexcept(noexcept(GetCurrent(Left) == Right.Last))
338 {
339 return GetCurrent(Left) == Right.Last;
340 }
341
342 template <bool OtherConst>
343 requires std::sized_sentinel_for<
344 std::ranges::sentinel_t<Base>,
345 const_iter<OtherConst>>
346 [[nodiscard]]
347 friend constexpr std::ranges::range_difference_t<
348 detail::maybe_const_t<OtherConst, Vw>>
349 operator-(
350 const Iterator<OtherConst>& Left,
351 const Sentinel&
352 Right) noexcept(noexcept(GetCurrent(Left) - Right.Last))
353 {
354 return GetCurrent(Left) - Right.Last;
355 }
356
357 template <bool OtherConst>
358 requires std::sized_sentinel_for<
359 std::ranges::sentinel_t<Base>,
360 const_iter<OtherConst>>
361 [[nodiscard]]
362 friend constexpr std::ranges::range_difference_t<
363 detail::maybe_const_t<OtherConst, Vw>>
364 operator-(
365 const Sentinel& Left,
366 const Iterator<OtherConst>&
367 Right) noexcept(noexcept(Left.Last - GetCurrent(Right)))
368 {
369 return Left.Last - GetCurrent(Right);
370 }
371 };
372
373public:
374 convert_view()
375 requires std::default_initializable<Vw>
376 = default;
377
378 constexpr explicit convert_view(Vw Range_) noexcept(
379 std::is_nothrow_move_constructible_v<Vw>)
380 : Range(std::move(Range_))
381 {
382 }
383
384 [[nodiscard]]
385 constexpr Vw
386 base() const& noexcept(std::is_nothrow_copy_constructible_v<Vw>)
387 requires std::copy_constructible<Vw>
388 {
389 return Range;
390 }
391
392 [[nodiscard]]
393 constexpr Vw base() && noexcept(std::is_nothrow_move_constructible_v<Vw>)
394 {
395 return std::move(Range);
396 }
397
398 [[nodiscard]]
399 constexpr Iterator<false> begin() noexcept(
400 noexcept(std::ranges::begin(Range)) &&
401 std::is_nothrow_move_constructible_v<std::ranges::iterator_t<Vw>>)
402 {
403 return Iterator<false>(*this, std::ranges::begin(Range));
404 }
405
406 [[nodiscard]]
407 constexpr Iterator<true> begin() const noexcept(
408 noexcept(std::ranges::begin(Range)) &&
409 std::is_nothrow_move_constructible_v<std::ranges::iterator_t<Vw>>)
410 requires std::ranges::range<const Vw>
411 {
412 return Iterator<true>(*this, std::ranges::begin(Range));
413 }
414
415 [[nodiscard]]
416 constexpr auto end() noexcept(
417 noexcept(std::ranges::end(Range)) &&
418 std::is_nothrow_move_constructible_v<decltype(std::ranges::end(Range))>)
419 {
420 if constexpr (std::ranges::common_range<Vw>) {
421 return Iterator<false>(*this, std::ranges::end(Range));
422 } else {
423 return Sentinel<false>(std::ranges::end(Range));
424 }
425 }
426
427 [[nodiscard]]
428 constexpr auto end() const noexcept(
429 noexcept(std::ranges::end(Range)) &&
430 std::is_nothrow_move_constructible_v<decltype(std::ranges::end(Range))>)
431 requires std::ranges::range<const Vw>
432 {
433 if constexpr (std::ranges::common_range<Vw>) {
434 return Iterator<true>(*this, std::ranges::end(Range));
435 } else {
436 return Sentinel<true>(std::ranges::end(Range));
437 }
438 }
439
440 [[nodiscard]]
441 constexpr auto size() noexcept(noexcept(std::ranges::size(Range)))
442 requires std::ranges::sized_range<Vw>
443 {
444 return std::ranges::size(Range);
445 }
446
447 [[nodiscard]]
448 constexpr auto size() const noexcept(noexcept(std::ranges::size(Range)))
449 requires std::ranges::sized_range<const Vw>
450 {
451 return std::ranges::size(Range);
452 }
453};
454
455namespace detail {
456
457template <typename T>
458using all_t = decltype(std::views::all(std::declval<T>()));
459
460template <typename T>
461struct convert_fn : public std::ranges::range_adaptor_closure<convert_fn<T>> {
462 template <std::ranges::viewable_range Rng>
463 [[nodiscard]]
464 constexpr auto operator()(Rng&& Range) const noexcept(
465 noexcept(convert_view<all_t<Rng>, T>(std::forward<Rng>(Range))))
466 requires requires {
467 convert_view<all_t<Rng>, T>(static_cast<Rng&&>(Range));
468 }
469 {
470 return convert_view<all_t<Rng>, T>(std::forward<Rng>(Range));
471 }
472};
473
474} // namespace detail
475
476template <typename T>
477inline constexpr detail::convert_fn<T> convert;
478
479} // namespace probfd::views
480
481#endif
STL namespace.