/* SPDX-License-Identifier: MIT */ #ifndef RGBDS_ITERTOOLS_HPP #define RGBDS_ITERTOOLS_HPP #include #include template class EnumSeqIterator { T _value; public: explicit EnumSeqIterator(T value) : _value(value) {} EnumSeqIterator &operator++() { _value = (T)(_value + 1); return *this; } auto operator*() const { return _value; } friend auto operator==(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) { return lhs._value == rhs._value; } friend auto operator!=(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) { return lhs._value != rhs._value; } }; template class EnumSeq { T _start; T _stop; public: explicit EnumSeq(T stop) : _start((T)0), _stop(stop) {} explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {} EnumSeqIterator begin() { return EnumSeqIterator(_start); } EnumSeqIterator end() { return EnumSeqIterator(_stop); } }; // 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 class Zip { std::tuple _iters; public: explicit Zip(std::tuple &&iters) : _iters(iters) {} Zip &operator++() { std::apply([](auto &&...it) { (++it, ...); }, _iters); return *this; } auto operator*() const { return std::apply( [](auto &&...it) { return std::tuple(*it...); }, _iters ); } friend auto operator==(Zip const &lhs, Zip const &rhs) { return std::get<0>(lhs._iters) == std::get<0>(rhs._iters); } friend auto operator!=(Zip const &lhs, Zip const &rhs) { return std::get<0>(lhs._iters) != std::get<0>(rhs._iters); } }; namespace detail { template class ZipContainer { std::tuple _containers; public: explicit ZipContainer(Containers &&...containers) : _containers(std::forward(containers)...) {} auto begin() { return Zip(std::apply( [](auto &&...containers) { using std::begin; return std::make_tuple(begin(containers)...); }, _containers )); } auto end() { return Zip(std::apply( [](auto &&...containers) { using std::end; return std::make_tuple(end(containers)...); }, _containers )); } }; // Take ownership of objects and rvalue refs passed to us, but not lvalue refs template using Holder = std:: conditional_t, T, std::remove_cv_t>>; } // namespace detail // Does the same number of iterations as the first container's iterator! template static constexpr auto zip(Containers &&...cs) { return detail::ZipContainer...>(std::forward(cs)...); } #endif // RGBDS_ITERTOOLS_HPP