header_utils
Loading...
Searching...
No Matches
ranges.h
1
4
5#pragma once
6
7#include <ranges>
8#include <stdexcept>
9#include <span>
10
11namespace ghassanpl
12{
15
18
19 using std::ranges::random_access_range;
20 using std::ranges::contiguous_range;
21 using std::random_access_iterator;
22
25 template <typename RANGE>
26 using range_value = std::ranges::range_value_t<std::decay_t<RANGE>>;
27
29 template <typename RANGE>
30 using range_iterator = std::ranges::iterator_t<std::decay_t<RANGE>>;
31
33 template <typename RANGE, typename FUNC>
34 concept range_predicate = requires (FUNC func, RANGE range) { { func(*std::ranges::begin(range)) } -> std::convertible_to<bool>; };
35
37 constexpr bool valid_index(random_access_range auto& range, std::integral auto index)
38 {
39 return index >= 0 && index < std::ranges::size(range);
40 }
41
44 constexpr auto modulo_index(size_t range_size, std::integral auto index)
45 {
46 const auto cpp_modulo_sucks = (index % static_cast<decltype(index)>(range_size));
47 return (index < 0) ? (range_size + cpp_modulo_sucks) : cpp_modulo_sucks;
48 }
49
52 constexpr auto modulo_index(random_access_range auto& range, std::integral auto index)
53 {
54 const auto range_size = std::ranges::size(range);
55 return modulo_index(range_size, index);
56 }
57
59 constexpr decltype(auto) at(random_access_range auto& range, std::integral auto index)
60 {
61 if (!valid_index(range, index))
62 throw std::invalid_argument("index");
63 return *(std::ranges::begin(range) + index);
64 }
65
67 constexpr auto at_ptr(random_access_range auto& range, std::integral auto index)
68 -> decltype(std::to_address(std::ranges::begin(range) + index))
69 {
70 if (!valid_index(range, index))
71 return nullptr;
72 return std::to_address(std::ranges::begin(range) + index);
73 }
74
77 constexpr decltype(auto) modulo_at(random_access_range auto& range, std::integral auto index)
78 {
79 return at(range, modulo_index(range, index));
80 }
81
83 template <random_access_range RANGE, typename T>
84 constexpr auto index_of(RANGE const& range, T&& value) -> std::iter_difference_t<range_iterator<RANGE>>
85 requires requires (T value, RANGE range) { { *std::ranges::begin(range) == value } -> std::convertible_to<bool>; }
86 {
87 const auto it = std::ranges::find(range, std::forward<T>(value));
88 if (it == std::ranges::end(range))
89 return -1;
90 return std::ranges::distance(std::ranges::begin(range), it);
91 }
92
94 template <random_access_range RANGE, typename T = range_value<RANGE>>
95 constexpr auto at_or_default(RANGE&& range, std::integral auto index, T&& default_value)
96 {
97 if (!valid_index(range, index)) [[unlikely]] return std::forward<T>(default_value);
98 return at(range, index);
99 }
100
102 template <random_access_range RANGE, typename FUNC>
104 constexpr auto find_index(RANGE const& range, FUNC&& func) -> std::iter_difference_t<range_iterator<RANGE>>
105 {
106 const auto it = std::ranges::find_if(range, std::forward<FUNC>(func));
107 if (it == std::ranges::end(range))
108 return -1;
109 return std::ranges::distance(std::ranges::begin(range), it);
110 }
111
113 template <std::ranges::range RANGE, typename FUNC>
115 constexpr auto find_ptr(RANGE& range, FUNC&& func)
116 {
117 const auto it = std::ranges::find_if(range, std::forward<FUNC>(func));
118 return it == std::ranges::end(range) ? nullptr : std::to_address(it);
119 }
120
121 template <random_access_range RANGE, typename FUNC, typename DEF_TYPE = range_value<RANGE>>
123 auto find_if_or_default(RANGE& range, FUNC&& func, DEF_TYPE&& default_value = DEF_TYPE{})
124 {
125 auto it = std::ranges::find_if(range, std::forward<FUNC>(func));
126 if (it == std::ranges::end(range))
127 return range_value<RANGE>{std::forward<DEF_TYPE>(default_value)};
128 return *it;
129 }
130
132 constexpr auto to_index(random_access_iterator auto iterator, random_access_range auto const& range)
133 {
134 return std::ranges::distance(std::ranges::begin(range), iterator);
135 }
136
138 template <contiguous_range RANGE>
139 constexpr bool valid_address(RANGE&& range, range_value<RANGE>* pointer)
140 {
141 if (std::ranges::empty(range)) return false;
142 return pointer >= std::to_address(std::ranges::begin(range)) && pointer < std::to_address(std::ranges::end(range));
143 }
144
146
147 template <typename T, size_t N = std::dynamic_extent>
148 constexpr std::pair<std::span<T>, std::span<T>> split_at(std::span<T, N> span, size_t index)
149 {
150 if (index >= span.size())
151 return { span, std::span<T>{} };
152 return { span.subspan(0, index), span.subspan(index) };
153 }
154
155 template <typename T, size_t N = std::dynamic_extent>
156 constexpr std::array<std::span<T>, 3> split_at(std::span<T, N> span, size_t index, size_t size)
157 {
158 if (index >= span.size() || index + size >= span.size())
159 return { span, std::span<T>{}, std::span<T>{} };
160 return { span.subspan(0, index), span.subspan(index, size), span.subspan(index + size) };
161 }
162
163 template <typename T, size_t N = std::dynamic_extent>
164 constexpr std::pair<T*, T*> as_range(std::span<T, N> span)
165 {
166 return { span.data(), span.data() + span.size() };
167 }
168
171 template <typename TO, typename FROM, size_t N = std::dynamic_extent>
172 auto span_cast(std::span<FROM, N> bytes) noexcept
173 {
174 return std::span<TO>{ reinterpret_cast<TO*>(bytes.data()), (bytes.size() * sizeof(FROM)) / sizeof(TO) };
175 }
176
177 template <typename T1, size_t N1, typename T2, size_t N2>
178 constexpr bool are_adjacent(std::span<T1, N1> s1, std::span<T2, N2> s2)
179 {
180 return reinterpret_cast<char const*>(s1.data() + s1.size()) == reinterpret_cast<char const*>(s2.data())
181 || reinterpret_cast<char const*>(s2.data() + s2.size()) == reinterpret_cast<char const*>(s1.data());
182 }
183
184 template <typename T1, size_t N1, typename T2, size_t N2>
185 constexpr bool are_overlapping(std::span<T1, N1> s1, std::span<T2, N2> s2)
186 {
187 return valid_address(s1, s2.data()) || valid_address(s2, s1.data());
188 }
189
190 template <typename T, size_t N1, typename U, size_t N2>
191 requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>
192 constexpr bool ends_with(std::span<T, N1> s1, std::span<U, N2> s2)
193 {
194 if (s1.size() < s2.size()) return false;
195 return std::ranges::equal(s1.subspan(s1.size() - s2.size()), s2);
196 }
197
198 template <typename T, size_t N1, typename U, size_t N2>
199 requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>
200 constexpr bool starts_with(std::span<T, N1> s1, std::span<U, N2> s2)
201 {
202 if (s1.size() < s2.size()) return false;
203 return std::ranges::equal(s1.subspan(0, s2.size()), s2);
204 }
205
207
208 template <typename T, std::size_t... Ns>
209 constexpr auto join(std::array<T, Ns>... arrays)
210 {
211 std::array<T, (Ns + ...)> result;
212 std::size_t index = 0;
213 ((std::copy_n(std::make_move_iterator(arrays.begin()), Ns, result.begin() + index), index += Ns), ...);
214 return result;
215 }
216
217 template<typename T, size_t LL, typename... ARGS>
218 requires (std::same_as<std::remove_cvref_t<ARGS>, T> && ...)
219 constexpr auto join(std::array<T, LL> rhs, ARGS&&... args)
220 {
221 std::array<T, LL + sizeof...(ARGS)> ar;
222 auto current = std::move(rhs.begin(), rhs.end(), ar.begin());
223 ((*current++ = args), ...);
224 return ar;
225 }
226
227#ifndef __cpp_lib_ranges_contains
228
229 struct __contains_fn
230 {
231 template <std::input_iterator I, std::sentinel_for<I> S, class T, class Proj = std::identity>
232 requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<I, Proj>, const T*>
233 constexpr bool operator()(I first, S last, const T& value, Proj proj = {}) const
234 {
235 return std::ranges::find(std::move(first), last, value, proj) != last;
236 }
237
238 template <std::ranges::input_range R, class T, class Proj = std::identity>
239 requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<std::ranges::iterator_t<R>, Proj>, const T*>
240 constexpr bool operator()(R&& r, const T& value, Proj proj = {}) const
241 {
242 return (*this)(std::ranges::begin(r), std::ranges::end(r), std::move(value), proj);
243 }
244 };
245
247 constexpr inline __contains_fn contains{};
248
249
250 struct __contains_subrange_fn
251 {
252 template <std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2, std::sentinel_for<I2> S2, class Pred = std::ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity>
253 requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
254 constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
255 {
256 return (first2 == last2) || !std::ranges::search(first1, last1, first2, last2, pred, proj1, proj2).empty();
257 }
258
259 template <std::ranges::forward_range R1, std::ranges::forward_range R2, class Pred = std::ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity>
260 requires std::indirectly_comparable<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>, Pred, Proj1, Proj2>
261 constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
262 {
263 return (*this)(std::ranges::begin(r1), std::ranges::end(r1), std::ranges::begin(r2), std::ranges::end(r2), std::move(pred), std::move(proj1), std::move(proj2));
264 }
265 };
266
267
269 constexpr inline __contains_subrange_fn contains_subrange{};
270
271#endif
272
273#ifndef __cpp_lib_ranges_to_container
274
275 namespace detail
276 {
277 struct from_range_t { explicit from_range_t() = default; };
278 constexpr inline from_range_t from_range;
279
280 template <class RANGE, class CONTAINER>
281 concept _Ref_converts = std::convertible_to<std::ranges::range_reference_t<RANGE>, std::ranges::range_value_t<CONTAINER>>;
282
283 template <class RANGE, class CONTAINER, class... TYPES>
284 concept _Converts_direct_constructible = _Ref_converts<RANGE, CONTAINER>
285 && std::constructible_from<CONTAINER, RANGE, TYPES...>;
286
287 template <class RANGE, class CONTAINER, class... TYPES>
288 concept _Converts_tag_constructible = _Ref_converts<RANGE, CONTAINER>
289 && std::constructible_from<CONTAINER, const from_range_t&, RANGE, TYPES...>;
290
291 template <class RANGE, class CONTAINER, class... TYPES>
292 concept _Converts_and_common_constructible = _Ref_converts<RANGE, CONTAINER> && std::ranges::common_range<RANGE> //
293 && std::input_iterator<std::ranges::iterator_t<RANGE>> //
294 && std::constructible_from<CONTAINER, std::ranges::iterator_t<RANGE>, std::ranges::iterator_t<RANGE>, TYPES...>;
295
296 template <class CONTAINER, class REFERENCE>
297 concept can_push_back = requires (CONTAINER& container, REFERENCE ref) {
298 container.push_back(ref);
299 };
300
301 template <class CONTAINER, class REFERENCE>
302 concept can_insert_at_end = requires (CONTAINER & container, REFERENCE ref) {
303 container.insert(container.end(), ref);
304 };
305
306 // clang-format off
307 template <class RANGE, class CONTAINER, class... TYPES>
308 concept _Converts_constructible_insertable = _Ref_converts<RANGE, CONTAINER>
309 && std::constructible_from<CONTAINER, TYPES...>
312 // clang-format on
313
314 template <class REFERENCE, class CONTAINER>
315 [[nodiscard]] constexpr auto container_inserter(CONTAINER& _Cont)
316 {
318 return std::back_insert_iterator{ _Cont };
319 }
320 else {
321 return std::insert_iterator{ _Cont, _Cont.end() };
322 }
323 }
324
325 template <class RANGE, class CONTAINER>
326 concept sized_and_reservable =
327 std::ranges::sized_range<RANGE> &&
328 std::ranges::sized_range<CONTAINER> &&
329 requires(CONTAINER & container, const std::ranges::range_size_t<CONTAINER> count) {
330 container.reserve(count);
331 { container.capacity() } -> std::same_as<std::ranges::range_size_t<CONTAINER>>;
332 { container.max_size() } -> std::same_as<std::ranges::range_size_t<CONTAINER>>;
333 };
334
335 template <std::ranges::input_range RANGE>
336 struct phony_input_iterator
337 {
338 using iterator_category = std::input_iterator_tag;
339 using value_type = std::ranges::range_value_t<RANGE>;
340 using difference_type = ptrdiff_t;
341 using pointer = std::add_pointer_t<std::ranges::range_reference_t<RANGE>>;
342 using reference = std::ranges::range_reference_t<RANGE>;
343
344 reference operator*() const;
345 pointer operator->() const;
346
347 phony_input_iterator& operator++();
348 phony_input_iterator operator++(int);
349
350 bool operator==(const phony_input_iterator&) const;
351 };
352
353 template <template <class...> class CONTAINER, class RANGE, class... ARGS>
354 auto to_helper()
355 {
356 if constexpr (requires (RANGE range, ARGS... args) { CONTAINER(range, args...); })
357 return static_cast<decltype(CONTAINER(std::declval<RANGE>(), std::declval<ARGS>()...))*>(nullptr);
358 else if constexpr (requires (RANGE range, ARGS... args) { CONTAINER(from_range, range, args...); })
359 return static_cast<decltype(CONTAINER(from_range, std::declval<RANGE>(), std::declval<ARGS>()...))*>(nullptr);
360 else if constexpr (requires (phony_input_iterator<RANGE> it, ARGS... args) { CONTAINER(it, it, args...);})
361 return static_cast<decltype(CONTAINER(std::declval<phony_input_iterator<RANGE>>(), std::declval<phony_input_iterator<RANGE>>(), std::declval<ARGS>()...))*>(nullptr);
362 }
363
364 }
365
367 template <class CONTAINER, std::ranges::input_range RANGE, class... TYPES>
368 requires (!std::ranges::view<CONTAINER>)
369 [[nodiscard]] constexpr CONTAINER to(RANGE&& range, TYPES&&... args)
370 {
371 using namespace detail;
372 if constexpr (_Converts_direct_constructible<RANGE, CONTAINER, TYPES...>)
373 return CONTAINER(std::forward<RANGE>(range), std::forward<TYPES>(args)...);
374 else if constexpr (_Converts_tag_constructible<RANGE, CONTAINER, TYPES...>)
375 return CONTAINER(from_range, std::forward<RANGE>(range), std::forward<TYPES>(args)...);
376 else if constexpr (_Converts_and_common_constructible<RANGE, CONTAINER, TYPES...>)
377 return CONTAINER(std::ranges::begin(range), std::ranges::end(range), std::forward<TYPES>(args)...);
378 else if constexpr (_Converts_constructible_insertable<RANGE, CONTAINER, TYPES...>)
379 {
380 CONTAINER container(std::forward<TYPES>(args)...);
382 container.reserve(std::ranges::size(range));
383 }
384 std::ranges::copy(range, detail::container_inserter<std::ranges::range_reference_t<RANGE>>(container));
385 return container;
386 }
387 else if constexpr (std::ranges::input_range<std::ranges::range_reference_t<RANGE>>)
388 {
389 const auto transform = [](auto&& _Elem) {
390 return ghassanpl::to<std::ranges::range_value_t<CONTAINER>>(std::forward<decltype(_Elem)>(_Elem));
391 };
392 return ghassanpl::to<CONTAINER>(std::ranges::views::transform(range, transform), std::forward<TYPES>(args)...);
393 }
394 else
395 static_assert(always_false<CONTAINER>, "the program is ill-formed per N4910 [range.utility.conv.to]/1.3");
396 }
397
399 template <template <class...> class CONTAINER, std::ranges::input_range RANGE, class... TYPES, class _Deduced = std::remove_pointer_t<decltype(detail::to_helper<CONTAINER, RANGE, TYPES...>())>>
400 [[nodiscard]] constexpr _Deduced to(RANGE&& _Range, TYPES&&... ARGS)
401 {
402 return to<_Deduced>(std::forward<RANGE>(_Range), std::forward<TYPES>(ARGS)...);
403 }
404
406#endif
407
408 /*
409 template <class _Ty = void>
410 struct plus_equals
411 {
412 [[nodiscard]] constexpr _Ty operator()(_Ty& left, const _Ty& right) const { return left += right; }
413 };
414
415 template <>
416 struct plus_equals<void>
417 {
418 template <class _Ty1, class _Ty2>
419 [[nodiscard]] constexpr auto operator()(_Ty1& _Left, _Ty2&& _Right) const noexcept(noexcept(static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right)))
420 -> decltype(static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right))
421 {
422 return static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right);
423 }
424 using is_transparent = int;
425 };
426
427 template <std::ranges::range RANGE, std::movable FOLD_VALUE, typename FOLD_FUNC = std::plus<>>
428 requires std::is_invocable_v<FOLD_FUNC, FOLD_VALUE&, std::ranges::range_reference_t<RANGE>>
429 auto fold(RANGE&& r, FOLD_VALUE init, FOLD_FUNC&& op = {})
430 {
431 auto first = std::ranges::begin(r);
432 auto const last = std::ranges::end(r);
433 for (; first != last; ++first)
434 init = std::invoke(op, std::move(init), *first);
435 return init;
436 }
437
438 template <std::ranges::range RANGE, typename FOLD_VALUE, typename FOLD_FUNC = plus_equals<>>
439 decltype(auto) fold_while(RANGE&& r, FOLD_VALUE&& init, FOLD_FUNC&& op = {})
440 requires std::is_invocable_r_v<bool, FOLD_FUNC, FOLD_VALUE&, std::ranges::range_reference_t<RANGE>>
441 {
442 auto first = std::ranges::begin(r);
443 auto const last = std::ranges::end(r);
444 for (; first != last; ++first)
445 {
446 if (!std::invoke(op, std::ref(init), *first))
447 break;
448 }
449 return init;
450 }
451 */
452}
Concept to check if a function is a predicate on the range elements (i.e. returns boolable)
Definition ranges.h:34
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
auto at_ptr(std::map< K, V, C > const &map, VAL &&value)
Same as map_find()
Definition containers.h:160
constexpr auto join(std::array< T, Ns >... arrays)
Arrays.
Definition ranges.h:209
constexpr auto index_of(RANGE const &range, T &&value) -> std::iter_difference_t< range_iterator< RANGE > >
Find a value in range and returns an index to it.
Definition ranges.h:84
std::ranges::range_value_t< std::decay_t< RANGE > > range_value
Helper template to get the value type of a type that decays to a range
Definition ranges.h:26
constexpr decltype(auto) at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at index of range
Definition ranges.h:59
constexpr std::pair< std::span< T >, std::span< T > > split_at(std::span< T, N > span, size_t index)
Span stuff.
Definition ranges.h:148
constexpr bool valid_address(RANGE &&range, range_value< RANGE > *pointer)
Returns whether or not pointer is a valid pointer to an element in the contiguous range
Definition ranges.h:139
constexpr __contains_subrange_fn contains_subrange
contains_subrange(range, subrange)
Definition ranges.h:269
constexpr auto to_index(random_access_iterator auto iterator, random_access_range auto const &range)
Turns an iterator to range to an index.
Definition ranges.h:132
auto span_cast(std::span< FROM, N > bytes) noexcept
Casts a span of objects of type FROM to a span of objects of type TO Does not perform any checks,...
Definition ranges.h:172
constexpr bool valid_index(random_access_range auto &range, std::integral auto index)
Returns whether or not a given integer is a valid index to a random access range
Definition ranges.h:37
constexpr auto at_or_default(RANGE &&range, std::integral auto index, T &&default_value)
Return the element at index or default_value if not a valid index.
Definition ranges.h:95
constexpr auto modulo_index(size_t range_size, std::integral auto index)
Returns index converted to a valid index into a range of range_size as if using modulo arithmetic.
Definition ranges.h:44
constexpr auto find_index(RANGE const &range, FUNC &&func) -> std::iter_difference_t< range_iterator< RANGE > >
Find a value in range and returns an index to it.
Definition ranges.h:104
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:369
std::ranges::iterator_t< std::decay_t< RANGE > > range_iterator
Helper template to get the iterator type of a type that decays to a range
Definition ranges.h:30
constexpr auto find_ptr(RANGE &range, FUNC &&func)
Find a value in range and returns a pointer to it, or null if none found.
Definition ranges.h:115
constexpr __contains_fn contains
contains(range, el)
Definition ranges.h:247
constexpr decltype(auto) modulo_at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at modulo_index of range
Definition ranges.h:77
The below code is based on Sun's libm library code, which is licensed under the following license:
Primary namespace for everything in this library.
Definition align+rec2.h:10