// // recursive_iterator.h // iterator // // Created by Sam Jaffe on 2/17/17. // #pragma once #include #include #include #include #include #include #include #include namespace iterator::recursive { struct unbounded { template static constexpr recursion_type const value = iter_typeclass; using next = unbounded; static constexpr size_t size = std::numeric_limits::max(); }; template struct bounded { template static constexpr recursion_type const value = I == N ? recursion_type::END : iter_typeclass; using next = std::conditional_t>; static constexpr size_t size = N; }; template > struct tuple_expander; template struct tuple_expander { using iter = std::tuple>; decltype(auto) get(It iter) const { if constexpr (associative_value>) { return std::tie(iter->first, iter->second); } else { return std::tie(*iter); } } }; template using tuple_cat_t = decltype(std::tuple_cat(VAL(Ts)...)); template struct tuple_expander { using next = decltype(std::begin(*VAL(It))); using iter = tuple_cat_t>, typename tuple_expander::iter>; auto get(It) const { return std::make_tuple(); } }; template struct tuple_expander { using next = decltype(std::begin(VAL(It)->second)); using iter = tuple_cat_t>, typename tuple_expander::iter>; auto get(It iter) const { return std::tie(iter->first); }; }; /** * @brief An iterator type for nested collections, allowing you to treat it as * a single-layer collection. * * In order to provide a simple interface, if an associative container is used * in the chain, the type returned by operator*() is a tuple. If multiple * associative containers are nested, then the tuple will be of the form * std::tuple. To avoid copies, and allow * editting of underlying values, the tuple contains references. * * @tparam It The iterator type of the top-level collection. * @tparam Bnd The bounding type, representing how many layers this iterator * is willing to delve in the parent object. */ template class rimpl : public facade> { public: using sentinel_type = sentinel_t; using iters_t = typename tuple_expander::iter; static constexpr size_t n_iters = std::tuple_size_v; static constexpr size_t size = std::min(n_iters, Bnd::size); private: iters_t impl_; public: rimpl() = default; rimpl(end_aware_iterator iter) { assign<0>(iter); } template rimpl(end_aware_iterator other) : rimpl(end_aware_iterator(other)) {} template rimpl(rimpl other) : rimpl(end_aware_iterator(other)) {} template operator end_aware_iterator() const { return std::get>(impl_); } decltype(auto) dereference() const { // Special Case Handling for circumstances where at least everything up to // the deepest nested container is non-associative. In this case, we don't // want to transmute our single element/association into a tuple, since // there's no benefit from that. if constexpr (std::tuple_size_vbuild_tuple())> == 1) { return *std::get(impl_); } else { return build_tuple(); } } void increment() { increment_i(); } bool at_end() const { return std::get<0>(impl_).at_end(); } bool equal_to(rimpl const & other) const { return impl_ == other.impl_; } // Used by std::get, don't use elsewhere... auto const & impl() const { return impl_; } private: template bool increment_i() { auto & iter = std::get(impl_); if (iter.at_end()) { return false; } // Make sure we don't go OOB ++iter; if constexpr (I > 0) { while (iter.at_end() && increment_i()) { assign(*std::get(impl_)); } } return !iter.at_end(); } template decltype(auto) get() const { auto it = std::get(impl_); // In the case of a bounded recursive iterator, I need to ensure that the // effectively terminal iterator is treated as such even if there is still // iteration to be had. if constexpr (I == size - 1) { return tuple_expander{}.get( it); } else { return tuple_expander{}.get(it); } } template decltype(auto) build_tuple(std::index_sequence) const { return std::tuple_cat(get()...); } decltype(auto) build_tuple() const { return build_tuple(std::make_index_sequence()); } template void assign(end_aware_iterator it) { std::get(impl_) = it; if constexpr (I < size - 1) { if (!it.at_end()) { assign(*it); } } } template void assign(C && collection) { assign(end_aware_iterator(std::forward(collection))); } template void assign(std::pair const & pair) { assign(pair.second); } template void assign(std::pair & pair) { assign(pair.second); } }; } namespace std { template auto get(::iterator::recursive_iterator const & iter) { return ::std::get(iter.impl()); } template auto get(::iterator::recursive_iterator_n const & iter) { static_assert(I < N, "Cannot get past bounding level"); return ::std::get(iter.impl()); } } template auto make_recursive_iterator(C && collect) { return iterator::recursive_iterator>(collect); } template auto make_recursive_iterator(C && collect) { return iterator::recursive_iterator_n, Max>(collect); } namespace iterator::views { template struct recursive_n_fn : std::ranges::range_adaptor_closure> { template auto operator()(Rng && rng) const { auto begin = make_recursive_iterator(std::views::all(std::forward(rng))); return std::ranges::subrange(begin, decltype(begin)()); } }; struct recursive_fn : std::ranges::range_adaptor_closure { template auto operator()(Rng && rng) const { auto begin = make_recursive_iterator(std::views::all(std::forward(rng))); return std::ranges::subrange(begin, decltype(begin)()); } }; template constexpr recursive_n_fn recursive_n{}; constexpr recursive_fn recursive; } #include