openMSX
ranges.hh
Go to the documentation of this file.
1#ifndef RANGES_HH
2#define RANGES_HH
3
4#include <algorithm>
5#include <bit>
6#include <cassert>
7#include <cstdint>
8#include <functional>
9#include <iterator> // for std::begin(), std::end()
10#include <numeric>
11#include <span>
12#include <version> // for _LIBCPP_VERSION
13
14// Range based versions of the standard algorithms, these will likely become
15// part of c++20. For example see this post:
16// http://ericniebler.com/2018/12/05/standard-ranges/
17// In the future we can remove our implementation and instead use the standard
18// version (e.g. by a namespace alias like 'namespace ranges = std::ranges').
19//
20// All of the range algorithms below do nothing more than delegate to the
21// corresponding iterator-pair version of the algorithm.
22//
23// This list of algorithms is not complete. But it's easy to extend if/when we
24// need more.
25
26namespace ranges {
27
28// (Simplified) implementation of c++20 "range" and "sized_range" concepts.
29// * A "range" is something that has a begin() and end().
30// * A "sized_range" in addition has a size().
31template<typename T>
32concept range = requires(T& t) {
33 std::begin(t);
34 std::end (t);
35};
36
37template<typename T>
38concept sized_range = range<T> && requires(T& t) { std::size(t); };
39
40
41template<typename ForwardRange, typename Compare = std::less<>, typename Proj = std::identity>
42[[nodiscard]] bool is_sorted(ForwardRange&& range, Compare comp = {}, Proj proj = {})
43{
44 return std::is_sorted(std::begin(range), std::end(range),
45 [&](const auto& x, const auto& y) {
46 return comp(std::invoke(proj, x), std::invoke(proj, y));
47 });
48}
49
50template<typename RandomAccessRange>
51constexpr void sort(RandomAccessRange&& range)
52{
53 std::sort(std::begin(range), std::end(range));
54}
55
56template<typename RandomAccessRange, typename Compare>
57constexpr void sort(RandomAccessRange&& range, Compare comp)
58{
59 std::sort(std::begin(range), std::end(range), comp);
60}
61
62template<typename RAIter, typename Compare = std::less<>, typename Proj>
63constexpr void sort(RAIter first, RAIter last, Compare comp, Proj proj)
64{
65 std::sort(first, last,
66 [&](const auto& x, const auto& y) {
67 return comp(std::invoke(proj, x), std::invoke(proj, y));
68 });
69}
70
71template<typename RandomAccessRange, typename Compare = std::less<>, typename Proj>
72constexpr void sort(RandomAccessRange&& range, Compare comp, Proj proj)
73{
74 sort(std::begin(range), std::end(range), comp, proj);
75}
76
77template<typename RandomAccessRange>
78void stable_sort(RandomAccessRange&& range)
79{
80 std::stable_sort(std::begin(range), std::end(range));
81}
82
83template<typename RandomAccessRange, typename Compare>
84void stable_sort(RandomAccessRange&& range, Compare comp)
85{
86 std::stable_sort(std::begin(range), std::end(range), comp);
87}
88
89template<typename RAIter, typename Compare = std::less<>, typename Proj>
90void stable_sort(RAIter first, RAIter last, Compare comp, Proj proj)
91{
92 std::stable_sort(first, last,
93 [&](const auto& x, const auto& y) {
94 return comp(std::invoke(proj, x), std::invoke(proj, y));
95 });
96}
97
98template<typename RandomAccessRange, typename Compare = std::less<>, typename Proj>
99void stable_sort(RandomAccessRange&& range, Compare comp, Proj proj)
100{
101 stable_sort(std::begin(range), std::end(range), comp, proj);
102}
103
104template<typename ForwardRange, typename T>
105[[nodiscard]] bool binary_search(ForwardRange&& range, const T& value)
106{
107 return std::binary_search(std::begin(range), std::end(range), value);
108}
109
110template<typename ForwardRange, typename T, typename Compare>
111[[nodiscard]] bool binary_search(ForwardRange&& range, const T& value, Compare comp)
112{
113 return std::binary_search(std::begin(range), std::end(range), value, comp);
114}
115
116template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
117[[nodiscard]] auto lower_bound(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
118{
119 auto comp2 = [&](const auto& x, const auto& y) {
120 return comp(std::invoke(proj, x), y);
121 };
122 return std::lower_bound(std::begin(range), std::end(range), value, comp2);
123}
124
125template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
126[[nodiscard]] auto upper_bound(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
127{
128 auto comp2 = [&](const auto& x, const auto& y) {
129 return comp(x, std::invoke(proj, y));
130 };
131 return std::upper_bound(std::begin(range), std::end(range), value, comp2);
132}
133
134template<typename ForwardRange, typename T, typename Compare = std::less<>>
135[[nodiscard]] auto equal_range(ForwardRange&& range, const T& value, Compare comp = {})
136{
137 return std::equal_range(std::begin(range), std::end(range), value, comp);
138}
139template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
140[[nodiscard]] auto equal_range(ForwardRange&& range, const T& value, Compare comp, Proj proj)
141{
142 using Iter = decltype(std::begin(range));
143 using R = typename std::iterator_traits<Iter>::value_type;
144 struct Comp2 {
145 [[no_unique_address]] Compare comp;
146 [[no_unique_address]] Proj proj;
147
148 bool operator()(const R& x, const R& y) const {
149 return comp(std::invoke(proj, x), std::invoke(proj, y));
150 }
151 bool operator()(const R& x, const T& y) const {
152 return comp(std::invoke(proj, x), y);
153 }
154 bool operator()(const T& x, const R& y) const {
155 return comp(x, std::invoke(proj, y));
156 }
157 };
158 return std::equal_range(std::begin(range), std::end(range), value, Comp2{comp, proj});
159}
160
161template<typename InputRange, typename T>
162[[nodiscard]] auto find(InputRange&& range, const T& value)
163{
164 return std::find(std::begin(range), std::end(range), value);
165}
166
167template<typename InputRange, typename T, typename Proj>
168[[nodiscard]] auto find(InputRange&& range, const T& value, Proj proj)
169{
170 return find_if(std::forward<InputRange>(range),
171 [&](const auto& e) { return std::invoke(proj, e) == value; });
172}
173
174template<typename InputRange, typename UnaryPredicate>
175[[nodiscard]] auto find_if(InputRange&& range, UnaryPredicate pred)
176{
177 auto it = std::begin(range);
178 auto et = std::end(range);
179 for (; it != et; ++it) {
180 if (std::invoke(pred, *it)) {
181 return it;
182 }
183 }
184 return it;
185}
186
187template<typename InputRange, typename UnaryPredicate>
188[[nodiscard]] constexpr bool all_of(InputRange&& range, UnaryPredicate pred)
189{
190 //return std::all_of(std::begin(range), std::end(range), pred);
191 auto it = std::begin(range);
192 auto et = std::end(range); // allow 'it' and 'et' to have different types
193 for (; it != et; ++it) {
194 if (!std::invoke(pred, *it)) return false;
195 }
196 return true;
197}
198
199template<typename InputRange, typename UnaryPredicate>
200[[nodiscard]] constexpr bool any_of(InputRange&& range, UnaryPredicate pred)
201{
202 //return std::any_of(std::begin(range), std::end(range), pred);
203 auto it = std::begin(range);
204 auto et = std::end(range); // allow 'it' and 'et' to have different types
205 for (; it != et; ++it) {
206 if (std::invoke(pred, *it)) return true;
207 }
208 return false;
209}
210
211template<typename InputRange, typename UnaryPredicate>
212[[nodiscard]] constexpr bool none_of(InputRange&& range, UnaryPredicate pred)
213{
214 //return std::none_of(std::begin(range), std::end(range), pred);
215 auto it = std::begin(range);
216 auto et = std::end(range); // allow 'it' and 'et' to have different types
217 for (; it != et; ++it) {
218 if (std::invoke(pred, *it)) return false;
219 }
220 return true;
221}
222
223template<typename ForwardRange>
224[[nodiscard]] auto unique(ForwardRange&& range)
225{
226 return std::unique(std::begin(range), std::end(range));
227}
228
229template<typename ForwardRange, typename BinaryPredicate>
230[[nodiscard]] auto unique(ForwardRange&& range, BinaryPredicate pred)
231{
232 return std::unique(std::begin(range), std::end(range), pred);
233}
234
235template<typename RAIter, typename Compare = std::equal_to<>, typename Proj>
236[[nodiscard]] auto unique(RAIter first, RAIter last, Compare comp, Proj proj)
237{
238 return std::unique(first, last,
239 [&](const auto& x, const auto& y) {
240 return comp(std::invoke(proj, x), std::invoke(proj, y));
241 });
242}
243
244template<typename RandomAccessRange, typename Compare = std::equal_to<>, typename Proj>
245[[nodiscard]] auto unique(RandomAccessRange&& range, Compare comp, Proj proj)
246{
247 return unique(std::begin(range), std::end(range), comp, proj);
248}
249
250template<typename InputRange, typename OutputIter>
251 requires(!range<OutputIter>)
252constexpr auto copy(InputRange&& range, OutputIter out)
253{
254 return std::copy(std::begin(range), std::end(range), out);
255}
256
257template<sized_range Input, sized_range Output>
258constexpr auto copy(Input&& in, Output&& out)
259{
260 assert(std::size(in) <= std::size(out));
261 // Workaround: this doesn't work when copying a std::initializer_list into a std::array with libstdc++ debug-STL ???
262 // return std::copy(std::begin(in), std::end(in), std::begin(out));
263 auto f = std::begin(in);
264 auto l = std::end(in);
265 auto o = std::begin(out);
266 while (f != l) {
267 *o++ = *f++;
268 }
269 return o;
270}
271
272template<typename InputRange, typename OutputIter, typename UnaryPredicate>
273auto copy_if(InputRange&& range, OutputIter out, UnaryPredicate pred)
274{
275 return std::copy_if(std::begin(range), std::end(range), out, pred);
276}
277
278template<typename InputRange, typename OutputIter, typename UnaryOperation>
279auto transform(InputRange&& range, OutputIter out, UnaryOperation op)
280{
281 return std::transform(std::begin(range), std::end(range), out, op);
282}
283
284template<typename ForwardRange, typename Generator>
285void generate(ForwardRange&& range, Generator&& g)
286{
287 std::generate(std::begin(range), std::end(range), std::forward<Generator>(g));
288}
289
290template<typename ForwardRange, typename T>
291[[nodiscard]] auto remove(ForwardRange&& range, const T& value)
292{
293 return std::remove(std::begin(range), std::end(range), value);
294}
295
296template<typename ForwardRange, typename UnaryPredicate>
297[[nodiscard]] auto remove_if(ForwardRange&& range, UnaryPredicate pred)
298{
299 return std::remove_if(std::begin(range), std::end(range), pred);
300}
301
302template<typename ForwardRange, typename T>
303constexpr void replace(ForwardRange&& range, const T& old_value, const T& new_value)
304{
305 std::replace(std::begin(range), std::end(range), old_value, new_value);
306}
307
308template<typename ForwardRange, typename UnaryPredicate, typename T>
309void replace_if(ForwardRange&& range, UnaryPredicate pred, const T& new_value)
310{
311 std::replace_if(std::begin(range), std::end(range), pred, new_value);
312}
313
314template<typename ForwardRange, typename T>
315constexpr void fill(ForwardRange&& range, const T& value)
316{
317 std::fill(std::begin(range), std::end(range), value);
318}
319
320// part of c++20
321template<typename ForwardIt, typename T>
322constexpr void iota(ForwardIt first, ForwardIt last, T value)
323{
324 while (first != last) {
325 *first++ = value;
326 ++value;
327 }
328}
329
330template<typename ForwardRange, typename T>
331constexpr void iota(ForwardRange&& range, T&& value)
332{
333 ::ranges::iota(std::begin(range), std::end(range), std::forward<T>(value));
334}
335
336template<typename InputRange, typename T>
337[[nodiscard]] T accumulate(InputRange&& range, T init)
338{
339 return std::accumulate(std::begin(range), std::end(range), init);
340}
341
342template<typename InputRange, typename T, typename BinaryOperation>
343[[nodiscard]] T accumulate(InputRange&& range, T init, BinaryOperation op)
344{
345 return std::accumulate(std::begin(range), std::end(range), init, op);
346}
347
348template<typename InputRange, typename T>
349[[nodiscard]] auto count(InputRange&& range, const T& value)
350{
351 return std::count(std::begin(range), std::end(range), value);
352}
353
354template<typename InputRange, typename UnaryPredicate>
355[[nodiscard]] auto count_if(InputRange&& range, UnaryPredicate pred)
356{
357 auto first = std::begin(range);
358 auto last = std::end(range);
359 typename std::iter_difference_t<decltype(first)> count = 0;
360 while (first != last) {
361 if (std::invoke(pred, *first++)) ++count;
362 }
363 return count;
364 //return std::count_if(std::begin(range), std::end(range), pred);
365}
366
367template<typename InputRange1, typename InputRange2, typename OutputIter>
368auto set_difference(InputRange1&& range1, InputRange2&& range2, OutputIter out)
369{
370 return std::set_difference(std::begin(range1), std::end(range1),
371 std::begin(range2), std::end(range2),
372 out);
373}
374
375template<range InputRange1, range InputRange2,
376 typename Pred = std::equal_to<void>,
377 typename Proj1 = std::identity, typename Proj2 = std::identity>
378[[nodiscard]] constexpr bool equal(InputRange1&& range1, InputRange2&& range2, Pred pred = {},
379 Proj1 proj1 = {}, Proj2 proj2 = {})
380{
381 auto it1 = std::begin(range1);
382 auto it2 = std::begin(range2);
383 auto et1 = std::end(range1);
384 auto et2 = std::end(range2);
385 for (; (it1 != et1) && (it2 != et2); ++it1, ++it2) {
386 if (!std::invoke(pred, std::invoke(proj1, *it1), std::invoke(proj2, *it2))) {
387 return false;
388 }
389 }
390 return (it1 == et1) && (it2 == et2);
391}
392
393template<sized_range SizedRange1, sized_range SizedRange2,
394 typename Pred = std::equal_to<void>,
395 typename Proj1 = std::identity, typename Proj2 = std::identity>
396[[nodiscard]] constexpr bool equal(SizedRange1&& range1, SizedRange2&& range2, Pred pred = {},
397 Proj1 proj1 = {}, Proj2 proj2 = {})
398{
399 if (std::size(range1) != std::size(range2)) return false;
400
401 auto it1 = std::begin(range1);
402 auto it2 = std::begin(range2);
403 auto et1 = std::end(range1);
404 for (; it1 != et1; ++it1, ++it2) {
405 if (!std::invoke(pred, std::invoke(proj1, *it1), std::invoke(proj2, *it2))) {
406 return false;
407 }
408 }
409 return true;
410}
411
412// Test whether all elements in the given range are equal to each other (after
413// applying a projection).
414template<typename InputRange, typename Proj = std::identity>
415bool all_equal(InputRange&& range, Proj proj = {})
416{
417 auto it = std::begin(range);
418 auto et = std::end(range);
419 if (it == et) return true;
420
421 auto val = std::invoke(proj, *it);
422 for (++it; it != et; ++it) {
423 if (std::invoke(proj, *it) != val) {
424 return false;
425 }
426 }
427 return true;
428}
429
430} // namespace ranges
431
432// Perform a binary-search in a sorted range.
433// The given 'range' must be sorted according to 'comp'.
434// We search for 'value' after applying 'proj' to the elements in the range.
435// Returns a pointer to the element in 'range', or nullptr if not found.
436//
437// This helper function is typically used to simplify code like:
438// auto it = ranges::lower_bound(myMap, name, {}, &Element::name);
439// if ((it != myMap.end()) && (it->name == name)) {
440// ... use *it
441// }
442// Note that this code needs to check both for the end-iterator and that the
443// element was actually found. By using binary_find() this complexity is hidden:
444// if (auto m = binary_find(myMap, name, {}, &Element::name)) {
445// ... use *m
446// }
447template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
448[[nodiscard]] auto* binary_find(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
449{
450 auto it = ranges::lower_bound(range, value, comp, proj);
451 return ((it != std::end(range)) && (!std::invoke(comp, value, std::invoke(proj, *it))))
452 ? &*it
453 : nullptr;
454}
455
456// Convenience function to convert part of an array (or vector, ...) into a
457// span. These are not part of the c++ ranges namespace, but often these results
458// are then further used in range algorithms, that's why I placed them in this
459// header.
460
461template<typename Range>
462constexpr auto make_span(Range&& range)
463{
464#ifndef _LIBCPP_VERSION
465 // C++20 version, works with gcc/visual studio
466 // Simple enough that we don't actually need this helper function.
467 return std::span(range);
468#else
469 // Unfortunately we do need a workaround for clang/libc++, version 14 and 15
470 // return std::span(std::begin(range), std::end(range));
471
472 // Further workaround for Xcode-14, clang-13
473 // Don't always use this workaround, because '&*begin()' is actually
474 // undefined behavior when the range is empty. It works fine in
475 // practice, except when using a DEBUG-STL version.
476 return std::span(&*std::begin(range), std::size(range));
477#endif
478}
479
480template<typename Range>
481[[nodiscard]] constexpr auto subspan(Range&& range, size_t offset, size_t count = std::dynamic_extent)
482{
483 return make_span(std::forward<Range>(range)).subspan(offset, count);
484}
485
486template<size_t Count, typename Range>
487[[nodiscard]] constexpr auto subspan(Range&& range, size_t offset = 0)
488{
489 return make_span(std::forward<Range>(range)).subspan(offset).template first<Count>();
490}
491
492template<typename T, size_t Size>
493[[nodiscard]] inline auto as_byte_span(std::span<T, Size> s)
494{
495 return std::span{std::bit_cast<const uint8_t*>(s.data()), s.size_bytes()};
496}
497
498#endif
int g
TclObject t
bool comp(const uint8_t *p, const uint8_t *q)
bool is_sorted(ForwardRange &&range, Compare comp={}, Proj proj={})
Definition ranges.hh:42
constexpr bool all_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:188
bool binary_search(ForwardRange &&range, const T &value)
Definition ranges.hh:105
auto unique(ForwardRange &&range)
Definition ranges.hh:224
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:315
auto remove(ForwardRange &&range, const T &value)
Definition ranges.hh:291
constexpr bool none_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:212
void replace_if(ForwardRange &&range, UnaryPredicate pred, const T &new_value)
Definition ranges.hh:309
bool all_equal(InputRange &&range, Proj proj={})
Definition ranges.hh:415
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:175
auto count(InputRange &&range, const T &value)
Definition ranges.hh:349
auto remove_if(ForwardRange &&range, UnaryPredicate pred)
Definition ranges.hh:297
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition ranges.hh:322
T accumulate(InputRange &&range, T init)
Definition ranges.hh:337
void stable_sort(RandomAccessRange &&range)
Definition ranges.hh:78
auto find(InputRange &&range, const T &value)
Definition ranges.hh:162
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition ranges.hh:126
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition ranges.hh:135
constexpr bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
Definition ranges.hh:378
auto set_difference(InputRange1 &&range1, InputRange2 &&range2, OutputIter out)
Definition ranges.hh:368
void generate(ForwardRange &&range, Generator &&g)
Definition ranges.hh:285
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition ranges.hh:279
auto copy_if(InputRange &&range, OutputIter out, UnaryPredicate pred)
Definition ranges.hh:273
constexpr auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:252
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
Definition ranges.hh:303
constexpr bool any_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:200
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:51
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition ranges.hh:117
auto count_if(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:355
auto as_byte_span(std::span< T, Size > s)
Definition ranges.hh:493
constexpr auto make_span(Range &&range)
Definition ranges.hh:462
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:481
auto * binary_find(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition ranges.hh:448