1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ITERTOOLS_HPP
#define RGBDS_ITERTOOLS_HPP
#include <deque>
#include <optional>
#include <stddef.h>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
// A wrapper around iterables to reverse their iteration order; used in `for`-each loops.
template<typename IterableT>
struct ReversedIterable {
IterableT &_iterable;
};
template<typename IterableT>
auto begin(ReversedIterable<IterableT> r) {
return std::rbegin(r._iterable);
}
template<typename IterableT>
auto end(ReversedIterable<IterableT> r) {
return std::rend(r._iterable);
}
template<typename IterableT>
ReversedIterable<IterableT> reversed(IterableT &&_iterable) {
return {_iterable};
}
// A map from `std::string` keys to `ItemT` items, iterable in the order the items were inserted.
template<typename ItemT>
class InsertionOrderedMap {
std::deque<ItemT> list;
std::unordered_map<std::string, size_t> map; // Indexes into `list`
public:
size_t size() const { return list.size(); }
bool empty() const { return list.empty(); }
bool contains(std::string const &name) const { return map.find(name) != map.end(); }
ItemT &operator[](size_t i) { return list[i]; }
typename decltype(list)::iterator begin() { return list.begin(); }
typename decltype(list)::iterator end() { return list.end(); }
typename decltype(list)::const_iterator begin() const { return list.begin(); }
typename decltype(list)::const_iterator end() const { return list.end(); }
ItemT &add(std::string const &name) {
map[name] = list.size();
return list.emplace_back();
}
ItemT &add(std::string const &name, ItemT &&value) {
map[name] = list.size();
list.emplace_back(std::move(value));
return list.back();
}
ItemT &addAnonymous() {
// Add the new item to the list, but do not update the map
return list.emplace_back();
}
std::optional<size_t> findIndex(std::string const &name) const {
if (auto search = map.find(name); search != map.end()) {
return search->second;
}
return std::nullopt;
}
};
// An iterable of `enum` values in the half-open range [start, stop).
template<typename EnumT>
class EnumSeq {
EnumT _start;
EnumT _stop;
class Iterator {
EnumT _value;
public:
explicit Iterator(EnumT value) : _value(value) {}
Iterator &operator++() {
_value = static_cast<EnumT>(_value + 1);
return *this;
}
EnumT operator*() const { return _value; }
bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
};
public:
explicit EnumSeq(EnumT stop) : _start(static_cast<EnumT>(0)), _stop(stop) {}
explicit EnumSeq(EnumT start, EnumT stop) : _start(start), _stop(stop) {}
Iterator begin() { return Iterator(_start); }
Iterator end() { return Iterator(_stop); }
};
// Only needed inside `ZipContainer` below.
// This is not a fully generic implementation; its current use cases only require for-loop behavior.
// We also assume that all iterators have the same length.
template<typename... IteratorTs>
class ZipIterator {
std::tuple<IteratorTs...> _iters;
public:
explicit ZipIterator(std::tuple<IteratorTs...> &&iters) : _iters(iters) {}
ZipIterator &operator++() {
std::apply([](auto &&...it) { (++it, ...); }, _iters);
return *this;
}
auto operator*() const {
return std::apply(
[](auto &&...it) { return std::tuple<decltype(*it)...>(*it...); }, _iters
);
}
bool operator==(ZipIterator const &rhs) const {
return std::get<0>(_iters) == std::get<0>(rhs._iters);
}
};
// Only needed inside `zip` below.
template<typename... IterableTs>
class ZipContainer {
std::tuple<IterableTs...> _containers;
public:
explicit ZipContainer(IterableTs &&...containers)
: _containers(std::forward<IterableTs>(containers)...) {}
auto begin() {
return ZipIterator(std::apply(
[](auto &&...containers) {
using std::begin;
return std::make_tuple(begin(containers)...);
},
_containers
));
}
auto end() {
return ZipIterator(std::apply(
[](auto &&...containers) {
using std::end;
return std::make_tuple(end(containers)...);
},
_containers
));
}
};
// Only needed inside `zip` below.
// Take ownership of objects and rvalue refs passed to us, but not lvalue refs
template<typename IterableT>
using ZipHolder = std::conditional_t<
std::is_lvalue_reference_v<IterableT>,
IterableT,
std::remove_cv_t<std::remove_reference_t<IterableT>>>;
// Iterates over N containers at once, yielding tuples of N items at a time.
// Does the same number of iterations as the first container's iterator!
template<typename... IterableTs>
static constexpr auto zip(IterableTs &&...containers) {
return ZipContainer<ZipHolder<IterableTs>...>(std::forward<IterableTs>(containers)...);
}
#endif // RGBDS_ITERTOOLS_HPP
|