openMSX
stl.hh
Go to the documentation of this file.
1#ifndef STL_HH
2#define STL_HH
3
4#include <algorithm>
5#include <cassert>
6#include <functional>
7#include <iterator>
8#include <initializer_list>
9#include <map>
10#include <numeric>
11#include <tuple>
12#include <utility>
13#include <variant>
14#include <vector>
15
16// shared between various other classes
18
19// Predicate that can be called with any number of parameters (of any type) and
20// just always returns 'true'. This can be useful as a default parameter value.
22 template<typename ...Args>
23 bool operator()(Args&& ...) const {
24 return true;
25 }
26};
27
34template<typename ITER, typename VAL>
35[[nodiscard]] constexpr bool contains(ITER first, ITER last, const VAL& val)
36{
37 // c++20: return std::find(first, last, val) != last;
38 while (first != last) {
39 if (*first == val) return true;
40 ++first;
41 }
42 return false;
43}
44template<typename RANGE, typename VAL>
45[[nodiscard]] constexpr bool contains(const RANGE& range, const VAL& val)
46{
47 return contains(std::begin(range), std::end(range), val);
48}
49
50template<typename ITER, typename VAL, typename Proj>
51[[nodiscard]] /*constexpr*/ bool contains(ITER first, ITER last, const VAL& val, Proj proj)
52{
53 // c++20: return std::find(first, last, val) != last;
54 while (first != last) {
55 if (std::invoke(proj, *first) == val) return true;
56 ++first;
57 }
58 return false;
59}
60template<typename RANGE, typename VAL, typename Proj>
61[[nodiscard]] /*constexpr*/ bool contains(const RANGE& range, const VAL& val, Proj proj)
62{
63 return contains(std::begin(range), std::end(range), val, proj);
64}
65
66
74template<typename ITER, typename VAL, typename Proj = std::identity>
75[[nodiscard]] /*constexpr*/ ITER find_unguarded(ITER first, ITER last, const VAL& val, Proj proj = {})
76{
77 return find_if_unguarded(first, last,
78 [&](const auto& e) { return std::invoke(proj, e) == val; });
79}
80template<typename RANGE, typename VAL, typename Proj = std::identity>
81[[nodiscard]] /*constexpr*/ auto find_unguarded(RANGE& range, const VAL& val, Proj proj = {})
82{
83 return find_unguarded(std::begin(range), std::end(range), val, proj);
84}
85
90template<typename ITER, typename PRED>
91[[nodiscard]] constexpr ITER find_if_unguarded(ITER first, ITER last, PRED pred)
92{
93 (void)last;
94 while (true) {
95 assert(first != last);
96 if (pred(*first)) return first;
97 ++first;
98 }
99}
100template<typename RANGE, typename PRED>
101[[nodiscard]] constexpr auto find_if_unguarded(RANGE& range, PRED pred)
102{
103 return find_if_unguarded(std::begin(range), std::end(range), pred);
104}
105
111template<typename RANGE, typename VAL, typename Proj = std::identity>
112[[nodiscard]] /*constexpr*/ auto rfind_unguarded(RANGE& range, const VAL& val, Proj proj = {})
113{
114 auto it = find_unguarded(std::rbegin(range), std::rend(range), val, proj);
115 ++it;
116 return it.base();
117}
118
119template<typename RANGE, typename PRED>
120[[nodiscard]] constexpr auto rfind_if_unguarded(RANGE& range, PRED pred)
121{
122 auto it = find_if_unguarded(std::rbegin(range), std::rend(range), pred);
123 ++it;
124 return it.base();
125}
126
127
136template<typename VECTOR>
137void move_pop_back(VECTOR& v, typename VECTOR::iterator it)
138{
139 // Check for self-move-assignment.
140 //
141 // This check is only needed in libstdc++ when compiled with
142 // -D_GLIBCXX_DEBUG. In non-debug mode this routine works perfectly
143 // fine without the check.
144 //
145 // See here for a related discussion:
146 // http://stackoverflow.com/questions/13129031/on-implementing-stdswap-in-terms-of-move-assignment-and-move-constructor
147 // It's not clear whether the assert in libstdc++ is conforming
148 // behavior.
149 if (&*it != &v.back()) {
150 *it = std::move(v.back());
151 }
152 v.pop_back();
153}
154
155
171template<typename ForwardIt, typename OutputIt, typename UnaryPredicate>
172[[nodiscard]] std::pair<OutputIt, ForwardIt> partition_copy_remove(
173 ForwardIt first, ForwardIt last, OutputIt out_true, UnaryPredicate p)
174{
175 first = std::find_if(first, last, p);
176 auto out_false = first;
177 if (first != last) {
178 goto l_true;
179 while (first != last) {
180 if (p(*first)) {
181l_true: *out_true++ = std::move(*first++);
182 } else {
183 *out_false++ = std::move(*first++);
184 }
185 }
186 }
187 return std::pair(out_true, out_false);
188}
189
190template<typename ForwardRange, typename OutputIt, typename UnaryPredicate>
191[[nodiscard]] auto partition_copy_remove(ForwardRange&& range, OutputIt out_true, UnaryPredicate p)
192{
193 return partition_copy_remove(std::begin(range), std::end(range), out_true, p);
194}
195
196
197// Like range::transform(), but with equal source and destination.
198template<typename ForwardRange, typename UnaryOperation>
199auto transform_in_place(ForwardRange&& range, UnaryOperation op)
200{
201 return std::transform(std::begin(range), std::end(range), std::begin(range), op);
202}
203
204
205// Returns (a copy of) the minimum value in [first, last).
206// Requires: first != last.
207template<typename InputIterator, typename Proj = std::identity>
208[[nodiscard]] /*constexpr*/ auto min_value(InputIterator first, InputIterator last, Proj proj = {})
209{
210 assert(first != last);
211 auto result = std::invoke(proj, *first++);
212 while (first != last) {
213 result = std::min(result, std::invoke(proj, *first++));
214 }
215 return result;
216}
217
218template<typename InputRange, typename Proj = std::identity>
219[[nodiscard]] /*constexpr*/ auto min_value(InputRange&& range, Proj proj = {})
220{
221 return min_value(std::begin(range), std::end(range), proj);
222}
223
224// Returns (a copy of) the maximum value in [first, last).
225// Requires: first != last.
226template<typename InputIterator, typename Proj = std::identity>
227[[nodiscard]] /*constexpr*/ auto max_value(InputIterator first, InputIterator last, Proj proj = {})
228{
229 assert(first != last);
230 auto result = std::invoke(proj, *first++);
231 while (first != last) {
232 result = std::max(result, std::invoke(proj, *first++));
233 }
234 return result;
235}
236
237template<typename InputRange, typename Proj = std::identity>
238[[nodiscard]] /*constexpr*/ auto max_value(InputRange&& range, Proj proj = {})
239{
240 return max_value(std::begin(range), std::end(range), proj);
241}
242
243
244// Returns the sum of the elements in the given range.
245// Assumes: elements can be summed via operator+, with a default constructed
246// value being the identity-element for this operator.
247template<typename InputRange, typename Proj = std::identity>
248[[nodiscard]] constexpr auto sum(InputRange&& range, Proj proj = {})
249{
250 using Iter = decltype(std::begin(range));
251 using VT = typename std::iterator_traits<Iter>::value_type;
252 using RT = decltype(std::invoke(proj, std::declval<VT>()));
253
254 auto first = std::begin(range);
255 auto last = std::end(range);
256 RT init{};
257 while (first != last) {
258 init = std::move(init) + std::invoke(proj, *first++);
259 }
260 return init;
261}
262
263// to_vector
264namespace detail {
265 template<typename T, typename Iterator>
266 using ToVectorType = std::conditional_t<
267 std::is_same_v<T, void>,
268 typename std::iterator_traits<Iterator>::value_type,
269 T>;
270}
271
272// Convert any range to a vector. Optionally specify the type of the elements
273// in the result.
274// Example:
275// auto v1 = to_vector(view::drop(my_list, 3));
276// auto v2 = to_vector<Base*>(getDerivedPtrs());
277template<typename T = void, typename Range>
278[[nodiscard]] auto to_vector(Range&& range)
279 -> std::vector<detail::ToVectorType<T, decltype(std::begin(range))>>
280{
281 return {std::begin(range), std::end(range)};
282}
283
284// Optimized version for r-value input and no type conversion.
285template<typename T>
286[[nodiscard]] auto to_vector(std::vector<T>&& v)
287{
288 return std::move(v);
289}
290
291
292// append() / concat()
293namespace detail {
294
295template<typename... Ranges>
296[[nodiscard]] constexpr size_t sum_of_sizes(const Ranges&... ranges)
297{
298 return (0 + ... + std::distance(std::begin(ranges), std::end(ranges)));
299}
300
301template<typename Result>
302void append(Result&)
303{
304 // nothing
305}
306
307template<typename Result, typename Range, typename... Tail>
308void append(Result& x, Range&& y, Tail&&... tail)
309{
310#ifdef _GLIBCXX_DEBUG
311 // Range can be a view::transform
312 // but vector::insert in libstdc++ debug mode will wrongly try
313 // to take its address to check for self-insertion (see
314 // gcc/include/c++/7.1.0/debug/functions.h
315 // __foreign_iterator_aux functions). So avoid vector::insert
316 for (auto&& e : y) {
317 x.emplace_back(std::forward<decltype(e)>(e));
318 }
319#else
320 x.insert(std::end(x), std::begin(y), std::end(y));
321#endif
322 detail::append(x, std::forward<Tail>(tail)...);
323}
324
325// Allow move from an rvalue-vector.
326// But don't allow to move from any rvalue-range. It breaks stuff like
327// append(v, view::reverse(w));
328template<typename Result, typename T2, typename... Tail>
329void append(Result& x, std::vector<T2>&& y, Tail&&... tail)
330{
331 x.insert(std::end(x),
332 std::move_iterator(std::begin(y)),
333 std::move_iterator(std::end(y)));
334 detail::append(x, std::forward<Tail>(tail)...);
335}
336
337} // namespace detail
338
339// Append a range to a vector.
340template<typename T, typename... Tail>
341void append(std::vector<T>& v, Tail&&... tail)
342{
343 auto extra = detail::sum_of_sizes(std::forward<Tail>(tail)...);
344 auto current = v.size();
345 if (auto required = current + extra;
346 v.capacity() < required) {
347 v.reserve(current + std::max(current, extra));
348 }
349 detail::append(v, std::forward<Tail>(tail)...);
350}
351
352// If both source and destination are vectors of the same type and the
353// destination is empty and the source is an rvalue, then move the whole vector
354// at once instead of moving element by element.
355template<typename T>
356void append(std::vector<T>& v, std::vector<T>&& range)
357{
358 if (v.empty()) {
359 v = std::move(range);
360 } else {
361 v.insert(std::end(v),
362 std::move_iterator(std::begin(range)),
363 std::move_iterator(std::end(range)));
364 }
365}
366
367template<typename T>
368void append(std::vector<T>& x, std::initializer_list<T> list)
369{
370 x.insert(x.end(), list);
371}
372
373
374template<typename T = void, typename Range, typename... Tail>
375[[nodiscard]] auto concat(const Range& range, Tail&&... tail)
376{
377 using T2 = detail::ToVectorType<T, decltype(std::begin(range))>;
378 std::vector<T2> result;
379 append(result, range, std::forward<Tail>(tail)...);
380 return result;
381}
382
383template<typename T, typename... Tail>
384[[nodiscard]] std::vector<T> concat(std::vector<T>&& v, Tail&&... tail)
385{
386 append(v, std::forward<Tail>(tail)...);
387 return std::move(v);
388}
389
390
391// Concatenate two std::arrays (at compile time).
392template<typename T, size_t X, size_t Y>
393constexpr auto concatArray(const std::array<T, X>& x, const std::array<T, Y>& y)
394{
395 std::array<T, X + Y> result = {};
396 // c++20: std::ranges::copy(x, &result[0]);
397 // c++20: std::ranges::copy(y, &result[X]);
398 for (size_t i = 0; i < X; ++i) result[0 + i] = x[i];
399 for (size_t i = 0; i < Y; ++i) result[X + i] = y[i];
400 return result;
401}
402// TODO implement in a generic way for any number of arrays
403template<typename T, size_t X, size_t Y, size_t Z>
404constexpr auto concatArray(const std::array<T, X>& x,
405 const std::array<T, Y>& y,
406 const std::array<T, Z>& z)
407{
408 std::array<T, X + Y + Z> result = {};
409 for (size_t i = 0; i < X; ++i) result[ i] = x[i];
410 for (size_t i = 0; i < Y; ++i) result[X + i] = y[i];
411 for (size_t i = 0; i < Z; ++i) result[X + Y + i] = z[i];
412 return result;
413}
414
415
416// lookup in std::map
417template<typename Key, typename Value, typename Key2>
418[[nodiscard]] const Value* lookup(const std::map<Key, Value>& m, const Key2& k)
419{
420 auto it = m.find(k);
421 return (it != m.end()) ? &it->second : nullptr;
422}
423
424template<typename Key, typename Value, typename Key2>
425[[nodiscard]] Value* lookup(std::map<Key, Value>& m, const Key2& k)
426{
427 auto it = m.find(k);
428 return (it != m.end()) ? &it->second : nullptr;
429}
430
431// will likely become part of future c++ standard
432template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
433// explicit deduction guide (not needed as of C++20)
434template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
435
436
437// --- Utility to retrieve the index for a given type from a std::variant ---
438
439template<typename> struct get_index_tag {};
440
441template<typename T, typename V> struct get_index;
442
443template<typename T, typename... Ts>
444struct get_index<T, std::variant<Ts...>>
445 : std::integral_constant<size_t, std::variant<get_index_tag<Ts>...>(get_index_tag<T>()).index()> {};
446
447
448// Utility to initialize an array via a generator function,
449// without first default constructing the elements.
450
451template<typename T, typename F, size_t... Is>
452[[nodiscard]] static constexpr auto generate_array(F f, std::index_sequence<Is...>)
453 -> std::array<T, sizeof...(Is)>
454{
455 return {{f(Is)...}};
456}
457
458template<size_t N, typename F>
459[[nodiscard]] static constexpr auto generate_array(F f)
460{
461 using T = decltype(f(0));
462 return generate_array<T>(f, std::make_index_sequence<N>{});
463}
464
465
466// c++23 std::to_underlying
467template<typename E>
468[[nodiscard]] constexpr auto to_underlying(E e) noexcept {
469 return static_cast<std::underlying_type_t<E>>(e);
470}
471
472// Like std::array, but operator[] takes an enum
473template<typename Enum, typename T, size_t S = size_t(-1)>
475private:
476 [[nodiscard]] static constexpr size_t get_size_helper() {
477 if constexpr (requires { Enum::NUM; }) {
478 return (S == size_t(-1)) ? static_cast<size_t>(Enum::NUM) : S;
479 } else {
480 assert(S != -1);
481 return S;
482 }
483 }
484
485public:
486 std::array<T, get_size_helper()> storage;
487
488 // Note: explicitly NO constructors, we want aggregate initialization
489
490 [[nodiscard]] constexpr const auto& operator[](Enum e) const { return storage[to_underlying(e)]; }
491 [[nodiscard]] constexpr auto& operator[](Enum e) { return storage[to_underlying(e)]; }
492
493 // This list is incomplete. Add more when needed.
494 [[nodiscard]] constexpr auto begin() const { return storage.begin(); }
495 [[nodiscard]] constexpr auto begin() { return storage.begin(); }
496 [[nodiscard]] constexpr auto end() const { return storage.end(); }
497 [[nodiscard]] constexpr auto end() { return storage.end(); }
498
499 [[nodiscard]] constexpr auto empty() const { return storage.empty(); }
500 [[nodiscard]] constexpr auto size() const { return storage.size(); }
501
502 [[nodiscard]] constexpr const auto* data() const { return storage.data(); }
503 [[nodiscard]] constexpr auto* data() { return storage.data(); }
504
505 [[nodiscard]] friend constexpr auto operator<=>(const array_with_enum_index& x, const array_with_enum_index& y) = default;
506};
507
508template<typename Iterator, typename Sentinel>
510{
511public:
512 iterator_range(Iterator first_, Sentinel last_)
513 : first(first_), last(last_) {}
514
515 Iterator begin() const { return first; }
516 Sentinel end() const { return last; }
517
518private:
519 Iterator first;
520 Sentinel last;
521};
522
523#endif
Sentinel end() const
Definition stl.hh:516
iterator_range(Iterator first_, Sentinel last_)
Definition stl.hh:512
Iterator begin() const
Definition stl.hh:515
Definition join.hh:10
void append(Result &)
Definition stl.hh:302
std::conditional_t< std::is_same_v< T, void >, typename std::iterator_traits< Iterator >::value_type, T > ToVectorType
Definition stl.hh:269
constexpr size_t sum_of_sizes(const Ranges &... ranges)
Definition stl.hh:296
STL namespace.
constexpr auto sum(InputRange &&range, Proj proj={})
Definition stl.hh:248
ITER find_unguarded(ITER first, ITER last, const VAL &val, Proj proj={})
Faster alternative to 'find' when it's guaranteed that the value will be found (if not the behavior i...
Definition stl.hh:75
const Value * lookup(const std::map< Key, Value > &m, const Key2 &k)
Definition stl.hh:418
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
Definition stl.hh:278
std::pair< OutputIt, ForwardIt > partition_copy_remove(ForwardIt first, ForwardIt last, OutputIt out_true, UnaryPredicate p)
This is like a combination of partition_copy() and remove().
Definition stl.hh:172
constexpr ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to 'find_if' when it's guaranteed that the predicate will be true for at least one...
Definition stl.hh:91
constexpr auto to_underlying(E e) noexcept
Definition stl.hh:468
auto min_value(InputIterator first, InputIterator last, Proj proj={})
Definition stl.hh:208
void append(std::vector< T > &v, Tail &&... tail)
Definition stl.hh:341
auto max_value(InputIterator first, InputIterator last, Proj proj={})
Definition stl.hh:227
constexpr auto concatArray(const std::array< T, X > &x, const std::array< T, Y > &y)
Definition stl.hh:393
auto transform_in_place(ForwardRange &&range, UnaryOperation op)
Definition stl.hh:199
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition stl.hh:137
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition stl.hh:112
auto concat(const Range &range, Tail &&... tail)
Definition stl.hh:375
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:35
constexpr auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition stl.hh:120
Definition stl_test.cc:7
bool operator()(Args &&...) const
Definition stl.hh:23
constexpr auto size() const
Definition stl.hh:500
constexpr auto empty() const
Definition stl.hh:499
friend constexpr auto operator<=>(const array_with_enum_index &x, const array_with_enum_index &y)=default
constexpr const auto & operator[](Enum e) const
Definition stl.hh:490
constexpr const auto * data() const
Definition stl.hh:502
constexpr auto begin() const
Definition stl.hh:494
constexpr auto end() const
Definition stl.hh:496
constexpr auto begin()
Definition stl.hh:495
constexpr auto end()
Definition stl.hh:497
constexpr auto & operator[](Enum e)
Definition stl.hh:491
std::array< T, get_size_helper()> storage
Definition stl.hh:486
constexpr auto * data()
Definition stl.hh:503