// // recursive_iterator.hpp // iterator // // Created by Sam Jaffe on 2/17/17. // #pragma once #include "iterator_fwd.hpp" #include #include #include #include "detail/recursive_traits.h" #include "end_aware_iterator.hpp" #include "facade.h" namespace iterator::recursive { template struct bounded { template static constexpr recursion_type const value = I == N ? recursion_type::END : typeclass_t::value; using next = std::conditional_t>; static constexpr size_t size = N; }; struct unbounded { template static constexpr recursion_type const value = typeclass_t::value; using next = unbounded; static constexpr size_t size = std::numeric_limits::max(); }; template > struct tuple; template struct tuple { using iter = std::tuple>; decltype(auto) get(It iter) const { if constexpr (is_associative{}) { return std::tie(iter->first, iter->second); } else { return std::tie(*iter); } } }; template using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); template struct tuple { using next = decltype(std::begin(*std::declval())); using iter = tuple_cat_t>, typename tuple::iter>; auto get(It) const { return std::make_tuple(); } }; template struct tuple { using next = decltype(std::begin(std::declval()->second)); using iter = tuple_cat_t>, typename tuple::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> { private: typename tuple::iter impl_; public: static constexpr size_t size = std::min(std::tuple_size_v::iter>, Bnd::size); 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(); } } template bool increment() { 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()) { assign(*std::get(impl_)); } } return !iter.at_end(); } 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 decltype(auto) build_tuple() const { // 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. auto it = std::get(impl_); if constexpr (I == size - 1) { return tuple{}.get(it); } else { // Implemented as a recursive function instead of a parameter-pack // because OSX has a compiler bug regarding certain forms of parameter // packs... return std::tuple_cat(tuple{}.get(it), build_tuple()); } } 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(make_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); } }; } MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::recursive::rimpl); 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) -> iterator::recursive_iterator { return iterator::recursive_iterator{ make_end_aware_iterator(collect)}; } template auto make_recursive_iterator(C const & collect) -> iterator::recursive_iterator { return iterator::recursive_iterator{ make_end_aware_iterator(collect)}; } template auto make_recursive_iterator(C & collect) -> iterator::recursive_iterator_n { return iterator::recursive_iterator_n{ make_end_aware_iterator(collect)}; } template auto make_recursive_iterator(C const & collect) -> iterator::recursive_iterator_n { return iterator::recursive_iterator_n{ make_end_aware_iterator(collect)}; }