Selaa lähdekoodia

Merge branch 'proxy'

* proxy:
  Convert zip_iterator to use proxy.
  Fix some type issues w/ facade and proxy.
  Expand iterator_fwd.hpp
  Move traits elsewhere.
  Use a tuple as the basis of recursive_iterator.
  Deduplicate impl.
  More removal of redundant things.
  Segregate and shrink names for recursives.
  Use if constexpr for recursive_iterator_base handling to improve readability.
  Compress get() without weakening its reference semantics.
  Strip most of the iterator typedefs off of the various intermediate layers of recursive_iterator.
  Alter recursive iterator w.r.t. new facade code.
  Make more things facades.
  Add a proxy helper for iterators that implement only small QoL changes to a wrapped iterator type.
  Upgrade unkeyed_iterator to use facade.
  Add a C++17 version of iterator_facade using type deduction. TODO: Testing.
Sam Jaffe 4 vuotta sitten
vanhempi
commit
cd060637de

+ 3 - 0
include/iterator/detail/arrow_proxy.h

@@ -5,7 +5,10 @@
 
 namespace iterator { namespace detail {
   template <typename Reference> struct arrow_proxy {
+    arrow_proxy(Reference r) : r(std::move(r)) {}
     Reference r;
     Reference * operator->() { return std::addressof(r); }
   };
+
+  template <typename R> arrow_proxy(R r) -> arrow_proxy<R>;
 }}

+ 0 - 107
include/iterator/detail/flatten_iterator_layer.hpp

@@ -1,107 +0,0 @@
-#pragma once
-
-#include "recursive_iterator_base.hpp"
-#include "recursive_iterator_traits.hpp"
-
-namespace iterator { namespace detail {
-
-  /**
-   * @class flatten_iterator_layer
-   * @brief A single layer for recursing down a nested collection. Represents
-   * associative containers.
-   *
-   * @copydoc recursive_iterator_layer
-   */
-  template <typename Iterator, typename RecursiveIterator_NextLayer>
-  class flatten_iterator_layer : public recursive_iterator_base<Iterator>,
-                                 public RecursiveIterator_NextLayer {
-  public:
-    using super = RecursiveIterator_NextLayer;
-    using layer = recursive_iterator_base<Iterator>;
-    using key_type =
-        typename std::tuple_element<0, typename layer::value_type>::type;
-
-  protected:
-    using recursive_category = continue_layer_tag_t;
-    using next_value_type =
-        typename next_layer_type<typename super::value_type,
-                                 typename super::recursive_category>::type;
-    using next_reference =
-        typename next_layer_type<typename super::reference,
-                                 typename super::recursive_category>::type;
-
-  public:
-    using value_type =
-        decltype(std::tuple_cat(std::make_tuple(std::declval<key_type>()),
-                                std::declval<next_value_type>()));
-    using reference = decltype(std::tuple_cat(
-        std::tie(std::declval<key_type>()), std::declval<next_reference>()));
-    using pointer = void;
-    using difference_type = typename super::difference_type;
-    using iterator_category = std::forward_iterator_tag;
-
-  public:
-    flatten_iterator_layer() = default;
-    flatten_iterator_layer(layer v) : flatten_iterator_layer() {
-      assign(v);
-      update();
-    }
-    template <typename OIter, typename Rec>
-    flatten_iterator_layer(flatten_iterator_layer<OIter, Rec> const & other)
-        : layer(static_cast<recursive_iterator_base<OIter> const &>(other)),
-          super(static_cast<Rec const &>(other)) {}
-
-    /**
-     * @brief Concatenate the key in this layer, with the dereferenced data from
-     * the next.
-     *
-     * Due to the use of the next_layer_type metaprogramming, a type such as
-     * std::map<K, std::vector<std::tuple<T1, T2, T3>>> would return a reference
-     * of type std::tuple<K const &, std::tuple<T1, T2, T3>&>, preserving
-     * sub-aggregates of pair/tuple type. Similarly, forward_as_tuple means
-     * even a key-type of pair/tuple will not be unwrapped.
-     */
-    reference operator*() const {
-      return std::tuple_cat(std::forward_as_tuple(std::get<0>(layer::get())),
-                            next_reference(super::get()));
-    }
-
-    /**
-     * Unimplemented because we return an inline constructed type, and tuple
-     * can only be accessed through std::get anyway.
-     */
-    pointer operator->() const;
-
-    bool operator==(flatten_iterator_layer const & other) const {
-      return layer::operator==(other) && super::operator==(other);
-    }
-
-  protected:
-    reference get() const { return operator*(); }
-
-    /**
-     * @copydoc recursive_iterator_layer::next
-     */
-    void next() {
-      super::next();
-      update();
-    }
-
-    void update() {
-      layer & self = static_cast<layer &>(*this);
-      while (super::done() && !(++self).done()) {
-        super::assign(make_end_aware_iterator(self->second));
-      }
-    }
-
-    /**
-     * @copydoc recursive_iterator_layer::assign
-     */
-    void assign(layer v) {
-      static_cast<layer &>(*this) = v;
-      if (!v.done()) { super::assign(make_end_aware_iterator(v->second)); }
-    }
-
-    bool done() const { return layer::done(); }
-  };
-}}

+ 0 - 81
include/iterator/detail/recursive_iterator_base.hpp

@@ -1,81 +0,0 @@
-#pragma once
-
-#include "../end_aware_iterator.hpp"
-#include "recursive_iterator_traits.hpp"
-
-namespace iterator { namespace detail {
-
-  template <typename Iterator, typename = void> class recursive_iterator_base;
-
-  /**
-   * @class recursive_iterator_base
-   * @brief A thin wrapper around end_aware_iterator for the purposes of
-   * template metaprogramming.
-   */
-  template <typename Iterator, typename>
-  class recursive_iterator_base : public end_aware_iterator<Iterator> {
-  public:
-    using super = end_aware_iterator<Iterator>;
-
-  protected:
-    using recursive_category = terminal_layer_tag_t;
-
-  public:
-    using super::super;
-    recursive_iterator_base(super const & iter) : super(iter) {}
-    recursive_iterator_base(super && iter) : super(std::move(iter)) {}
-    recursive_iterator_base() = default;
-
-  protected:
-    typename super::reference get() const { return super::operator*(); }
-  };
-
-  /**
-   * @class recursive_iterator_base
-   * @brief An SFINAE specialization of recursive_iterator_base for associative
-   * containers
-   *
-   * Because it is possible for recursive iterator to step over multiple layers
-   * of associative containers, the return type is made into a tuple, so that
-   * the caller does not need to write something like
-   * `it->second.second.second'. Instead, the return type is a tuple of
-   * references, so that the caller can write code like `std::get<3>(*it)'.
-   *
-   * For example, the ref type for std::map<int, std::map<float, double> >
-   * with this would be std::tuple<int const&, float const&, double &>.
-   */
-  template <typename Iterator>
-  class recursive_iterator_base<Iterator, is_associative_t<Iterator>>
-      : public end_aware_iterator<Iterator> {
-  public:
-    using super = end_aware_iterator<Iterator>;
-    using first_type = decltype((std::declval<Iterator>()->first));
-    using second_type = decltype((std::declval<Iterator>()->second));
-
-  protected:
-    using recursive_category = continue_layer_tag_t;
-
-  public:
-    using value_type = std::tuple<first_type, second_type>;
-    using reference = std::tuple<first_type &, second_type &>;
-
-  public:
-    using super::super;
-    recursive_iterator_base(super const & iter) : super(iter) {}
-    recursive_iterator_base(super && iter) : super(std::move(iter)) {}
-    recursive_iterator_base() = default;
-
-  protected:
-    /**
-     * An alternative function to operator*(), which allows single layer
-     * recursive iterators (on associative containers) to return the
-     * underlying value/reference type, and nested containers to propogate
-     * a tuple or a pair as necessary.
-     */
-    reference get() const {
-      auto & pair = super::operator*();
-      return std::tie(pair.first, pair.second);
-    }
-  };
-
-}}

+ 0 - 180
include/iterator/detail/recursive_iterator_impl.hpp

@@ -1,180 +0,0 @@
-//
-//  recursive_iterator_meta.hpp
-//  iterator
-//
-//  Created by Sam Jaffe on 2/21/17.
-//
-
-#pragma once
-
-#include "flatten_iterator_layer.hpp"
-#include "recursive_iterator_base.hpp"
-#include "recursive_iterator_layer.hpp"
-#include "recursive_iterator_traits.hpp"
-
-namespace iterator { namespace detail {
-  template <typename Iterator, typeclass = typeclass_t<Iterator>::value>
-  class recursive_iterator_impl;
-
-  /**
-   * A bounded_recursive_iterator_impl where N == Max is always a terminal node.
-   */
-  template <typename Iterator, std::size_t N, std::size_t Max,
-            typeclass = std::conditional_t<N == Max, typeclass_t<void>,
-                                           typeclass_t<Iterator>>::value>
-  class bounded_recursive_iterator_impl;
-
-  /**
-   * @class recursive_iterator_impl
-   * @brief The default (terminal) implementation of an unbounded recursive
-   * iterator.
-   *
-   * @see recursive_iterator_base
-   * @tparam Iterator The iterator type being processed, such as
-   * std::vector<int>::iterator
-   */
-  template <typename Iterator>
-  class recursive_iterator_impl<Iterator, typeclass::TERMINAL>
-      : public recursive_iterator_base<Iterator> {
-  public:
-    using super = recursive_iterator_base<Iterator>;
-
-  public:
-    using super::super;
-    recursive_iterator_impl() = default;
-
-  protected:
-    void next() { super::operator++(); }
-    void assign(super eat) { static_cast<super &>(*this) = eat; }
-  };
-
-  /**
-   * @class recursive_iterator_impl
-   *
-   * An SFINAE specialization of bounded_recursive_iterator_impl for
-   * non-associative container types.
-   * @see recursive_iterator_layer
-   */
-  template <typename Iterator>
-  class recursive_iterator_impl<Iterator, typeclass::CONTAINER>
-      : public recursive_iterator_layer<
-            Iterator, recursive_iterator_impl<value_iterator<Iterator>>> {
-  public:
-    using next_layer = recursive_iterator_impl<value_iterator<Iterator>>;
-    using super = recursive_iterator_layer<Iterator, next_layer>;
-
-  public:
-    /**
-     * A special override of operator* that allows collections like
-     * std::vector<std::vector<std::map<K, V>>> still use the value/reference
-     * type of the map. Works only for nested collections with one associative
-     * container at the bottom level.
-     */
-    auto operator*() const -> decltype(*(next_layer &)(*this)) {
-      return next_layer::operator*();
-    }
-    using super::super;
-    recursive_iterator_impl() = default;
-  };
-
-  /**
-   * @class recursive_iterator_impl
-   *
-   * An SFINAE specialization of bounded_recursive_iterator_impl for
-   * associative container types.
-   * @see flatten_iterator_layer
-   */
-  template <typename Iterator>
-  class recursive_iterator_impl<Iterator, typeclass::ASSOCIATIVE_CONTAINER>
-      : public flatten_iterator_layer<
-            Iterator, recursive_iterator_impl<mapped_iterator<Iterator>>> {
-  public:
-    using next_layer = recursive_iterator_impl<mapped_iterator<Iterator>>;
-    using super = flatten_iterator_layer<Iterator, next_layer>;
-
-  public:
-    using super::super;
-    recursive_iterator_impl() = default;
-  };
-
-  /**
-   * @class bounded_recursive_iterator_impl
-   * @brief The default (terminal) implementation of a recursive iterator up to
-   * Max levels deep.
-   *
-   * @see recursive_iterator_base
-   * @tparam Iterator The iterator type being processed, such as
-   * std::vector<int>::iterator
-   * @tparam N The current layer of depth, starts at 1.
-   * @tparam Max The maximum recursive depth to dive down, in case you need to
-   * process some sub-collection in a specific manner.
-   */
-  template <typename Iterator, std::size_t N, std::size_t Max>
-  class bounded_recursive_iterator_impl<Iterator, N, Max, typeclass::TERMINAL>
-      : public recursive_iterator_base<Iterator> {
-  public:
-    using super = recursive_iterator_base<Iterator>;
-
-  public:
-    using super::super;
-    bounded_recursive_iterator_impl() = default;
-
-  protected:
-    void next() { super::operator++(); }
-    void assign(super eat) { static_cast<super &>(*this) = eat; }
-  };
-
-  /**
-   * @class bounded_recursive_iterator_impl
-   *
-   * An SFINAE specialization of bounded_recursive_iterator_impl for
-   * non-associative container types.
-   * @see recursive_iterator_layer
-   */
-  template <typename Iterator, std::size_t N, std::size_t Max>
-  class bounded_recursive_iterator_impl<Iterator, N, Max, typeclass::CONTAINER>
-      : public recursive_iterator_layer<
-            Iterator, bounded_recursive_iterator_impl<value_iterator<Iterator>,
-                                                      N + 1, Max>> {
-  public:
-    using next_layer =
-        bounded_recursive_iterator_impl<value_iterator<Iterator>, N + 1, Max>;
-    using super = recursive_iterator_layer<Iterator, next_layer>;
-
-  public:
-    /**
-     * A special override of operator* that allows collections like
-     * std::vector<std::vector<std::map<K, V>>> still use the value/reference
-     * type of the map. Works only for nested collections with one associative
-     * container at the bottom/Max level.
-     */
-    auto operator*() const -> decltype(*(next_layer &)(*this)) {
-      return next_layer::operator*();
-    }
-    using super::super;
-    bounded_recursive_iterator_impl() = default;
-  };
-
-  /**
-   * @class bounded_recursive_iterator_impl
-   *
-   * An SFINAE specialization of bounded_recursive_iterator_impl for
-   * associative container types.
-   * @see flatten_iterator_layer
-   */
-  template <typename Iterator, std::size_t N, std::size_t Max>
-  class bounded_recursive_iterator_impl<Iterator, N, Max,
-                                        typeclass::ASSOCIATIVE_CONTAINER>
-      : public flatten_iterator_layer<
-            Iterator, bounded_recursive_iterator_impl<mapped_iterator<Iterator>,
-                                                      N + 1, Max>> {
-  public:
-    using next_layer =
-        bounded_recursive_iterator_impl<mapped_iterator<Iterator>, N + 1, Max>;
-    using super = flatten_iterator_layer<Iterator, next_layer>;
-
-  public:
-    using super::super;
-    bounded_recursive_iterator_impl() = default;
-  };
-}}

+ 0 - 89
include/iterator/detail/recursive_iterator_layer.hpp

@@ -1,89 +0,0 @@
-#pragma once
-
-#include "recursive_iterator_base.hpp"
-#include "recursive_iterator_traits.hpp"
-
-namespace iterator { namespace detail {
-  /**
-   * @class recursive_iterator_layer
-   * @brief A single layer for recursing down a nested collection. Represents
-   * non-associative containers.
-   *
-   * Provides dispatch/overloading for types and functions of recursive_iterator
-   * chains to resolve ambiguous typedefs and operators.
-   *
-   * @see recursive_iterator_impl
-   * @see bounded_recursive_iterator_impl
-   * @tparam Iterator The underlying iterator type of the layer
-   * @tparam RecursiveIterator_NextLayer The next layer, either a
-   * recursive_iterator_impl, or a bounded_recursive_iterator_impl
-   */
-  template <typename Iterator, typename RecursiveIterator_NextLayer>
-  class recursive_iterator_layer : public recursive_iterator_base<Iterator>,
-                                   public RecursiveIterator_NextLayer {
-  public:
-    using super = RecursiveIterator_NextLayer;
-    using layer = recursive_iterator_base<Iterator>;
-
-  protected:
-    using recursive_category = continue_layer_tag_t;
-
-  public:
-    using value_type = typename super::value_type;
-    using reference = typename super::reference;
-    using pointer = typename super::pointer;
-    using difference_type = typename super::difference_type;
-    using iterator_category = std::forward_iterator_tag;
-
-  public:
-    recursive_iterator_layer() = default;
-    recursive_iterator_layer(layer v) : recursive_iterator_layer() {
-      assign(v);
-      update();
-    }
-    template <typename OIter, typename Rec>
-    recursive_iterator_layer(recursive_iterator_layer<OIter, Rec> const & other)
-        : layer(static_cast<recursive_iterator_base<OIter> const &>(other)),
-          super(static_cast<Rec const &>(other)) {}
-
-    reference operator*() const { return super::get(); }
-
-    pointer operator->() const { return super::operator->(); }
-
-    bool operator==(recursive_iterator_layer const & other) const {
-      return layer::operator==(other) && super::operator==(other);
-    }
-
-  protected:
-    reference get() const { return operator*(); }
-
-    /**
-     * Advance the iterator step. If the next layer has reached the end, then
-     * we advance this iterator until it reaches either its own end, or a
-     * non-empty subcollection to start iterating over.
-     */
-    void next() {
-      super::next();
-      update();
-    }
-
-    /**
-     * Update the underlying iterator and propogate updates down the chain so
-     * that if there is data available, the iterator is in a dereferencable
-     * state.
-     */
-    void assign(layer v) {
-      static_cast<layer &>(*this) = v;
-      if (!v.done()) { super::assign(make_end_aware_iterator(*v)); }
-    }
-
-    void update() {
-      layer & self = static_cast<layer &>(*this);
-      while (super::done() && !(++self).done()) {
-        super::assign(make_end_aware_iterator(*self));
-      }
-    }
-
-    bool done() const { return layer::done(); }
-  };
-}}

+ 0 - 120
include/iterator/detail/recursive_iterator_traits.hpp

@@ -1,120 +0,0 @@
-#pragma once
-
-#include <iterator>
-#include <string>
-#include <tuple>
-#include <utility>
-
-#include "../iterator_fwd.hpp"
-
-namespace iterator { namespace detail {
-  template <typename Iterator, typename RecursiveIterator_NextLayer>
-  class flatten_iterator_layer;
-  template <typename Iterator, typename RecursiveIterator_NextLayer>
-  class recursive_iterator_layer;
-
-  // Helper struct for dealing with merging output from associative and
-  // non-associative containers.
-  // TODO: This seems unnecessarily verbose t.b.h.
-  struct terminal_layer_tag_t;
-  struct continue_layer_tag_t;
-
-  template <typename V, typename Tag> struct next_layer_type {
-    using type = std::tuple<V>;
-  };
-
-  template <typename V> struct next_layer_type<V, continue_layer_tag_t> {
-    using type = V;
-  };
-
-  // Helpers for condensing type deductions
-  template <typename IterType>
-  using value_iterator = decltype(std::begin(*std::declval<IterType>()));
-
-  template <typename IterType>
-  using mapped_iterator =
-      decltype(std::begin(std::declval<IterType>()->second));
-
-  // Type trait to identify value_type ~~ std::pair<K const, V>, which is
-  // a safe bet towards 'this is an associative container type'
-  template <typename T, typename = void>
-  struct is_associative : std::false_type {};
-  template <typename T>
-  struct is_associative<T, std::enable_if_t<std::is_const<
-                               typename T::value_type::first_type>::value>>
-      : std::true_type {};
-
-  template <typename T>
-  using is_associative_t = std::enable_if_t<is_associative<T>::value>;
-
-  // Type deduction guides for constructing recursive iterators.
-  enum class typeclass { TERMINAL, CONTAINER, ASSOCIATIVE_CONTAINER };
-
-  template <typename> struct void_t { using type = void; };
-
-  template <typename T, typename = void> struct typeclass_t {
-    static constexpr typeclass const value{typeclass::TERMINAL};
-  };
-
-  template <typename T> struct is_string_iterator : std::false_type {};
-  template <>
-  struct is_string_iterator<std::string::iterator> : std::true_type {};
-  template <>
-  struct is_string_iterator<std::string::const_iterator> : std::true_type {};
-
-  template <typename T>
-  struct typeclass_t<
-      T, std::enable_if_t<!is_string_iterator<value_iterator<T>>::value>> {
-    static constexpr typeclass const value{typeclass::CONTAINER};
-  };
-
-  template <typename T>
-  struct typeclass_t<
-      T, std::enable_if_t<is_string_iterator<value_iterator<T>>::value>> {
-    static constexpr typeclass const value{typeclass::TERMINAL};
-  };
-
-  template <typename T>
-  struct typeclass_t<T, typename void_t<mapped_iterator<T>>::type> {
-    static constexpr typeclass const value{typeclass::ASSOCIATIVE_CONTAINER};
-  };
-
-  // Accessor templates for invoking std::get
-  template <std::size_t I, typename It, typename = void> struct accessor;
-
-  template <typename It> struct accessor<0, end_aware_iterator<It>> {
-    using type = end_aware_iterator<It>;
-  };
-
-  template <typename It, typename Rec>
-  struct accessor<0, recursive_iterator_layer<It, Rec>> {
-    using type = end_aware_iterator<It>;
-  };
-
-  template <typename It, typename Rec>
-  struct accessor<0, flatten_iterator_layer<It, Rec>> {
-    using type = end_aware_iterator<It>;
-  };
-
-  template <typename It> struct accessor<0, It> {
-    using type = typename accessor<0, typename It::super>::type;
-  };
-
-  template <std::size_t I, typename It>
-  struct accessor<I, It, typename std::enable_if<I != 0>::type> {
-    using type = typename accessor<I, typename It::super>::type;
-  };
-
-  template <std::size_t I, typename It, typename Rec>
-  struct accessor<I, recursive_iterator_layer<It, Rec>,
-                  typename std::enable_if<I != 0>::type> {
-    using type = typename accessor<I - 1, Rec>::type;
-  };
-
-  template <std::size_t I, typename It, typename Rec>
-  struct accessor<I, flatten_iterator_layer<It, Rec>,
-                  typename std::enable_if<I != 0>::type> {
-    using type = typename accessor<I - 1, Rec>::type;
-  };
-
-}}

+ 52 - 0
include/iterator/detail/recursive_traits.h

@@ -0,0 +1,52 @@
+#pragma once
+
+#include <iterator>
+
+#include "traits.h"
+
+namespace iterator::recursive {
+  // Type deduction guides for constructing recursive iterators.
+  enum class recursion_type { END, THRU, ASSOC };
+
+  // Helpers for condensing type deductions
+  template <typename It>
+  using value = decltype(std::begin(*std::declval<It>()));
+
+  template <typename It>
+  using mapped = decltype(std::begin(std::declval<It>()->second));
+
+  template <typename T> using val_key_t = typename T::value_type::first_type;
+
+  // Type trait to identify value_type ~~ std::pair<K const, V>, which is
+  // a safe bet towards 'this is an associative container type'
+  template <typename T, typename = void>
+  struct is_associative : std::false_type {};
+  template <typename T>
+  struct is_associative<T, std::enable_if_t<std::is_const_v<val_key_t<T>>>>
+      : std::true_type {};
+
+  template <typename T, typename = void> struct typeclass_t {
+    static constexpr recursion_type const value{recursion_type::END};
+  };
+
+  template <typename T> struct is_string_iter : std::false_type {};
+  template <> struct is_string_iter<std::string::iterator> : std::true_type {};
+  template <>
+  struct is_string_iter<std::string::const_iterator> : std::true_type {};
+
+  template <typename T> constexpr bool is_string_iter_v = is_string_iter<T>{};
+
+  template <typename T>
+  struct typeclass_t<T, std::enable_if_t<!is_string_iter_v<value<T>>>> {
+    constexpr static recursion_type value{recursion_type::THRU};
+  };
+
+  template <typename T>
+  struct typeclass_t<T, std::enable_if_t<is_string_iter_v<value<T>>>> {
+    constexpr static recursion_type value{recursion_type::END};
+  };
+
+  template <typename T> struct typeclass_t<T, detail::void_t<mapped<T>>> {
+    constexpr static recursion_type value{recursion_type::ASSOC};
+  };
+}

+ 28 - 0
include/iterator/detail/traits.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#define _val(type) std::declval<type>()
+#define exists(expr) void_t<decltype(expr)>
+
+namespace iterator::detail {
+  template <typename> using void_t = void;
+
+  template <typename T, typename = void> struct reference_helper {
+    using type = decltype(*std::declval<T>());
+  };
+  template <typename T>
+  struct reference_helper<T, void_t<typename T::reference>> {
+    using type = typename T::reference;
+  };
+
+  template <typename T, typename = void> struct value_type_helper {
+    using reference = typename reference_helper<T>::type;
+    using type = std::remove_cv_t<std::remove_reference_t<reference>>;
+  };
+  template <typename T>
+  struct value_type_helper<T, void_t<typename T::value_type>> {
+    using type = typename T::value_type;
+  };
+
+  template <typename T> using value_type = typename value_type_helper<T>::type;
+  template <typename T> using reference = typename reference_helper<T>::type;
+}

+ 23 - 47
include/iterator/end_aware_iterator.hpp

@@ -9,76 +9,52 @@
 
 #include "iterator_fwd.hpp"
 
-#include <iterator>
+#include "facade.h"
 
 namespace iterator {
   /**
    * @class end_aware_iterator
    * @brief An iterator that keeps track of the relative end of the range.
    *
-   * @tparam Iterator The underlying iterator type
+   * @tparam It The underlying iterator type
    */
-  template <typename Iterator> class end_aware_iterator {
+  template <typename It>
+  class end_aware_iterator : public facade<end_aware_iterator<It>> {
   public:
-    using iter_type = Iterator;
-    using value_type = typename std::iterator_traits<iter_type>::value_type;
-    using reference = typename std::iterator_traits<iter_type>::reference;
-    using pointer = typename std::iterator_traits<iter_type>::pointer;
-    using difference_type =
-        typename std::iterator_traits<iter_type>::difference_type;
-    using iterator_category = std::forward_iterator_tag;
+    using value_type = typename It::value_type;
+    struct sentinel_type {};
+    static constexpr sentinel_type sentinel;
 
   public:
     end_aware_iterator() = default;
-    end_aware_iterator(iter_type it, iter_type end) : curr_(it), end_(end) {}
-    end_aware_iterator(iter_type end) : curr_(end), end_(end) {}
+    end_aware_iterator(It it, It end) : curr_(it), end_(end) {}
+    end_aware_iterator(It end) : curr_(end), end_(end) {}
 
     template <typename I>
     end_aware_iterator(end_aware_iterator<I> const & other)
         : curr_(other.current()), end_(other.end()) {}
 
-    end_aware_iterator & operator++() {
-      if (!done()) { ++curr_; }
-      return *this;
+    decltype(auto) dereference() const { return *curr_; }
+    void increment() { ++curr_; }
+    bool at_end() const { return curr_ == end_; }
+    bool equal_to(end_aware_iterator const & other) const {
+      // TODO: Fix this clause
+      return (at_end() && other.at_end()) || curr_ == other.curr_;
     }
 
-    end_aware_iterator operator++(int) {
-      end_aware_iterator tmp{*this};
-      operator++();
-      return tmp;
-    }
-
-    reference operator*() const { return *curr_; }
-    pointer operator->() const { return std::addressof(*curr_); }
-
-    bool done() const { return curr_ == end_; }
-    /**
-     * When comparing iterators that do not point to the same collection/range,
-     * the result is not specified by the standard. Therefore, as a matter of
-     * convenience, even if the underlying data is different, all end-aware
-     * iterators in the done() state are considered equal.
-     *
-     * @see done
-     * @return true if both iterators are done, or if the underlying iterators
-     * are logically equal
-     */
-    bool operator==(end_aware_iterator const & other) const {
-      return (done() && other.done()) ||
-             (curr_ == other.curr_ && end_ == other.end_);
-    }
-
-    bool operator!=(end_aware_iterator const & other) {
-      return !(operator==(other));
-    }
-
-    iter_type current() const { return curr_; }
-    iter_type end() const { return end_; }
+    It current() const { return curr_; }
+    It end() const { return end_; }
 
   private:
-    iter_type curr_, end_;
+    It curr_, end_;
   };
+
+  template <typename It> end_aware_iterator(It) -> end_aware_iterator<It>;
+  template <typename It> end_aware_iterator(It, It) -> end_aware_iterator<It>;
 }
 
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::end_aware_iterator);
+
 template <typename Iter>
 iterator::end_aware_iterator<Iter> make_end_aware_iterator(Iter a, Iter b) {
   return iterator::end_aware_iterator<Iter>(a, b);

+ 263 - 0
include/iterator/facade.h

@@ -0,0 +1,263 @@
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+#include "detail/arrow_proxy.h"
+#include "detail/traits.h"
+
+namespace iterator::detail {
+  template <typename, typename = void> struct has_equal_to : std::false_type {};
+  template <typename T>
+  struct has_equal_to<T, exists(_val(T).equal_to(_val(T)))> : std::true_type {};
+
+  template <typename, typename = void>
+  struct has_distance_to : std::false_type {};
+  template <typename T>
+  struct has_distance_to<T, exists(_val(T).distance_to(_val(T)))>
+      : std::true_type {};
+
+  template <typename, typename = void>
+  struct is_advanceable_iterator : std::false_type {};
+  template <typename T>
+  struct is_advanceable_iterator<T, exists(_val(T).advance({}))>
+      : std::true_type {};
+
+  template <typename, typename = void>
+  struct is_single_pass_iterator : std::false_type {};
+  template <typename T>
+  struct is_single_pass_iterator<T, void_t<typename T::single_pass_iterator>>
+      : std::true_type {};
+
+  template <typename, typename = void>
+  struct has_increment : std::false_type {};
+  template <typename T>
+  struct has_increment<T, exists(_val(T).increment())> : std::true_type {};
+
+  template <typename, typename = void>
+  struct has_decrement : std::false_type {};
+  template <typename T>
+  struct has_decrement<T, exists(_val(T).decrement())> : std::true_type {};
+
+  template <typename, typename = void> struct distance_to {
+    using type = std::ptrdiff_t;
+  };
+
+  template <typename T>
+  struct distance_to<T, std::enable_if_t<has_distance_to<T>{}>> {
+    using type = decltype(_val(T).distance_to(_val(T)));
+  };
+
+  template <typename, typename = void> struct sentinal_type {
+    using type = void;
+  };
+
+  template <typename T>
+  struct sentinal_type<T, void_t<typename T::sentinal_type>> {
+    using type = typename T::sentinal_type;
+  };
+
+  template <typename T> using distance_to_t = typename distance_to<T>::type;
+  template <typename T> using sentinal_type_t = typename sentinal_type<T>::type;
+  template <typename T> constexpr bool has_equal_to_v = has_equal_to<T>{};
+  template <typename T> constexpr bool has_distance_to_v = has_distance_to<T>{};
+  template <typename T> constexpr bool has_increment_v = has_increment<T>{};
+  template <typename T> constexpr bool has_decrement_v = has_decrement<T>{};
+  template <typename T>
+  constexpr bool is_advanceable_iterator_v = is_advanceable_iterator<T>{};
+
+  template <typename T>
+  constexpr bool is_random_access_iterator_v =
+      has_distance_to_v<T> && is_advanceable_iterator_v<T>;
+  template <typename T>
+  constexpr bool is_bidirectional_iterator_v =
+      has_decrement_v<T> && has_increment_v<T> && has_equal_to_v<T>;
+  template <typename T>
+  constexpr bool is_single_pass_iterator_v = is_single_pass_iterator<T>{};
+
+  template <typename T>
+  using iterator_category_t = std::conditional_t<
+      is_random_access_iterator_v<T>, std::random_access_iterator_tag,
+      std::conditional_t<is_bidirectional_iterator_v<T>,
+                         std::bidirectional_iterator_tag,
+                         std::conditional_t<is_single_pass_iterator_v<T>,
+                                            std::input_iterator_tag,
+                                            std::forward_iterator_tag>>>;
+}
+
+#undef _val
+#undef exists
+
+namespace iterator {
+
+  template <typename D, typename T>
+  using difference_type_arg_t =
+      std::enable_if_t<std::is_convertible_v<D, detail::distance_to_t<T>>>;
+
+  template <typename D, typename T>
+  using sentinel_type_arg_t =
+      std::enable_if_t<std::is_same_v<D, detail::sentinal_type_t<T>>>;
+
+  template <typename self_type> class facade {
+  public:
+    decltype(auto) operator*() const { return self().dereference(); }
+
+    decltype(auto) operator->() const {
+      if constexpr (std::is_reference<decltype(**this)>{}) {
+        return std::addressof(**this);
+      } else {
+        return detail::arrow_proxy{**this};
+      }
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    decltype(auto) operator[](D off) const {
+      return *(self() + off);
+    }
+
+    self_type & operator++() {
+      if constexpr (detail::is_advanceable_iterator_v<self_type>) {
+        self() += 1;
+      } else {
+        self().increment();
+      }
+      return self();
+    }
+
+    auto operator++(int) {
+      if constexpr (detail::is_single_pass_iterator_v<self_type>) {
+        ++*this;
+      } else {
+        auto tmp = self();
+        ++*this;
+        return tmp;
+      }
+    }
+
+    self_type & operator--() {
+      if constexpr (detail::is_advanceable_iterator_v<self_type>) {
+        self() -= 1;
+      } else {
+        self().decrement();
+      }
+      return self();
+    }
+
+    self_type operator--(int) {
+      auto tmp = self();
+      --*this;
+      return tmp;
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    friend self_type & operator+=(self_type & self, D off) {
+      static_assert(detail::is_advanceable_iterator_v<self_type>,
+                    "must be advancable");
+      self.advance(off);
+      return self;
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    friend self_type & operator-=(self_type & self, D off) {
+      static_assert(detail::is_advanceable_iterator_v<self_type>,
+                    "must be advancable");
+      self.advance(-off);
+      return self;
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    friend auto operator+(self_type self, D off) {
+      static_assert(detail::is_advanceable_iterator_v<self_type>,
+                    "must be advancable");
+      return self += off;
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    friend auto operator+(D off, self_type self) {
+      static_assert(detail::is_advanceable_iterator_v<self_type>,
+                    "must be advancable");
+      return self += off;
+    }
+
+    template <typename D, typename = difference_type_arg_t<D, self_type>>
+    friend auto operator-(self_type self, D off) {
+      static_assert(detail::is_advanceable_iterator_v<self_type>,
+                    "must be advancable");
+      return self -= off;
+    }
+
+    friend auto operator-(self_type const & left, self_type const & right) {
+      return right.distance_to(left);
+    }
+
+    friend bool operator==(self_type const & left, self_type const & right) {
+      if constexpr (detail::has_distance_to_v<self_type>) {
+        return (left - right) == 0;
+      } else {
+        return left.equal_to(right);
+      }
+    }
+
+    template <typename S, typename = sentinel_type_arg_t<S, self_type>>
+    friend auto operator==(self_type self, S) {
+      return self.at_end();
+    }
+
+    template <typename S, typename = sentinel_type_arg_t<S, self_type>>
+    friend auto operator!=(self_type self, S) {
+      return !self.at_end();
+    }
+
+    friend bool operator!=(self_type const & left, self_type const & right) {
+      return !(left == right);
+    }
+
+    friend bool operator<(self_type const & left, self_type const & right) {
+      return (left - right) < 0;
+    }
+
+    friend bool operator<=(self_type const & left, self_type const & right) {
+      return (left - right) <= 0;
+    }
+
+    friend bool operator>(self_type const & left, self_type const & right) {
+      return (left - right) > 0;
+    }
+
+    friend bool operator>=(self_type const & left, self_type const & right) {
+      return (left - right) >= 0;
+    }
+
+  private:
+    self_type & self() { return *static_cast<self_type *>(this); }
+    self_type const & self() const {
+      return *static_cast<self_type const *>(this);
+    }
+  };
+}
+
+namespace std {
+  // In C++20, a concept/requires could be used to eschew the need for the below
+  // macros.
+  template <typename I> struct iterator_traits<::iterator::facade<I>> {
+    using reference = decltype(*std::declval<I>());
+    using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
+    using pointer = decltype(std::declval<I>().operator->());
+    using difference_type = ::iterator::detail::distance_to_t<I>;
+    using iterator_category = ::iterator::detail::iterator_category_t<I>;
+  };
+}
+
+#define MAKE_ITERATOR_FACADE_TYPEDEFS(type)                                    \
+  namespace std {                                                              \
+    template <>                                                                \
+    struct iterator_traits<type>                                               \
+        : std::iterator_traits<::iterator::facade<type>> {};                   \
+  }
+
+#define MAKE_ITERATOR_FACADE_TYPEDEFS_T(type)                                  \
+  namespace std {                                                              \
+    template <typename... T>                                                   \
+    struct iterator_traits<type<T...>>                                         \
+        : std::iterator_traits<::iterator::facade<type<T...>>> {};             \
+  }

+ 23 - 28
include/iterator/filter_iterator.hpp

@@ -10,53 +10,48 @@
 #include <functional>
 
 #include "end_aware_iterator.hpp"
+#include "facade.h"
 
 namespace iterator {
   template <typename Iter>
-  class filter_iterator : public end_aware_iterator<Iter> {
-  private:
-    using super = end_aware_iterator<Iter>;
-
+  class filter_iterator : public facade<filter_iterator<Iter>> {
   public:
-    using value_type = typename super::value_type;
-    using reference = typename super::reference;
-    using pointer = typename super::pointer;
-    using difference_type = typename super::difference_type;
-    using iterator_category = typename super::iterator_category;
+    using super = end_aware_iterator<Iter>;
+    using reference = decltype(*std::declval<super>());
+    using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
+    using difference_type = std::ptrdiff_t;
+    using iterator_category = std::forward_iterator_tag;
 
   public:
     filter_iterator() = default;
     template <typename... Args>
     filter_iterator(std::function<bool(value_type const &)> && p,
                     Args &&... super_args)
-        : super(std::forward<Args>(super_args)...), pred(std::move(p)) {
-      if (should_advance()) { advance(); }
-    }
-
-    filter_iterator & operator++() {
-      advance();
-      return *this;
-    }
-
-    filter_iterator operator++(int) {
-      filter_iterator tmp{*this};
-      operator++();
-      return tmp;
+        : base(end_aware_iterator(std::forward<Args>(super_args)...)),
+          pred(std::move(p)) {
+      if (should_advance()) { increment(); }
     }
 
-  private:
-    bool should_advance() {
-      return !super::done() && !pred(super::operator*());
-    }
-    void advance() {
+    decltype(auto) dereference() const { return base.dereference(); }
+    void increment() {
       do {
-        super::operator++();
+        base.increment();
       } while (should_advance());
     }
+    bool equal_to(filter_iterator const & other) const {
+      return base == other.base;
+    }
+
+  public:
+    bool should_advance() { return !base.at_end() && !pred(dereference()); }
+
+    end_aware_iterator<Iter> base;
     std::function<bool(value_type const &)> pred;
   };
 }
 
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::filter_iterator);
+
 template <typename Pred, typename Iter>
 iterator::filter_iterator<Iter> make_filter_iterator(Pred && p, Iter it,
                                                      Iter end) {

+ 15 - 4
include/iterator/iterator_fwd.hpp

@@ -9,9 +9,20 @@
 
 #include <cstdlib>
 
+namespace iterator::recursive {
+  struct unbounded;
+  template <size_t N, size_t I = 1> struct bounded;
+  template <typename Iter, typename = unbounded> class rimpl;
+}
+
 namespace iterator {
-  template <typename Iterator> class end_aware_iterator;
-  template <typename MetaIterator> class joining_iterator;
-  template <typename Iterator> class recursive_iterator;
-  template <typename Iterator, std::size_t MaxDepth> class recursive_iterator_n;
+  template <typename Iter> class end_aware_iterator;
+  template <typename Iter> class filter_iterator;
+  template <typename Iter> class joining_iterator;
+  template <typename Iter> class unkeyed_iterator;
+  template <typename... Iters> class zip_iterator;
+
+  template <typename Iter> using recursive_iterator = recursive::rimpl<Iter>;
+  template <typename Iter, std::size_t N>
+  using recursive_iterator_n = recursive::rimpl<Iter, recursive::bounded<N>>;
 }

+ 10 - 21
include/iterator/join_iterator.hpp

@@ -12,8 +12,11 @@
 #include <iterator>
 #include <utility>
 
+#include "facade.h"
+
 namespace iterator {
-  template <typename MetaIterator> class joining_iterator {
+  template <typename MetaIterator>
+  class joining_iterator : public facade<joining_iterator<MetaIterator>> {
   public:
     using join_iter = MetaIterator;
     using joinable_type = typename join_iter::value_type;
@@ -41,42 +44,28 @@ namespace iterator {
       if (update) { update_iterator(); }
     }
 
-    joining_iterator & operator++() {
-      if ((++iterator_).done()) {
+    void increment() {
+      if ((iterator_++).at_end()) {
         ++joiner_;
         update_iterator();
       }
-      return *this;
-    }
-
-    joining_iterator operator++(int) {
-      joining_iterator tmp{*this};
-      operator++();
-      return tmp;
     }
 
-    reference operator*() const { return iterator_.operator*(); }
-    pointer operator->() const { return iterator_.operator->(); }
+    decltype(auto) dereference() const { return *iterator_; }
 
-    bool operator==(joining_iterator const & other) const {
+    bool equal_to(joining_iterator const & other) const {
       return joiner_ == other.joiner_ && iterator_ == other.iterator_;
     }
 
-    bool operator!=(joining_iterator const & other) const {
-      return !(operator==(other));
-    }
-
     end_aware_iterator<join_iter> join_iterator() const { return joiner_; }
     end_aware_iterator<iter_type> element_iterator() const { return iterator_; }
 
   private:
     void update_iterator() {
-      while (!joiner_.done() && std::begin(*joiner_) == std::end(*joiner_)) {
+      while (!joiner_.at_end() && make_end_aware_iterator(*joiner_).at_end()) {
         ++joiner_;
       }
-      if (!joiner_.done()) {
-        iterator_ = {std::begin(*joiner_), std::end(*joiner_)};
-      }
+      if (!joiner_.at_end()) { iterator_ = make_end_aware_iterator(*joiner_); }
     }
 
     end_aware_iterator<join_iter> joiner_;

+ 99 - 0
include/iterator/proxy.h

@@ -0,0 +1,99 @@
+#pragma once
+
+#include <iterator>
+
+#include "facade.h"
+
+namespace iterator {
+  template <typename Iter, typename Self,
+            typename Category =
+                typename std::iterator_traits<Iter>::iterator_category>
+  class proxy;
+
+  template <typename Iter, typename Self>
+  class proxy<Iter, Self, std::input_iterator_tag> : public facade<Self> {
+  public:
+    using single_pass_iterator = void;
+
+  private:
+    Iter impl_;
+
+  public:
+    proxy(Iter impl = {}) : impl_(impl) {}
+    template <typename... Args>
+    proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
+
+    decltype(auto) dereference() const { return *impl_; }
+    void increment() { ++impl_; }
+    bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+
+  protected:
+    auto & impl() const { return impl_; }
+  };
+
+  template <typename Iter, typename Self>
+  class proxy<Iter, Self, std::forward_iterator_tag> : public facade<Self> {
+  private:
+    Iter impl_;
+
+  public:
+    proxy(Iter impl = {}) : impl_(impl) {}
+    template <typename... Args>
+    proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
+
+    decltype(auto) dereference() const { return *impl_; }
+    void increment() { ++impl_; }
+    bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+
+  protected:
+    auto & impl() const { return impl_; }
+  };
+
+  template <typename Iter, typename Self>
+  class proxy<Iter, Self, std::bidirectional_iterator_tag>
+      : public facade<Self> {
+  private:
+    Iter impl_;
+
+  public:
+    proxy(Iter impl = {}) : impl_(impl) {}
+    template <typename... Args>
+    proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
+
+    decltype(auto) dereference() const { return *impl_; }
+    void increment() { ++impl_; }
+    void decrement() { --impl_; }
+    bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+
+  protected:
+    auto & impl() const { return impl_; }
+  };
+
+  template <typename Iter, typename Self>
+  class proxy<Iter, Self, std::random_access_iterator_tag>
+      : public facade<Self> {
+  public:
+    using difference_type =
+        typename std::iterator_traits<Iter>::difference_type;
+
+  private:
+    Iter impl_;
+
+  public:
+    proxy(Iter impl = {}) : impl_(impl) {}
+    template <typename... Args>
+    proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
+
+    decltype(auto) dereference() const { return *impl_; }
+    void advance(difference_type off) { impl_ += off; }
+    // This shouldn't need to be implemented, but for some reason my traits
+    // are not correctly deducing here.
+    bool equal_to(Self const & other) const { return distance_to(other) == 0; }
+    difference_type distance_to(Self const & other) const {
+      return other.impl_ - impl_;
+    }
+
+  protected:
+    auto & impl() const { return impl_; }
+  };
+}

+ 149 - 71
include/iterator/recursive_iterator.hpp

@@ -7,15 +7,68 @@
 
 #pragma once
 
+#include "iterator_fwd.hpp"
+
+#include <string>
 #include <tuple>
+#include <utility>
+
+#include "detail/recursive_traits.h"
+#include "end_aware_iterator.hpp"
+#include "facade.h"
+
+namespace iterator::recursive {
+  template <size_t N, size_t I> struct bounded {
+    template <typename It>
+    static constexpr recursion_type const value =
+        I == N ? recursion_type::END : typeclass_t<It>::value;
+    using next = std::conditional_t<I == N, void, bounded<N, I + 1>>;
+    static constexpr size_t size = N;
+  };
 
-#include "detail/recursive_iterator_impl.hpp"
-#include "detail/recursive_iterator_traits.hpp"
-#include "iterator_fwd.hpp"
+  struct unbounded {
+    template <typename It>
+    static constexpr recursion_type const value = typeclass_t<It>::value;
+    using next = unbounded;
+    static constexpr size_t size = std::numeric_limits<size_t>::max();
+  };
+
+  template <typename It, typename Bnd = unbounded,
+            recursion_type = Bnd::template value<It>>
+  struct tuple;
+
+  template <typename It, typename Bnd>
+  struct tuple<It, Bnd, recursion_type::END> {
+    using iter = std::tuple<end_aware_iterator<It>>;
+    decltype(auto) get(It iter) const {
+      if constexpr (is_associative<It>{}) {
+        return std::tie(iter->first, iter->second);
+      } else {
+        return std::tie(*iter);
+      }
+    }
+  };
+
+  template <typename... Ts>
+  using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));
+
+  template <typename It, typename Bnd>
+  struct tuple<It, Bnd, recursion_type::THRU> {
+    using next = decltype(std::begin(*std::declval<It>()));
+    using iter = tuple_cat_t<std::tuple<end_aware_iterator<It>>,
+                             typename tuple<next, typename Bnd::next>::iter>;
+    auto get(It) const { return std::make_tuple(); }
+  };
+
+  template <typename It, typename Bnd>
+  struct tuple<It, Bnd, recursion_type::ASSOC> {
+    using next = decltype(std::begin(std::declval<It>()->second));
+    using iter = tuple_cat_t<std::tuple<end_aware_iterator<It>>,
+                             typename tuple<next, typename Bnd::next>::iter>;
+    auto get(It iter) const { return std::tie(iter->first); };
+  };
 
-namespace iterator {
   /**
-   * @class recursive_iterator
    * @brief An iterator type for nested collections, allowing you to treat it as
    * a single-layer collection.
    *
@@ -25,115 +78,140 @@ namespace iterator {
    * std::tuple<key1, key2, ..., keyN, value>. To avoid copies, and allow
    * editting of underlying values, the tuple contains references.
    *
-   * @tparam Iterator The iterator type of the top-level collection.
+   * @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 <typename Iterator>
-  class recursive_iterator : public detail::recursive_iterator_impl<Iterator> {
-  public:
-    using super = detail::recursive_iterator_impl<Iterator>;
+  template <typename It, typename Bnd>
+  class rimpl : public facade<rimpl<It, Bnd>> {
+  private:
+    typename tuple<It, Bnd>::iter impl_;
 
   public:
-    using super::super;
-    recursive_iterator() = default;
-
-    recursive_iterator & operator++() {
-      (void)super::next();
-      return *this;
+    static constexpr size_t size =
+        std::min(std::tuple_size_v<typename tuple<It, Bnd>::iter>, Bnd::size);
+
+    rimpl() = default;
+    rimpl(end_aware_iterator<It> iter) { assign<0>(iter); }
+    template <typename Ot>
+    rimpl(end_aware_iterator<Ot> other)
+        : rimpl(end_aware_iterator<It>(other)) {}
+    template <typename Ot>
+    rimpl(rimpl<Ot, Bnd> other) : rimpl(end_aware_iterator<Ot>(other)) {}
+
+    template <typename T> operator end_aware_iterator<T>() const {
+      return std::get<end_aware_iterator<T>>(impl_);
     }
 
-    recursive_iterator operator++(int) {
-      recursive_iterator tmp{*this};
-      (void)super::next();
-      return tmp;
+    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_v<decltype(this->build_tuple())> == 1) {
+        return *std::get<size - 1>(impl_);
+      } else {
+        return build_tuple();
+      }
     }
 
-    bool operator!=(recursive_iterator const & other) const {
-      return !(super::operator==(other));
+    template <size_t I = size - 1> bool increment() {
+      auto & iter = std::get<I>(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 - 1>()) {
+          assign<I>(*std::get<I - 1>(impl_));
+        }
+      }
+      return !iter.at_end();
     }
-  };
 
-  /**
-   * @class recursive_iterator_n
-   * @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<std::string, std::map<std::string,
-   * std::vector<Data> > >;
-   * ...
-   * recursive_iterator_n<map_type::iterator, 2> iter{ ... };
-   * std::vector<Data> & 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 <typename Iterator, std::size_t N>
-  class recursive_iterator_n
-      : public detail::bounded_recursive_iterator_impl<Iterator, 1, N> {
-  public:
-    using super = detail::bounded_recursive_iterator_impl<Iterator, 1, N>;
-
-  public:
-    using super::super;
-    recursive_iterator_n() = default;
+    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 <size_t I = 0> 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<I>(impl_);
+      if constexpr (I == size - 1) {
+        return tuple<decltype(it), unbounded, recursion_type::END>{}.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<decltype(it)>{}.get(it),
+                              build_tuple<I + 1>());
+      }
+    }
 
-    recursive_iterator_n & operator++() {
-      (void)super::next();
-      return *this;
+    template <size_t I, typename C> void assign(end_aware_iterator<C> it) {
+      std::get<I>(impl_) = it;
+      if constexpr (I < size - 1) {
+        if (!it.at_end()) { assign<I + 1>(*it); }
+      }
     }
 
-    recursive_iterator_n operator++(int) {
-      recursive_iterator_n tmp{*this};
-      (void)super::next();
-      return tmp;
+    template <size_t I, typename C> void assign(C && collection) {
+      assign<I>(make_end_aware_iterator(std::forward<C>(collection)));
     }
 
-    bool operator!=(recursive_iterator_n const & other) const {
-      return !(super::operator==(other));
+    template <size_t I, typename K, typename V>
+    void assign(std::pair<K const, V> const & pair) {
+      assign<I>(pair.second);
+    }
+    template <size_t I, typename K, typename V>
+    void assign(std::pair<K const, V> & pair) {
+      assign<I>(pair.second);
     }
   };
+
 }
 
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::recursive::rimpl);
+
 namespace std {
   template <std::size_t I, typename It>
-  auto get(::iterator::recursive_iterator<It> const & iter) ->
-      typename ::iterator::detail::accessor<
-          I, ::iterator::recursive_iterator<It>>::type {
-    return iter;
+  auto get(::iterator::recursive_iterator<It> const & iter) {
+    return ::std::get<I>(iter.impl());
   }
 
   template <std::size_t I, typename It, std::size_t N>
-  auto get(::iterator::recursive_iterator_n<It, N> const & iter) ->
-      typename ::iterator::detail::accessor<
-          I, ::iterator::recursive_iterator_n<It, N>>::type {
+  auto get(::iterator::recursive_iterator_n<It, N> const & iter) {
     static_assert(I < N, "Cannot get past bounding level");
-    return iter;
+    return ::std::get<I>(iter.impl());
   }
 }
 
 template <typename C>
 auto make_recursive_iterator(C & collect)
     -> iterator::recursive_iterator<decltype(std::begin(collect))> {
-  return {make_end_aware_iterator(collect)};
+  return iterator::recursive_iterator<decltype(std::begin(collect))>{
+      make_end_aware_iterator(collect)};
 }
 
 template <typename C>
 auto make_recursive_iterator(C const & collect)
     -> iterator::recursive_iterator<decltype(std::begin(collect))> {
-  return {make_end_aware_iterator(collect)};
+  return iterator::recursive_iterator<decltype(std::begin(collect))>{
+      make_end_aware_iterator(collect)};
 }
 
 template <std::size_t Max, typename C>
 auto make_recursive_iterator(C & collect)
     -> iterator::recursive_iterator_n<decltype(std::begin(collect)), Max> {
-  return {make_end_aware_iterator(collect)};
+  return iterator::recursive_iterator_n<decltype(std::begin(collect)), Max>{
+      make_end_aware_iterator(collect)};
 }
 
 template <std::size_t Max, typename C>
 auto make_recursive_iterator(C const & collect)
     -> iterator::recursive_iterator_n<decltype(std::begin(collect)), Max> {
-  return {make_end_aware_iterator(collect)};
+  return iterator::recursive_iterator_n<decltype(std::begin(collect)), Max>{
+      make_end_aware_iterator(collect)};
 }

+ 10 - 51
include/iterator/unkeyed_iterator.hpp

@@ -7,7 +7,7 @@
 
 #pragma once
 
-#include <iterator>
+#include "proxy.h"
 
 namespace iterator {
   /**
@@ -25,57 +25,16 @@ namespace iterator {
    * }
    * \endcode
    */
-  template <typename Iterator> class unkeyed_iterator {
-  private:
-    using impl_value_type = typename std::iterator_traits<Iterator>::value_type;
-    using impl_reference = typename std::iterator_traits<Iterator>::reference;
-    static constexpr std::size_t const value_index =
-        std::tuple_size<impl_value_type>::value - 1;
-
+  template <typename Iterator>
+  class unkeyed_iterator : public proxy<Iterator, unkeyed_iterator<Iterator>> {
   public:
-    using value_type = typename std::remove_reference<decltype(
-        std::get<value_index>(std::declval<impl_value_type>()))>::type;
-    using reference =
-        decltype(std::get<value_index>(std::declval<impl_reference>()));
-    using pointer = value_type *;
-    using difference_type =
-        typename std::iterator_traits<Iterator>::difference_type;
-    using iterator_category =
-        typename std::iterator_traits<Iterator>::iterator_category;
-
-    unkeyed_iterator() = default;
-    unkeyed_iterator(Iterator it) : base(it) {}
-
-    reference operator*() const { return std::get<value_index>(*base); }
-    pointer operator->() const { return std::addressof(operator*()); }
-    unkeyed_iterator & operator++() {
-      ++base;
-      return *this;
-    }
-    unkeyed_iterator operator++(int) {
-      unkeyed_iterator tmp{*this};
-      operator++();
-      return tmp;
-    }
-
-    unkeyed_iterator & operator--() {
-      --base;
-      return *this;
+    using proxy<Iterator, unkeyed_iterator<Iterator>>::proxy;
+    decltype(auto) dereference() const {
+      using value_type = typename Iterator::value_type;
+      constexpr auto index = std::tuple_size<value_type>::value - 1;
+      return std::get<index>(*this->impl());
     }
-    unkeyed_iterator operator--(int) {
-      unkeyed_iterator tmp{*this};
-      operator--();
-      return tmp;
-    }
-
-    bool operator==(unkeyed_iterator const & other) const {
-      return base == other.base;
-    }
-    bool operator!=(unkeyed_iterator const & other) const {
-      return base != other.base;
-    }
-
-  private:
-    Iterator base;
   };
 }
+
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::unkeyed_iterator)

+ 51 - 78
include/iterator/zip_iterator.hpp

@@ -3,105 +3,78 @@
 #include <iterator>
 #include <tuple>
 
-#include "detail/arrow_proxy.h"
+#include "proxy.h"
 
-namespace iterator {
-  template <typename... Iterators> class zip_iterator {
+namespace iterator::zip {
+  template <typename... Iters> struct impl {
   public:
-    using value_type = std::tuple<typename Iterators::value_type...>;
-    using reference = std::tuple<typename Iterators::reference...>;
-    using pointer = void; // tuple cannot be accessed by member
-    using difference_type =
-        std::common_type_t<typename Iterators::difference_type...>;
-    using iterator_category =
-        std::common_type_t<typename Iterators::iterator_category...>;
-
-  private:
-    std::tuple<Iterators...> iterators_;
+    using difference_type = std::common_type_t<
+        typename std::iterator_traits<Iters>::difference_type...>;
 
   public:
-    zip_iterator() = default;
-    zip_iterator(Iterators &&... iters) : iterators_(iters...) {}
-
-    reference operator*() const {
-      return reference(*std::get<Iterators>(iterators_)...);
-    }
-
-    pointer operator->() const;
+    std::tuple<Iters...> data;
 
-    zip_iterator & operator++() {
-      [[maybe_unused]] auto l = {(++(std::get<Iterators>(iterators_)), 0)...};
-      return *this;
-    }
-
-    zip_iterator operator++(int) {
-      zip_iterator tmp = *this;
-      operator++();
-      return tmp;
-    }
+  public:
+    impl() = default;
+    impl(Iters &&... iters) : data(iters...) {}
 
-    zip_iterator & operator--() {
-      [[maybe_unused]] auto l = {(--(std::get<Iterators>(iterators_)), 0)...};
-      return *this;
+    auto operator*() const {
+      return std::make_tuple(*std::get<Iters>(data)...);
     }
 
-    zip_iterator operator--(int) {
-      zip_iterator tmp = *this;
-      operator--();
-      return tmp;
+    void operator++(int) {
+      [[maybe_unused]] auto l = {(++(std::get<Iters>(data)), 0)...};
     }
 
-    zip_iterator & operator+=(difference_type d) {
-      [[maybe_unused]] auto l = {
-          ((std::get<Iterators>(iterators_) += d), 0)...};
-      return *this;
+    void operator--(int) {
+      [[maybe_unused]] auto l = {(--(std::get<Iters>(data)), 0)...};
     }
 
-    zip_iterator operator+(difference_type d) {
-      return zip_iterator{*this} += d;
+    void operator+=(difference_type d) {
+      [[maybe_unused]] auto l = {((std::get<Iters>(data) += d), 0)...};
     }
 
-    reference operator[](difference_type d) { return *(*this + d); }
+    bool operator==(impl const & other) const { return data == other.data; }
 
-    zip_iterator & operator-=(difference_type d) {
-      [[maybe_unused]] auto l = {
-          ((std::get<Iterators>(iterators_) -= d), 0)...};
-      return *this;
+    auto operator-(impl const & other) const {
+      return std::get<0>(data) - std::get<0>(other.data);
     }
+  };
+}
 
-    zip_iterator operator-(difference_type d) {
-      return zip_iterator{*this} -= d;
-    }
+namespace std {
+  template <typename... Iters>
+  struct iterator_traits<::iterator::zip::impl<Iters...>> {
+    using difference_type =
+        common_type_t<typename iterator_traits<Iters>::difference_type...>;
+    using iterator_category =
+        common_type_t<typename iterator_traits<Iters>::iterator_category...>;
+  };
+}
 
-    difference_type operator-(zip_iterator const & other) {
-      return std::get<0>(iterators_) - std::get<0>(other.iterators_);
-    }
+namespace iterator {
+  template <typename... Iters>
+  class zip_iterator
+      : public proxy<zip::impl<Iters...>, zip_iterator<Iters...>> {
+  private:
+    using super = proxy<zip::impl<Iters...>, zip_iterator<Iters...>>;
 
-    bool operator==(zip_iterator const & rhs) const {
-      return iterators_ == rhs.iterators_;
-    }
-    bool operator!=(zip_iterator const & rhs) const {
-      return iterators_ != rhs.iterators_;
-    }
-    bool operator<(zip_iterator const & rhs) const {
-      return iterators_ < rhs.iterators_;
-    }
-    bool operator<=(zip_iterator const & rhs) const {
-      return iterators_ <= rhs.iterators_;
-    }
-    bool operator>(zip_iterator const & rhs) const {
-      return iterators_ > rhs.iterators_;
-    }
-    bool operator>=(zip_iterator const & rhs) const {
-      return iterators_ >= rhs.iterators_;
-    }
+  public:
+    zip_iterator() = default;
+    zip_iterator(Iters &&... iters) : super(std::forward<Iters>(iters)...) {}
   };
+}
 
-  template <typename... Is>
-  zip_iterator<Is...> operator+(typename zip_iterator<Is...>::difference_type d,
-                                zip_iterator<Is...> iter) {
-    return iter += d;
-  }
+namespace std {
+  template <typename... T>
+  struct iterator_traits<::iterator::zip_iterator<T...>>
+      : std::iterator_traits<
+            ::iterator::facade<::iterator::zip_iterator<T...>>> {
+    // This shouldn't need to be implemented, but for some reason my traits
+    // are not correctly deducing here.
+    using iterator_category =
+        common_type_t<typename iterator_traits<T>::iterator_category...>;
+  };
 }
 
 template <typename... Is>

+ 6 - 7
test/end_aware_iterator_test.cxx

@@ -36,19 +36,19 @@ TEST(EndAwareIteratorTest, MutableActionsArePassthrough) {
 TEST(EndAwareIteratorTest, CanTellYouThatItsReachedEnd) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator it{v.end() - 1, v.end()};
-  EXPECT_FALSE(it.done());
+  EXPECT_FALSE(it.at_end());
   ++it;
   EXPECT_THAT(it, end_aware_iterator(v.end(), v.end()));
-  EXPECT_TRUE(it.done());
+  EXPECT_TRUE(it.at_end());
 }
 
 TEST(EndAwareIteratorTest, SingleArgIsEnd) {
   std::vector<int> v{1, 2, 3, 4, 5};
-  EXPECT_TRUE(end_aware_iterator(v.begin()).done());
+  EXPECT_TRUE(end_aware_iterator(v.begin()).at_end());
 }
 
 TEST(EndAwareIteratorTest, EmptyIteratorIsEnd) {
-  EXPECT_TRUE(end_aware_iterator().done());
+  EXPECT_TRUE(end_aware_iterator().at_end());
 }
 
 TEST(EndAwareIteratorTest, AllEndPointsAreEqual) {
@@ -74,11 +74,10 @@ TEST(EndAwareIteratorTest, PostIncrementReturnsCopyOfPrev) {
   EXPECT_THAT(*eai, 2);
 }
 
-TEST(EndAwareIteratorTest, IncrementOnEndIsNoOp) {
+TEST(EndAwareIteratorTest, IncrementOnEndIsUnsafe) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator it{v.end(), v.end()};
   end_aware_iterator const cp = it;
   ++it;
-  EXPECT_THAT(it, cp);
-  EXPECT_TRUE(it.done());
+  EXPECT_NE(it, cp);
 }

+ 2 - 2
test/filter_iterator_test.cxx

@@ -42,7 +42,7 @@ TEST(FilterIteratorTest, IfNonMatchThenStartIsEnd) {
   EXPECT_THAT(it, end);
 }
 
-TEST(FilterIteratorTest, IncrementEndIsNoOp) {
+TEST(FilterIteratorTest, IncrementOnEndIsUnsafe) {
   std::vector<int> const data = {1, 2, 3, 4, 5};
   auto pred = [](int i) { return i % 2 == 0; };
   auto it = make_filter_iterator(pred, data);
@@ -50,5 +50,5 @@ TEST(FilterIteratorTest, IncrementEndIsNoOp) {
   ++++it;
   EXPECT_THAT(it, end);
   ++it;
-  EXPECT_THAT(it, end);
+  EXPECT_NE(it, end);
 }

+ 6 - 4
test/join_iterator_test.cxx

@@ -70,20 +70,22 @@ TEST(JoinIteratorTest, PostIncrementReturnsCopyOfPrev) {
 
 TEST(JoinIteratorTest, MovesFromListToListWhenReachingEnd) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv), {mv[0].end(), mv[0].end()});
+  join_iterator it(make_end_aware_iterator(mv));
+  std::advance(it, 3);
   EXPECT_THAT(*++it, mv[1][0]);
 }
 
 TEST(JoinIteratorTest, SkipsOverEmptyElements) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv), {mv[0].end(), mv[0].end()});
+  join_iterator it(make_end_aware_iterator(mv));
+  std::advance(it, 3);
   EXPECT_THAT(*++it, mv[2][0]);
 }
 
-TEST(JoinIteratorTest, IncrementEndIsNoOp) {
+TEST(JoinIteratorTest, IncrementEndIsUnsafe) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   join_iterator it({mv.end(), mv.end()}, {mv.back().end(), mv.back().end()});
   join_iterator const cp = it;
   ++it;
-  EXPECT_THAT(it, cp);
+  EXPECT_NE(it, cp);
 }

+ 5 - 5
test/recursive_iterator_accessors_test.cxx

@@ -20,11 +20,11 @@ TEST(RecursiveIteratorTest, CanArrowMultiVector) {
   EXPECT_THAT(rit.operator->(), &obj[0][0]);
 }
 
-TEST(RecursiveIteratorTest, CannotArrowMap) {
-  std::map<int, std::vector<int>> obj{{1, {{0, 1}}}, {2, {{2, 3}}}};
-  auto rit = make_recursive_iterator(obj);
-  testing::StaticAssertTypeEq<decltype(rit.operator->()), void>();
-}
+// TEST(RecursiveIteratorTest, CannotArrowMap) {
+//  std::map<int, std::vector<int>> obj{{1, {{0, 1}}}, {2, {{2, 3}}}};
+//  auto rit = make_recursive_iterator(obj);
+//  testing::StaticAssertTypeEq<decltype(rit.operator->()), void>();
+//}
 
 TEST(RecursiveIteratorTest, CanAccessOuterterator) {
   std::map<int, std::vector<int>> obj{{1, {{0, 1}}}, {2, {{2, 3}}}};

+ 18 - 14
test/zip_iterator_test.cxx

@@ -21,32 +21,36 @@ TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
   std::forward_list<int> fwd{1, 2, 3};
   {
     auto zit = make_zip_iterator(rnd.begin(), ss.begin());
-    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
-                                std::random_access_iterator_tag>();
+    testing::StaticAssertTypeEq<
+        std::iterator_traits<decltype(zit)>::iterator_category,
+        std::random_access_iterator_tag>();
   }
   {
     auto zit = make_zip_iterator(rnd.begin(), bid.begin());
-    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
-                                std::bidirectional_iterator_tag>();
+    testing::StaticAssertTypeEq<
+        std::iterator_traits<decltype(zit)>::iterator_category,
+        std::bidirectional_iterator_tag>();
   }
   {
     auto zit = make_zip_iterator(rnd.begin(), fwd.begin());
-    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
-                                std::forward_iterator_tag>();
+    testing::StaticAssertTypeEq<
+        std::iterator_traits<decltype(zit)>::iterator_category,
+        std::forward_iterator_tag>();
   }
   {
     auto zit = make_zip_iterator(bid.begin(), fwd.begin());
-    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
-                                std::forward_iterator_tag>();
+    testing::StaticAssertTypeEq<
+        std::iterator_traits<decltype(zit)>::iterator_category,
+        std::forward_iterator_tag>();
   }
 }
 
-TEST(ZipIteratorTest, CannotInvokeOperatorArrow) {
-  std::vector<int> is{1, 2, 3};
-  std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
-  testing::StaticAssertTypeEq<decltype(zit.operator->()), void>();
-}
+// TEST(ZipIteratorTest, CannotInvokeOperatorArrow) {
+//  std::vector<int> is{1, 2, 3};
+//  std::vector<std::string> ss{"A", "B", "C"};
+//  auto zit = make_zip_iterator(is.begin(), ss.begin());
+//  testing::StaticAssertTypeEq<decltype(zit.operator->()), void>();
+//}
 
 TEST(ZipIteratorTest, CanCombineParallelObjects) {
   std::vector<int> is{1, 2, 3};