#ifndef ITER_CHAIN_HPP_ #define ITER_CHAIN_HPP_ #include "internal/iterbase.hpp" #include #include #include #include #include #include #include namespace iter { namespace impl { template class Chained; template class ChainedFromIterable; // rather than a chain function, use a callable object to support // from_iterable class ChainMaker; } } template class iter::impl::Chained { private: friend class ChainMaker; static_assert(std::tuple_size>::value == sizeof...(Is), "tuple size != sizeof Is"); static_assert( are_same>...>::value, "All chained iterables must have iterators that " "dereference to the same type, including cv-qualifiers " "and references."); using IterTupType = iterator_tuple_type; using DerefType = iterator_deref>; using ArrowType = iterator_arrow>; template static DerefType get_and_deref(IterTupType& iters) { return *std::get(iters); } template static ArrowType get_and_arrow(IterTupType& iters) { return apply_arrow(std::get(iters)); } template static void get_and_increment(IterTupType& iters) { ++std::get(iters); } template static bool get_and_check_not_equal( const IterTupType& lhs, const IterTupType& rhs) { return std::get(lhs) != std::get(rhs); } using DerefFunc = DerefType (*)(IterTupType&); using ArrowFunc = ArrowType (*)(IterTupType&); using IncFunc = void (*)(IterTupType&); using NeqFunc = bool (*)(const IterTupType&, const IterTupType&); constexpr static std::array derefers{ {get_and_deref...}}; constexpr static std::array arrowers{ {get_and_arrow...}}; constexpr static std::array incrementers{ {get_and_increment...}}; constexpr static std::array neq_comparers{ {get_and_check_not_equal...}}; using TraitsValue = iterator_traits_deref>; private: Chained(TupType&& t) : tup(std::move(t)) {} TupType tup; public: Chained(Chained&&) = default; class Iterator : public std::iterator { private: std::size_t index; IterTupType iters; IterTupType ends; void check_for_end_and_adjust() { while (this->index < sizeof...(Is) && !(neq_comparers[this->index](this->iters, this->ends))) { ++this->index; } } public: Iterator(std::size_t i, IterTupType&& in_iters, IterTupType&& in_ends) : index{i}, iters(in_iters), ends(in_ends) { this->check_for_end_and_adjust(); } decltype(auto) operator*() { return derefers[this->index](this->iters); } decltype(auto) operator -> () { return arrowers[this->index](this->iters); } Iterator& operator++() { incrementers[this->index](this->iters); this->check_for_end_and_adjust(); return *this; } Iterator operator++(int) { auto ret = *this; ++*this; return ret; } bool operator!=(const Iterator& other) const { return this->index != other.index || (this->index != sizeof...(Is) && neq_comparers[this->index](this->iters, other.iters)); } bool operator==(const Iterator& other) const { return !(*this != other); } }; Iterator begin() { return {0, IterTupType{std::begin(std::get(this->tup))...}, IterTupType{std::end(std::get(this->tup))...}}; } Iterator end() { return {sizeof...(Is), IterTupType{std::end(std::get(this->tup))...}, IterTupType{std::end(std::get(this->tup))...}}; } }; template constexpr std::array::DerefFunc, sizeof...(Is)> iter::impl::Chained::derefers; template constexpr std::array::ArrowFunc, sizeof...(Is)> iter::impl::Chained::arrowers; template constexpr std::array::IncFunc, sizeof...(Is)> iter::impl::Chained::incrementers; template constexpr std::array::NeqFunc, sizeof...(Is)> iter::impl::Chained::neq_comparers; template class iter::impl::ChainedFromIterable { private: Container container; friend class ChainMaker; ChainedFromIterable(Container&& in_container) : container(std::forward(in_container)) {} public: ChainedFromIterable(ChainedFromIterable&&) = default; class Iterator : public std::iterator>> { private: using SubContainer = iterator_deref; using SubIter = iterator_type; iterator_type top_level_iter; iterator_type top_level_end; std::unique_ptr sub_iter_p; std::unique_ptr sub_end_p; static std::unique_ptr clone_sub_pointer(const SubIter* sub_iter) { return sub_iter ? std::make_unique(*sub_iter) : nullptr; } bool sub_iters_differ(const Iterator& other) const { if (this->sub_iter_p == other.sub_iter_p) { return false; } if (this->sub_iter_p == nullptr || other.sub_iter_p == nullptr) { // since the first check tests if they're the same, // this will return if only one is nullptr return true; } return *this->sub_iter_p != *other.sub_iter_p; } public: Iterator( iterator_type&& top_iter, iterator_type&& top_end) : top_level_iter{std::move(top_iter)}, top_level_end{std::move(top_end)}, sub_iter_p{!(top_iter != top_end) ? // iter == end ? nullptr : std::make_unique(std::begin(*top_iter))}, sub_end_p{!(top_iter != top_end) ? // iter == end ? nullptr : std::make_unique(std::end(*top_iter))} {} Iterator(const Iterator& other) : top_level_iter{other.top_level_iter}, top_level_end{other.top_level_end}, sub_iter_p{clone_sub_pointer(other.sub_iter_p.get())}, sub_end_p{clone_sub_pointer(other.sub_end_p.get())} {} Iterator& operator=(const Iterator& other) { if (this == &other) { return *this; } this->top_level_iter = other.top_level_iter; this->top_level_end = other.top_level_end; this->sub_iter_p = clone_sub_pointer(other.sub_iter_p.get()); this->sub_end_p = clone_sub_pointer(other.sub_end_p.get()); return *this; } Iterator(Iterator&&) = default; Iterator& operator=(Iterator&&) = default; ~Iterator() = default; Iterator& operator++() { ++*this->sub_iter_p; if (!(*this->sub_iter_p != *this->sub_end_p)) { ++this->top_level_iter; if (this->top_level_iter != this->top_level_end) { sub_iter_p = std::make_unique(std::begin(*this->top_level_iter)); sub_end_p = std::make_unique(std::end(*this->top_level_iter)); } else { sub_iter_p.reset(); sub_end_p.reset(); } } return *this; } Iterator operator++(int) { auto ret = *this; ++*this; return ret; } bool operator!=(const Iterator& other) const { return this->top_level_iter != other.top_level_iter || this->sub_iters_differ(other); } bool operator==(const Iterator& other) const { return !(*this != other); } iterator_deref> operator*() { return **this->sub_iter_p; } iterator_arrow> operator->() { return apply_arrow(*this->sub_iter_p); } }; Iterator begin() { return {std::begin(this->container), std::end(this->container)}; } Iterator end() { return {std::end(this->container), std::end(this->container)}; } }; class iter::impl::ChainMaker { private: template Chained chain_impl( TupleType&& in_containers, std::index_sequence) const { return {std::move(in_containers)}; } public: // expose regular call operator to provide usual chain() template auto operator()(Containers&&... cs) const { return this->chain_impl( std::tuple{std::forward(cs)...}, std::index_sequence_for{}); } // chain.from_iterable template ChainedFromIterable from_iterable(Container&& container) const { return {std::forward(container)}; } }; namespace iter { namespace { constexpr auto chain = iter::impl::ChainMaker{}; } } #endif