/* SPDX-License-Identifier: MIT */ #ifndef RGBDS_ITERTOOLS_HPP #define RGBDS_ITERTOOLS_HPP #include #include #include "platform.h" // __PRETTY_FUNCTION__ template static inline void report() { puts(__PRETTY_FUNCTION__); } // 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: 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>>; } // 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