// // recursive_iterator.hpp // iterator // // Created by Sam Jaffe on 2/17/17. // #pragma once #include "iterator_fwd.hpp" #include #include #include #include #include "detail/traits.h" #include "end_aware_iterator.hpp" #include "facade.h" namespace iterator::recursive { // Helpers for condensing type deductions template using value = decltype(std::begin(*std::declval())); template using mapped = decltype(std::begin(std::declval()->second)); // Type trait to identify value_type ~~ std::pair, which is // a safe bet towards 'this is an associative container type' template struct is_associative : std::false_type {}; template struct is_associative::value>> : std::true_type {}; // Type deduction guides for constructing recursive iterators. enum class typeclass { TERMINAL, CONTAINER, ASSOCIATIVE_CONTAINER }; template struct typeclass_t { static constexpr typeclass const value{typeclass::TERMINAL}; }; template struct is_string_iterator : std::false_type {}; template <> struct is_string_iterator : std::true_type {}; template <> struct is_string_iterator : std::true_type {}; template struct typeclass_t>::value>> { static constexpr typeclass const value{typeclass::CONTAINER}; }; template struct typeclass_t>::value>> { static constexpr typeclass const value{typeclass::TERMINAL}; }; template struct typeclass_t>> { static constexpr typeclass const value{typeclass::ASSOCIATIVE_CONTAINER}; }; } namespace iterator::recursive { template struct bounded { template static constexpr typeclass const value = I == N ? typeclass::TERMINAL : typeclass_t::value; using next = std::conditional_t>; static constexpr size_t size = N; }; struct unbounded { template static constexpr typeclass 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); }; }; 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 iterator { /** * @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. */ template using recursive_iterator = recursive::rimpl; /** * @copydoc recursive_iterator * This object has bounded recursive depth, so that it can be used to get * sub-collections, which may be used in other functions. * * For Example: * @code * using map_type = std::map > >; * ... * recursive_iterator_n iter{ ... }; * std::vector & data = std::get<2>(*iter); * reload_data_from_file( std::get<1>(*iter), data ); * @endcode * * @tparam N The maximum depth to recurse into the object */ template using recursive_iterator_n = 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)}; }