瀏覽代碼

Merge branch 'refactor/cleanup'

* refactor/cleanup:
  chore: updates
  refactor: add more type deduction guides
  refactor: a lot of re-indenting
  refactor: apply clang-format
  refactor: improve use of facade in zip/indexed iterators
  cleanup: don't re-declare std namespace
  refactor: remove useless bits from end_aware_iterator
  chore: update project settings
Sam Jaffe 2 年之前
父節點
當前提交
f2cf5bee42

+ 2 - 2
include/iterator/detail/arrow_proxy.h

@@ -3,7 +3,7 @@
  */
 #pragma once
 
-namespace iterator { namespace detail {
+namespace iterator::detail {
   template <typename Reference> struct arrow_proxy {
     arrow_proxy(Reference r) : r(std::move(r)) {}
     Reference r;
@@ -11,4 +11,4 @@ namespace iterator { namespace detail {
   };
 
   template <typename R> arrow_proxy(R r) -> arrow_proxy<R>;
-}}
+}

+ 4 - 11
include/iterator/end_aware_iterator.hpp

@@ -32,7 +32,7 @@ namespace iterator {
 
     template <typename I>
     end_aware_iterator(end_aware_iterator<I> const & other)
-        : curr_(other.current()), end_(other.end()) {}
+        : curr_(other.curr_), end_(other.end_) {}
 
     decltype(auto) dereference() const { return *curr_; }
     void increment() { ++curr_; }
@@ -42,10 +42,8 @@ namespace iterator {
       return (at_end() && other.at_end()) || curr_ == other.curr_;
     }
 
-    It current() const { return curr_; }
-    It end() const { return end_; }
-
   private:
+    template <typename O> friend class end_aware_iterator;
     It curr_, end_;
   };
 
@@ -55,15 +53,10 @@ namespace iterator {
 
 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);
-}
-
 template <typename C> auto make_end_aware_iterator(C & collect) {
-  return make_end_aware_iterator(std::begin(collect), std::end(collect));
+  return iterator::end_aware_iterator(std::begin(collect), std::end(collect));
 }
 
 template <typename C> auto make_end_aware_iterator(C const & collect) {
-  return make_end_aware_iterator(std::begin(collect), std::end(collect));
+  return iterator::end_aware_iterator(std::begin(collect), std::end(collect));
 }

+ 15 - 22
include/iterator/facade.h

@@ -89,7 +89,6 @@ namespace iterator::detail {
 #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>>>;
@@ -236,28 +235,22 @@ namespace iterator {
   };
 }
 
-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>;
-  };
-}
+// In C++20, a concept/requires could be used to eschew the need for the below
+// macros.
+template <typename I> struct std::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>> {};                   \
-  }
+  template <>                                                                  \
+  struct std::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...>>> {};             \
-  }
+  template <typename... T>                                                     \
+  struct std::iterator_traits<type<T...>>                                      \
+      : std::iterator_traits<::iterator::facade<type<T...>>> {}

+ 26 - 29
include/iterator/filter_iterator.hpp

@@ -12,6 +12,8 @@
 #include "end_aware_iterator.hpp"
 #include "facade.h"
 
+template <typename C> using iter = decltype(std::begin(std::declval<C>()));
+
 namespace iterator {
   template <typename Iter>
   class filter_iterator : public facade<filter_iterator<Iter>> {
@@ -21,51 +23,46 @@ namespace iterator {
     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;
+    using predicate_t = std::function<bool(value_type const &)>;
 
   public:
     filter_iterator() = default;
-    template <typename... Args>
-    filter_iterator(std::function<bool(value_type const &)> && p,
-                    Args &&... super_args)
-        : base(end_aware_iterator(std::forward<Args>(super_args)...)),
-          pred(std::move(p)) {
+
+    template <typename C>
+    filter_iterator(predicate_t pred, C && c)
+        : filter_iterator(std::move(pred), std::begin(c), std::end(c)) {}
+
+    filter_iterator(predicate_t pred, Iter curr, Iter end)
+        : filter_iterator(std::move(pred), end_aware_iterator(curr, end)) {}
+
+    filter_iterator(predicate_t pred, end_aware_iterator<Iter> curr)
+        : base_(curr), pred_(std::move(pred)) {
       if (should_advance()) { increment(); }
     }
 
-    decltype(auto) dereference() const { return base.dereference(); }
+    decltype(auto) dereference() const { return base_.dereference(); }
     void increment() {
       do {
-        base.increment();
+        base_.increment();
       } while (should_advance());
     }
     bool equal_to(filter_iterator const & other) const {
-      return base == other.base;
+      return base_ == other.base_;
     }
 
   public:
-    bool should_advance() { return !base.at_end() && !pred(dereference()); }
+    bool should_advance() { return !base_.at_end() && !pred_(dereference()); }
 
-    end_aware_iterator<Iter> base;
-    std::function<bool(value_type const &)> pred;
+    end_aware_iterator<Iter> base_;
+    predicate_t 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) {
-  return {p, it, end};
-}
-
-template <typename Pred, typename C>
-auto make_filter_iterator(Pred && p, C & collect)
-    -> iterator::filter_iterator<decltype(std::begin(collect))> {
-  return {p, std::begin(collect), std::end(collect)};
+  template <typename P, typename C>
+  filter_iterator(P, C &&) -> filter_iterator<iter<C>>;
+  template <typename P, typename It>
+  filter_iterator(P, end_aware_iterator<It>) -> filter_iterator<It>;
+  template <typename P, typename It>
+  filter_iterator(P, It, It) -> filter_iterator<It>;
 }
 
-template <typename Pred, typename C>
-auto make_filter_iterator(Pred && p, C const & collect)
-    -> iterator::filter_iterator<decltype(std::begin(collect))> {
-  return {p, std::begin(collect), std::end(collect)};
-}
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::filter_iterator);

+ 31 - 82
include/iterator/indexed_iterator.hpp

@@ -9,100 +9,49 @@
 
 #include <iterator>
 
-#include "detail/arrow_proxy.h"
+#include "iterator/facade.h"
 
 namespace iterator {
-  template <typename Iterator> class indexed_iterator {
-  private:
-    using base_value_type = typename std::iterator_traits<Iterator>::value_type;
-    using base_reference = typename std::iterator_traits<Iterator>::reference;
-
+  template <typename It>
+  class indexed_iterator : public facade<indexed_iterator<It>> {
   public:
-    using index_type = std::size_t;
-    using value_type = std::pair<index_type, base_value_type>;
-    using reference = std::pair<index_type, base_reference>;
-    using pointer = detail::arrow_proxy<reference>;
-    using difference_type =
-        typename std::iterator_traits<Iterator>::difference_type;
-    using iterator_category =
-        typename std::iterator_traits<Iterator>::iterator_category;
+    using reference =
+        std::pair<size_t, typename std::iterator_traits<It>::reference>;
+    using difference_type = typename std::iterator_traits<It>::difference_type;
 
-    indexed_iterator() : _base(), _index(0) {}
-    indexed_iterator(Iterator base, index_type idx = 0)
-        : _base(base), _index(idx) {}
+  public:
+    indexed_iterator() = default;
+    indexed_iterator(It base) : base_(base) {}
+    indexed_iterator(It base, size_t idx) : base_(base), index_(idx) {}
 
-    template <typename OtherIterator>
-    indexed_iterator(indexed_iterator<OtherIterator> const & oiter)
-        : _base(oiter._base), _index(oiter._index) {}
+    template <typename O>
+    indexed_iterator(indexed_iterator<O> const & oiter)
+        : base_(oiter.base_), index_(oiter.index_) {}
 
-    reference operator*() const { return reference{_index, *_base}; }
-    pointer operator->() const { return {operator*()}; }
+    reference dereference() const { return {index_, *base_}; }
 
-    indexed_iterator & operator++() {
-      ++_base;
-      ++_index;
-      return *this;
-    }
-    indexed_iterator operator++(int) {
-      indexed_iterator tmp{*this};
-      operator++();
-      return tmp;
-    }
-    bool operator==(indexed_iterator const & other) const {
-      return _base == other._base;
-    }
-    bool operator!=(indexed_iterator const & other) const {
-      return _base != other._base;
-    }
-
-    // Requires: iterator_category = bidirectional_iterator_tag
-    indexed_iterator & operator--() {
-      --_base;
-      --_index;
-      return *this;
-    }
-    indexed_iterator operator--(int) {
-      indexed_iterator tmp{*this};
-      operator--();
-      return tmp;
+    void advance(difference_type off) {
+      base_ += off;
+      index_ += off;
     }
 
-    // Requires: iterator_category = random_access_iterator_tag
-    indexed_iterator operator+(difference_type n) const {
-      return indexed_iterator{*this} += n;
+    // SFINAE means that if Iterator is not random access, then this still works
+    // TODO: Investigate using _index for comparisons instead of _base
+    bool equal_to(indexed_iterator const & other) const {
+      return base_ == other.base_;
     }
-    indexed_iterator & operator+=(difference_type n) {
-      _index += n;
-      _base += n;
-      return *this;
-    }
-    difference_type operator-(indexed_iterator const & it) const {
-      return _base - it._base;
-    }
-    indexed_iterator operator-(difference_type n) const {
-      return indexed_iterator{*this} -= n;
-    }
-    indexed_iterator & operator-=(difference_type n) {
-      _index -= n;
-      _base -= n;
-      return *this;
-    }
-    bool operator<=(indexed_iterator const & other) const {
-      return _base <= other._base;
-    }
-    bool operator<(indexed_iterator const & other) const {
-      return _base < other._base;
-    }
-    bool operator>=(indexed_iterator const & other) const {
-      return _base >= other._base;
-    }
-    bool operator>(indexed_iterator const & other) const {
-      return _base > other._base;
+    difference_type distance_to(indexed_iterator const & other) const {
+      return other.base_ - base_;
     }
 
   private:
-    template <typename It> friend class ::iterator::indexed_iterator;
-    Iterator _base;
-    index_type _index;
+    template <typename O> friend class indexed_iterator;
+    It base_;
+    size_t index_{0};
   };
+
+  template <typename It> indexed_iterator(It) -> indexed_iterator<It>;
+  template <typename It> indexed_iterator(It, size_t) -> indexed_iterator<It>;
 }
+
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::indexed_iterator);

+ 8 - 0
include/iterator/join_iterator.hpp

@@ -71,4 +71,12 @@ namespace iterator {
     end_aware_iterator<join_iter> joiner_;
     end_aware_iterator<iter_type> iterator_;
   };
+
+  template <typename JI>
+  joining_iterator(end_aware_iterator<JI>) -> joining_iterator<JI>;
+  template <typename JI, typename IT>
+  joining_iterator(end_aware_iterator<JI>, end_aware_iterator<IT>)
+      -> joining_iterator<JI>;
 }
+
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::joining_iterator);

+ 0 - 1
include/iterator/recursive_iterator.hpp

@@ -170,7 +170,6 @@ namespace iterator::recursive {
       assign<I>(pair.second);
     }
   };
-
 }
 
 MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::recursive::rimpl);

+ 3 - 1
include/iterator/unkeyed_iterator.hpp

@@ -35,6 +35,8 @@ namespace iterator {
       return std::get<index>(*this->impl());
     }
   };
+
+  template <typename It> unkeyed_iterator(It) -> unkeyed_iterator<It>;
 }
 
-MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::unkeyed_iterator)
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::unkeyed_iterator);

+ 42 - 52
include/iterator/zip_iterator.hpp

@@ -3,81 +3,71 @@
 #include <iterator>
 #include <tuple>
 
-#include "proxy.h"
+#include "facade.h"
 
-namespace iterator::zip {
-  template <typename... Iters> struct impl {
-  public:
-    using difference_type = std::common_type_t<
-        typename std::iterator_traits<Iters>::difference_type...>;
+namespace iterator::detail {
+  template <typename Tuple, typename IS> class zip_iterator_impl;
 
+  template <typename... Ts, size_t... Is>
+  class zip_iterator_impl<std::tuple<Ts...>, std::index_sequence<Is...>> {
   public:
-    std::tuple<Iters...> data;
+    using difference_type = std::common_type_t<
+        typename std::iterator_traits<Ts>::difference_type...>;
 
   public:
-    impl() = default;
-    impl(Iters &&... iters) : data(iters...) {}
+    zip_iterator_impl() = default;
+    zip_iterator_impl(Ts... iters) : _data(iters...) {}
 
-    auto operator*() const {
-      return std::make_tuple(*std::get<Iters>(data)...);
+    auto dereference() const {
+      return std::forward_as_tuple(*std::get<Is>(_data)...);
     }
 
-    void operator++(int) {
-      [[maybe_unused]] auto l = {(++(std::get<Iters>(data)), 0)...};
+    void increment() {
+      [[maybe_unused]] auto l = {((++std::get<Is>(_data)), 0)...};
     }
-
-    void operator--(int) {
-      [[maybe_unused]] auto l = {(--(std::get<Iters>(data)), 0)...};
+    void decrement() {
+      [[maybe_unused]] auto l = {((++std::get<Is>(_data)), 0)...};
     }
-
-    void operator+=(difference_type d) {
-      [[maybe_unused]] auto l = {((std::get<Iters>(data) += d), 0)...};
+    void advance(difference_type d) {
+      [[maybe_unused]] auto l = {((std::get<Is>(_data) += d), 0)...};
     }
 
-    bool operator==(impl const & other) const { return data == other.data; }
+    bool equal_to(zip_iterator_impl const & other) const {
+      return _data == other._data;
+    }
 
-    auto operator-(impl const & other) const {
-      return std::get<0>(data) - std::get<0>(other.data);
+    auto distance_to(zip_iterator_impl const & other) const {
+      return std::get<0>(other._data) - std::get<0>(_data);
     }
-  };
-}
 
-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...>;
+  private:
+    std::tuple<Ts...> _data;
   };
 }
 
 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...>>;
+  template <typename... Ts>
+  using index_sequence = decltype(std::make_index_sequence<sizeof...(Ts)>{});
+  template <typename... Ts>
+  using zip_impl =
+      detail::zip_iterator_impl<std::tuple<Ts...>, index_sequence<Ts...>>;
 
+  template <typename... Iters>
+  class zip_iterator : public zip_impl<Iters...>,
+                       public facade<zip_iterator<Iters...>> {
   public:
     zip_iterator() = default;
-    zip_iterator(Iters &&... iters) : super(std::forward<Iters>(iters)...) {}
+    zip_iterator(Iters... iters) : zip_impl<Iters...>(iters...) {}
   };
-}
 
-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... It> zip_iterator(It...) -> zip_iterator<It...>;
 }
 
-template <typename... Is>
-iterator::zip_iterator<Is...> make_zip_iterator(Is &&... iters) {
-  return {std::forward<Is>(iters)...};
-}
+template <typename... T>
+struct std::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...>;
+};

+ 149 - 0
iterator.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CDA2B62028581295004D5353 /* iterator in Headers */ = {isa = PBXBuildFile; fileRef = CDCB3BBC24E1CDE40029B771 /* iterator */; settings = {ATTRIBUTES = (Public, ); }; };
 		CDCB3BCA24E1D39B0029B771 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDEC1E09235167920091D9F2 /* GoogleMock.framework */; };
 		CDCB3BD624E1D5320029B771 /* join_iterator_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3BCD24E1D5320029B771 /* join_iterator_test.cxx */; };
 		CDCB3BD724E1D5320029B771 /* unkeyed_iterator_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3BCE24E1D5320029B771 /* unkeyed_iterator_test.cxx */; };
@@ -61,6 +62,20 @@
 
 /* Begin PBXFileReference section */
 		CD3C6DDB26238F8F00548B64 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
+		CDA2B60928581255004D5353 /* libiterator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiterator.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDA2B6122858128C004D5353 /* iterator_fwd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = iterator_fwd.hpp; sourceTree = "<group>"; };
+		CDA2B6132858128C004D5353 /* facade.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = facade.h; sourceTree = "<group>"; };
+		CDA2B6142858128C004D5353 /* indexed_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = indexed_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B6152858128C004D5353 /* unkeyed_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unkeyed_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B6162858128C004D5353 /* recursive_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = recursive_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B6182858128C004D5353 /* recursive_traits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = recursive_traits.h; sourceTree = "<group>"; };
+		CDA2B6192858128C004D5353 /* traits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = traits.h; sourceTree = "<group>"; };
+		CDA2B61A2858128C004D5353 /* arrow_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arrow_proxy.h; sourceTree = "<group>"; };
+		CDA2B61B2858128C004D5353 /* end_aware_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = end_aware_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B61C2858128C004D5353 /* proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = proxy.h; sourceTree = "<group>"; };
+		CDA2B61D2858128C004D5353 /* zip_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = zip_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B61E2858128C004D5353 /* filter_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filter_iterator.hpp; sourceTree = "<group>"; };
+		CDA2B61F2858128C004D5353 /* join_iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = join_iterator.hpp; sourceTree = "<group>"; };
 		CDCB3BBC24E1CDE40029B771 /* iterator */ = {isa = PBXFileReference; lastKnownFileType = folder; name = iterator; path = include/iterator; sourceTree = "<group>"; };
 		CDCB3BC124E1D3880029B771 /* iterator-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "iterator-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDCB3BC524E1D3880029B771 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -79,6 +94,13 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		CDA2B60728581255004D5353 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDCB3BBE24E1D3880029B771 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -95,6 +117,7 @@
 			children = (
 				CDEC1E01235167920091D9F2 /* GoogleMock.xcodeproj */,
 				CDCB3BBC24E1CDE40029B771 /* iterator */,
+				CDA2B6102858128C004D5353 /* include */,
 				CD21AE271E4A3E8600536178 /* test */,
 				CDCB3BC224E1D3880029B771 /* iterator-test */,
 				CD21AE1E1E4A3E7900536178 /* Products */,
@@ -106,6 +129,7 @@
 			isa = PBXGroup;
 			children = (
 				CDCB3BC124E1D3880029B771 /* iterator-test.xctest */,
+				CDA2B60928581255004D5353 /* libiterator.a */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -129,6 +153,42 @@
 			path = test;
 			sourceTree = "<group>";
 		};
+		CDA2B6102858128C004D5353 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				CDA2B6112858128C004D5353 /* iterator */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		CDA2B6112858128C004D5353 /* iterator */ = {
+			isa = PBXGroup;
+			children = (
+				CDA2B6122858128C004D5353 /* iterator_fwd.hpp */,
+				CDA2B6132858128C004D5353 /* facade.h */,
+				CDA2B6142858128C004D5353 /* indexed_iterator.hpp */,
+				CDA2B6152858128C004D5353 /* unkeyed_iterator.hpp */,
+				CDA2B6162858128C004D5353 /* recursive_iterator.hpp */,
+				CDA2B6172858128C004D5353 /* detail */,
+				CDA2B61B2858128C004D5353 /* end_aware_iterator.hpp */,
+				CDA2B61C2858128C004D5353 /* proxy.h */,
+				CDA2B61D2858128C004D5353 /* zip_iterator.hpp */,
+				CDA2B61E2858128C004D5353 /* filter_iterator.hpp */,
+				CDA2B61F2858128C004D5353 /* join_iterator.hpp */,
+			);
+			path = iterator;
+			sourceTree = "<group>";
+		};
+		CDA2B6172858128C004D5353 /* detail */ = {
+			isa = PBXGroup;
+			children = (
+				CDA2B6182858128C004D5353 /* recursive_traits.h */,
+				CDA2B6192858128C004D5353 /* traits.h */,
+				CDA2B61A2858128C004D5353 /* arrow_proxy.h */,
+			);
+			path = detail;
+			sourceTree = "<group>";
+		};
 		CDCB3BC224E1D3880029B771 /* iterator-test */ = {
 			isa = PBXGroup;
 			children = (
@@ -157,7 +217,35 @@
 		};
 /* End PBXGroup section */
 
+/* Begin PBXHeadersBuildPhase section */
+		CDA2B60528581255004D5353 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDA2B62028581295004D5353 /* iterator in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
 /* Begin PBXNativeTarget section */
+		CDA2B60828581255004D5353 /* iterator */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDA2B60F28581255004D5353 /* Build configuration list for PBXNativeTarget "iterator" */;
+			buildPhases = (
+				CDA2B60528581255004D5353 /* Headers */,
+				CDA2B60628581255004D5353 /* Sources */,
+				CDA2B60728581255004D5353 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = iterator;
+			productName = iterator;
+			productReference = CDA2B60928581255004D5353 /* libiterator.a */;
+			productType = "com.apple.product-type.library.static";
+		};
 		CDCB3BC024E1D3880029B771 /* iterator-test */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = CDCB3BC624E1D3880029B771 /* Build configuration list for PBXNativeTarget "iterator-test" */;
@@ -182,9 +270,14 @@
 		CD21AE151E4A3E7900536178 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
+				LastSwiftUpdateCheck = 1340;
 				LastUpgradeCheck = 1230;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
+					CDA2B60828581255004D5353 = {
+						CreatedOnToolsVersion = 13.4.1;
+						ProvisioningStyle = Automatic;
+					};
 					CDCB3BC024E1D3880029B771 = {
 						CreatedOnToolsVersion = 11.3.1;
 						ProvisioningStyle = Automatic;
@@ -210,6 +303,7 @@
 			);
 			projectRoot = "";
 			targets = (
+				CDA2B60828581255004D5353 /* iterator */,
 				CDCB3BC024E1D3880029B771 /* iterator-test */,
 			);
 		};
@@ -257,6 +351,13 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+		CDA2B60628581255004D5353 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDCB3BBD24E1D3880029B771 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -391,6 +492,45 @@
 			};
 			name = Release;
 		};
+		CDA2B60A28581255004D5353 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				EXECUTABLE_PREFIX = lib;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Debug;
+		};
+		CDA2B60B28581255004D5353 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				EXECUTABLE_PREFIX = lib;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				MACOSX_DEPLOYMENT_TARGET = 12.0;
+				MTL_FAST_MATH = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Release;
+		};
 		CDCB3BC724E1D3880029B771 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -446,6 +586,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		CDA2B60F28581255004D5353 /* Build configuration list for PBXNativeTarget "iterator" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDA2B60A28581255004D5353 /* Debug */,
+				CDA2B60B28581255004D5353 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		CDCB3BC624E1D3880029B771 /* Build configuration list for PBXNativeTarget "iterator-test" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 28 - 2
iterator.xcodeproj/xcshareddata/xcschemes/iterator-test.xcscheme

@@ -1,16 +1,33 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1240"
+   LastUpgradeVersion = "1340"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
       buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDA2B60828581255004D5353"
+               BuildableName = "libiterator.a"
+               BlueprintName = "iterator"
+               ReferencedContainer = "container:iterator.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
    </BuildAction>
    <TestAction
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
@@ -41,6 +58,15 @@
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
       debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDA2B60828581255004D5353"
+            BuildableName = "libiterator.a"
+            BlueprintName = "iterator"
+            ReferencedContainer = "container:iterator.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
    </ProfileAction>
    <AnalyzeAction
       buildConfiguration = "Debug">

+ 0 - 8
test/end_aware_iterator_test.cxx

@@ -7,14 +7,6 @@
 using end_aware_iterator =
     ::iterator::end_aware_iterator<std::vector<int>::iterator>;
 
-TEST(EndAwareIteratorTest, IsConstructedFromInputs) {
-  std::vector<int> v{1, 2, 3, 4, 5};
-  auto begin = v.begin(), end = v.end();
-  end_aware_iterator eai(begin, end);
-  EXPECT_THAT(eai.current(), begin);
-  EXPECT_THAT(eai.end(), end);
-}
-
 // TODO: This ought to be implemented as a compiles-test
 TEST(EndAwareIteratorTest, CanCastCompatibleIterators) {
   std::vector<int> v{1, 2, 3, 4, 5};

+ 8 - 6
test/filter_iterator_test.cxx

@@ -4,10 +4,12 @@
 
 #include "xcode_gtest_helper.h"
 
+using iterator::filter_iterator;
+
 TEST(FilterIteratorTest, CanPerformSkipsOnData) {
   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);
+  filter_iterator it(pred, data);
   decltype(it) end = {};
   EXPECT_THAT(std::distance(it, end), 2);
   EXPECT_THAT(*it++, 2);
@@ -18,7 +20,7 @@ TEST(FilterIteratorTest, CanPerformSkipsOnData) {
 TEST(FilterIteratorTest, MutatingContainerDoesNotMoveIterator) {
   std::vector<int> data = {1, 2, 3, 4, 5};
   auto pred = [](int i) { return i % 2 == 0; };
-  auto it = make_filter_iterator(pred, data);
+  filter_iterator it(pred, data);
   decltype(it) end = {};
   EXPECT_THAT(std::distance(it, end), 2);
   *it = 1;
@@ -29,7 +31,7 @@ TEST(FilterIteratorTest, MutatingContainerDoesNotMoveIterator) {
 TEST(FilterIteratorTest, CanConstructFilterFromSubRange) {
   std::vector<int> data = {1, 2, 3, 4, 5};
   auto pred = [](int i) { return i % 2 == 0; };
-  auto it = make_filter_iterator(pred, data.begin(), data.begin() + 3);
+  filter_iterator it(pred, data.begin(), data.begin() + 3);
   decltype(it) end = {};
   EXPECT_THAT(std::distance(it, end), 1);
 }
@@ -37,7 +39,7 @@ TEST(FilterIteratorTest, CanConstructFilterFromSubRange) {
 TEST(FilterIteratorTest, IfNonMatchThenStartIsEnd) {
   std::vector<int> const data = {1, 3, 5};
   auto pred = [](int i) { return i % 2 == 0; };
-  auto it = make_filter_iterator(pred, data);
+  filter_iterator it(pred, data);
   decltype(it) end = {};
   EXPECT_THAT(it, end);
 }
@@ -45,9 +47,9 @@ TEST(FilterIteratorTest, IfNonMatchThenStartIsEnd) {
 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);
+  filter_iterator it(pred, data);
   decltype(it) end = {};
-  ++++it;
+  ++ ++it;
   EXPECT_THAT(it, end);
   ++it;
   EXPECT_NE(it, end);

+ 36 - 28
test/indexed_iterator_test.cxx

@@ -7,40 +7,47 @@
 
 #include "iterator/end_aware_iterator.hpp"
 
-using idx_iterator = iterator::indexed_iterator<std::vector<int>::iterator>;
+using iterator::end_aware_iterator;
+using iterator::indexed_iterator;
+
+using testing::Ne;
 
 // TODO: This ought to be implemented as a compiles-test
 TEST(IndexedIteratorTest, CanCastCompatibleIterators) {
   std::vector<int> v{1, 2, 3, 4, 5};
-  idx_iterator eai(v.begin());
+  indexed_iterator eai(v.begin());
   ::iterator::indexed_iterator<std::vector<int>::const_iterator>{eai};
 }
 
 TEST(IndexedIteratorTest, CanLieAboutIndex) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it(vec.begin(), 3);
+  indexed_iterator it(vec.begin(), 3);
   EXPECT_THAT(it->first, 3);
 }
 
 TEST(IndexedIteratorTest, FakeIndexDoesntEffectEqualityCheck) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  EXPECT_THAT(idx_iterator(vec.begin()), idx_iterator(vec.begin(), 3));
-  EXPECT_THAT(idx_iterator(vec.begin()) + 3,
-              testing::Ne(idx_iterator(vec.begin(), 3)));
+  EXPECT_THAT(indexed_iterator(vec.begin()), indexed_iterator(vec.begin(), 3));
+  EXPECT_THAT(indexed_iterator(vec.begin()) + 3,
+              testing::Ne(indexed_iterator(vec.begin(), 3)));
+}
+
+TEST(IndexedIteratorTest, DoesNotTrackByIndex) {
+  std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
+  indexed_iterator end(vec.end());
+  EXPECT_THAT(decltype(end){}, Ne(end));
 }
 
-TEST(IndexedIteratorTest, IffBaseIteratorEmptyIsEndThenEmptyIsEnd) {
+TEST(IndexedIteratorTest, IteratorPropagatesAtEnd) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  EXPECT_THAT(idx_iterator(), testing::Ne(idx_iterator(vec.end())));
-  iterator::end_aware_iterator<decltype(vec.end())> end(vec.end());
-  EXPECT_THAT(iterator::indexed_iterator<decltype(end)>(),
-              iterator::indexed_iterator<decltype(end)>(end));
+  indexed_iterator end(end_aware_iterator(vec.end()));
+  EXPECT_THAT(decltype(end){}, end);
 }
 
 TEST(IndexedIteratorTest, CanCompareIteratorOrder) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator const begin(vec.begin());
-  idx_iterator const it = begin + 3;
+  indexed_iterator const begin(vec.begin());
+  indexed_iterator const it = begin + 3;
   EXPECT_THAT(begin, testing::Lt(it));
   EXPECT_THAT(begin, testing::Le(it));
   EXPECT_THAT(it, testing::Le(it));
@@ -51,7 +58,7 @@ TEST(IndexedIteratorTest, CanCompareIteratorOrder) {
 
 TEST(IndexedIteratorTest, PreIncrementAdvancesIterator) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it(vec.begin() + 1);
+  indexed_iterator it(vec.begin() + 1);
   EXPECT_THAT(it->second, 3);
   EXPECT_THAT((++it)->second, 2);
   EXPECT_THAT(it->second, 2);
@@ -59,7 +66,7 @@ TEST(IndexedIteratorTest, PreIncrementAdvancesIterator) {
 
 TEST(IndexedIteratorTest, PostIncrementReturnsCopyOfPrev) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it(vec.begin() + 1);
+  indexed_iterator it(vec.begin() + 1);
   EXPECT_THAT(it->second, 3);
   EXPECT_THAT((it++)->second, 3);
   EXPECT_THAT(it->second, 2);
@@ -67,7 +74,7 @@ TEST(IndexedIteratorTest, PostIncrementReturnsCopyOfPrev) {
 
 TEST(IndexedIteratorTest, PreDecrementAdvancesIterator) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it(vec.begin() + 1);
+  indexed_iterator it(vec.begin() + 1);
   EXPECT_THAT(it->second, 3);
   EXPECT_THAT((--it)->second, 5);
   EXPECT_THAT(it->second, 5);
@@ -75,7 +82,7 @@ TEST(IndexedIteratorTest, PreDecrementAdvancesIterator) {
 
 TEST(IndexedIteratorTest, PostDecrementReturnsCopyOfPrev) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it(vec.begin() + 1);
+  indexed_iterator it(vec.begin() + 1);
   EXPECT_THAT(it->second, 3);
   EXPECT_THAT((it--)->second, 3);
   EXPECT_THAT(it->second, 5);
@@ -83,24 +90,25 @@ TEST(IndexedIteratorTest, PostDecrementReturnsCopyOfPrev) {
 
 TEST(IndexedIteratorTest, CanWalkNStepsForward) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator const begin(vec.begin());
-  idx_iterator it = begin;
+  indexed_iterator const begin(vec.begin());
+  indexed_iterator it = begin;
   it += 4;
   EXPECT_THAT(std::distance(begin, it), 4);
 }
 
 TEST(IndexedIteratorTest, CanWalkNStepsBackwards) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator const end(vec.end());
-  idx_iterator it = end;
+  indexed_iterator const end(vec.end());
+  indexed_iterator it = end;
   it -= 4;
   EXPECT_THAT(std::distance(it, end), 4);
 }
 
 TEST(IndexedIteratorTest, RandomAccessIsPassthrough) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  EXPECT_THAT(idx_iterator(vec.begin()) + 4, idx_iterator(vec.begin() + 4));
-  EXPECT_THAT(idx_iterator(vec.end()) - 4, idx_iterator(vec.end() - 4));
+  EXPECT_THAT(indexed_iterator(vec.begin()) + 4,
+              indexed_iterator(vec.begin() + 4));
+  EXPECT_THAT(indexed_iterator(vec.end()) - 4, indexed_iterator(vec.end() - 4));
 }
 
 TEST(IndexedIteratorTest, TreatsVectorIteratorAsPairIdxValue) {
@@ -108,8 +116,8 @@ TEST(IndexedIteratorTest, TreatsVectorIteratorAsPairIdxValue) {
   std::vector<std::pair<int, int>> const expected{
       {0, 5}, {1, 3}, {2, 2}, {3, 8}, {4, 9}, {5, 11}, {6, 2}, {7, 4}};
 
-  std::vector<std::pair<int, int>> const result(idx_iterator(vec.begin()),
-                                                idx_iterator(vec.end()));
+  std::vector<std::pair<int, int>> const result(indexed_iterator(vec.begin()),
+                                                indexed_iterator(vec.end()));
   EXPECT_THAT(result, expected);
 }
 
@@ -118,13 +126,13 @@ TEST(IndexedIteratorTest, TreatsVectorIteratorAsMapIdxToValue) {
   std::map<int, int> const expected{{0, 5}, {1, 3},  {2, 2}, {3, 8},
                                     {4, 9}, {5, 11}, {6, 2}, {7, 4}};
 
-  std::map<int, int> const result(idx_iterator(vec.begin()),
-                                  idx_iterator(vec.end()));
+  std::map<int, int> const result(indexed_iterator(vec.begin()),
+                                  indexed_iterator(vec.end()));
   EXPECT_THAT(result, expected);
 }
 
 TEST(IndexedIteratorTest, CanMutatePointedToData) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator(vec.begin() + 4, 4)->second = -1;
+  indexed_iterator(vec.begin() + 4, 4)->second = -1;
   EXPECT_THAT(vec[4], -1);
 }

+ 22 - 18
test/join_iterator_test.cxx

@@ -4,57 +4,60 @@
 
 #include "xcode_gtest_helper.h"
 
-using join_iterator =
-    iterator::joining_iterator<std::vector<std::vector<int>>::iterator>;
+using iterator::end_aware_iterator;
+using iterator::joining_iterator;
+
+using testing::Ne;
 
 TEST(JoinIteratorTest, FirstDereferencedElemIsTheFirstInTheChain) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  EXPECT_THAT(*join_iterator(make_end_aware_iterator(mv)), mv[0][0]);
+  EXPECT_THAT(*joining_iterator(make_end_aware_iterator(mv)), mv[0][0]);
 }
 
 TEST(JoinIteratorTest, HoldsReferenceToContainedElements) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  EXPECT_THAT(join_iterator(make_end_aware_iterator(mv)).operator->(),
+  EXPECT_THAT(joining_iterator(make_end_aware_iterator(mv)).operator->(),
               &mv[0][0]);
 }
 
 TEST(JoinIteratorTest, DefaultCtorIsEnd) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  EXPECT_THAT(join_iterator(make_end_aware_iterator(mv)),
-              testing::Ne(join_iterator()));
+  joining_iterator it(make_end_aware_iterator(mv));
+  EXPECT_THAT(it, Ne(decltype(it)()));
   mv.clear();
-  EXPECT_THAT(join_iterator(make_end_aware_iterator(mv)), join_iterator());
+  EXPECT_THAT(joining_iterator(make_end_aware_iterator(mv)), decltype(it)());
 }
 
 // TODO: This ought to be implemented as a compiles-test
 TEST(JoinIteratorTest, CanCastCompatibleIterators) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv));
+  joining_iterator it(make_end_aware_iterator(mv));
   iterator::joining_iterator<decltype(mv)::const_iterator>{it};
 }
 
 TEST(JoinIteratorTest, CanAccessInternalIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   auto eai = make_end_aware_iterator(mv);
-  join_iterator it(eai);
+  joining_iterator it(eai);
   EXPECT_THAT(it.join_iterator(), eai);
 }
 
 TEST(JoinIteratorTest, CanAccessChildIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv));
+  joining_iterator it(make_end_aware_iterator(mv));
   EXPECT_THAT(it.element_iterator(), make_end_aware_iterator(mv[0]));
 }
 
 TEST(JoinIteratorTest, EmptyConstructorEqualsEnd) {
   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()});
-  EXPECT_THAT(it, join_iterator());
+  joining_iterator it(end_aware_iterator(mv.end()),
+                      end_aware_iterator(mv.back().end()));
+  EXPECT_THAT(it, decltype(it)());
 }
 
 TEST(JoinIteratorTest, PreIncrementAdvancesIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv));
+  joining_iterator it(make_end_aware_iterator(mv));
   EXPECT_THAT(*it, 1);
   EXPECT_THAT(*++it, 2);
   EXPECT_THAT(*it, 2);
@@ -62,7 +65,7 @@ TEST(JoinIteratorTest, PreIncrementAdvancesIterator) {
 
 TEST(JoinIteratorTest, PostIncrementReturnsCopyOfPrev) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  join_iterator it(make_end_aware_iterator(mv));
+  joining_iterator it(make_end_aware_iterator(mv));
   EXPECT_THAT(*it, 1);
   EXPECT_THAT(*it++, 1);
   EXPECT_THAT(*it, 2);
@@ -70,22 +73,23 @@ 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));
+  joining_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));
+  joining_iterator it(make_end_aware_iterator(mv));
   std::advance(it, 3);
   EXPECT_THAT(*++it, mv[2][0]);
 }
 
 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;
+  joining_iterator it(end_aware_iterator(mv.end()),
+                      end_aware_iterator(mv.back().end()));
+  joining_iterator const cp = it;
   ++it;
   EXPECT_NE(it, cp);
 }

+ 25 - 17
test/unkeyed_iterator_test.cxx

@@ -7,26 +7,29 @@
 
 #include "iterator/end_aware_iterator.hpp"
 
-using value_iterator = iterator::unkeyed_iterator<std::map<int, int>::iterator>;
+using iterator::end_aware_iterator;
+using iterator::unkeyed_iterator;
+
+using testing::Ne;
 
 TEST(UnkeyedIteratorTest, IteratorOnlyReturnsValues) {
   std::map<int, int> map{{1, 2}, {2, 3}};
   std::vector<int> const expected{2, 3};
-  std::vector<int> const result{value_iterator{map.begin()},
-                                value_iterator{map.end()}};
+  std::vector<int> const result{unkeyed_iterator{map.begin()},
+                                unkeyed_iterator{map.end()}};
   EXPECT_THAT(result, expected);
 }
 
 TEST(UnkeyedIteratorTest, CanModifyIteratedCollectionValues) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  value_iterator uit{map.begin()};
+  unkeyed_iterator uit{map.begin()};
   *uit = 4;
   EXPECT_THAT(map[1], 4);
 }
 
 TEST(UnkeyedIteratorTest, PreIncrementAdvancesIterator) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  value_iterator it(map.begin());
+  unkeyed_iterator it(map.begin());
   EXPECT_THAT(*it, 2);
   EXPECT_THAT(*++it, 3);
   EXPECT_THAT(*it, 3);
@@ -34,7 +37,7 @@ TEST(UnkeyedIteratorTest, PreIncrementAdvancesIterator) {
 
 TEST(UnkeyedIteratorTest, PostIncrementReturnsCopyOfPrev) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  value_iterator it(map.begin());
+  unkeyed_iterator it(map.begin());
   EXPECT_THAT(*it, 2);
   EXPECT_THAT(*it++, 2);
   EXPECT_THAT(*it, 3);
@@ -42,7 +45,7 @@ TEST(UnkeyedIteratorTest, PostIncrementReturnsCopyOfPrev) {
 
 TEST(UnkeyedIteratorTest, PreDecrementAdvancesIterator) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  value_iterator it(++map.begin());
+  unkeyed_iterator it(++map.begin());
   EXPECT_THAT(*it, 3);
   EXPECT_THAT(*--it, 2);
   EXPECT_THAT(*it, 2);
@@ -50,7 +53,7 @@ TEST(UnkeyedIteratorTest, PreDecrementAdvancesIterator) {
 
 TEST(UnkeyedIteratorTest, PostDecrementReturnsCopyOfPrev) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  value_iterator it(++map.begin());
+  unkeyed_iterator it(++map.begin());
   EXPECT_THAT(*it, 3);
   EXPECT_THAT(*it--, 3);
   EXPECT_THAT(*it, 2);
@@ -58,20 +61,25 @@ TEST(UnkeyedIteratorTest, PostDecrementReturnsCopyOfPrev) {
 
 TEST(UnkeyedIteratorTest, EqualityIsPassthrough) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  EXPECT_THAT(++value_iterator(map.begin()), value_iterator(++map.begin()));
-  EXPECT_THAT(value_iterator(map.begin()),
-              testing::Ne(value_iterator(++map.begin())));
+  EXPECT_THAT(++unkeyed_iterator(map.begin()), unkeyed_iterator(++map.begin()));
+  EXPECT_THAT(unkeyed_iterator(map.begin()),
+              Ne(unkeyed_iterator(++map.begin())));
+}
+
+TEST(UnkeyedIteratorTest, DoesNotGenerateAtEnd) {
+  std::map<int, int> map{{1, 2}, {2, 3}};
+  unkeyed_iterator end(map.end());
+  EXPECT_THAT(decltype(end)(), Ne(end));
 }
 
-TEST(UnkeyedIteratorTest, IffBaseIteratorEmptyIsEndThenEmptyIsEnd) {
+TEST(UnkeyedIteratorTest, PropagatesAtEnd) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  EXPECT_THAT(value_iterator(), testing::Ne(value_iterator(map.end())));
-  iterator::end_aware_iterator<decltype(map.end())> end(map.end());
-  EXPECT_THAT(iterator::unkeyed_iterator<decltype(end)>(),
-              iterator::unkeyed_iterator<decltype(end)>(end));
+  unkeyed_iterator end(end_aware_iterator(map.end()));
+  EXPECT_THAT(decltype(end)(), end);
 }
 
 TEST(UnkeyedIteratorTest, CanPointToObject) {
   std::map<int, int> map{{1, 2}, {2, 3}};
-  EXPECT_THAT(value_iterator(map.begin()).operator->(), &(map.begin()->second));
+  EXPECT_THAT(unkeyed_iterator(map.begin()).operator->(),
+              &(map.begin()->second));
 }

+ 1 - 0
test/xcode_gtest_helper.h

@@ -12,6 +12,7 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wquoted-include-in-framework-header"
+#pragma clang diagnostic ignored "-Wcomma"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>

+ 16 - 14
test/zip_iterator_test.cxx

@@ -14,31 +14,33 @@
 
 #include "xcode_gtest_helper.h"
 
+using iterator::zip_iterator;
+
 TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
   std::vector<int> rnd{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
   std::list<int> bid{1, 2, 3};
   std::forward_list<int> fwd{1, 2, 3};
   {
-    auto zit = make_zip_iterator(rnd.begin(), ss.begin());
+    zip_iterator zit(rnd.begin(), ss.begin());
     testing::StaticAssertTypeEq<
         std::iterator_traits<decltype(zit)>::iterator_category,
         std::random_access_iterator_tag>();
   }
   {
-    auto zit = make_zip_iterator(rnd.begin(), bid.begin());
+    zip_iterator zit(rnd.begin(), bid.begin());
     testing::StaticAssertTypeEq<
         std::iterator_traits<decltype(zit)>::iterator_category,
         std::bidirectional_iterator_tag>();
   }
   {
-    auto zit = make_zip_iterator(rnd.begin(), fwd.begin());
+    zip_iterator zit(rnd.begin(), fwd.begin());
     testing::StaticAssertTypeEq<
         std::iterator_traits<decltype(zit)>::iterator_category,
         std::forward_iterator_tag>();
   }
   {
-    auto zit = make_zip_iterator(bid.begin(), fwd.begin());
+    zip_iterator zit(bid.begin(), fwd.begin());
     testing::StaticAssertTypeEq<
         std::iterator_traits<decltype(zit)>::iterator_category,
         std::forward_iterator_tag>();
@@ -55,7 +57,7 @@ TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
 TEST(ZipIteratorTest, CanCombineParallelObjects) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zip_iterator zit(is.begin(), ss.begin());
   EXPECT_THAT(std::get<0>(*zit), 1);
   EXPECT_THAT(std::get<1>(*zit), "A");
 }
@@ -63,7 +65,7 @@ TEST(ZipIteratorTest, CanCombineParallelObjects) {
 TEST(ZipIteratorTest, AdvancingMovesAllElements) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zip_iterator zit(is.begin(), ss.begin());
   zit++;
   EXPECT_THAT(std::get<0>(*zit), 2);
   EXPECT_THAT(std::get<1>(*zit), "B");
@@ -72,7 +74,7 @@ TEST(ZipIteratorTest, AdvancingMovesAllElements) {
 TEST(ZipIteratorTest, CanDecrement) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zip_iterator zit(is.begin(), ss.begin());
   ++zit;
   zit--;
   EXPECT_THAT(std::get<0>(*zit), 1);
@@ -82,7 +84,7 @@ TEST(ZipIteratorTest, CanDecrement) {
 TEST(ZipIteratorTest, CanMoveByAnyAmount) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zip_iterator zit(is.begin(), ss.begin());
   zit = 2 + zit;
   EXPECT_THAT(std::get<0>(*zit), 3);
   EXPECT_THAT(std::get<1>(*zit), "C");
@@ -94,15 +96,15 @@ TEST(ZipIteratorTest, CanMoveByAnyAmount) {
 TEST(ZipIteratorTest, CanMarkDistance) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
-  auto zend = make_zip_iterator(is.end(), ss.end());
+  zip_iterator zit(is.begin(), ss.begin());
+  zip_iterator zend(is.end(), ss.end());
   EXPECT_THAT(zend - zit, 3);
 }
 
 TEST(ZipIteratorTest, CanRandomAccess) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zip_iterator zit(is.begin(), ss.begin());
   EXPECT_THAT(std::get<0>(zit[1]), 2);
   EXPECT_THAT(std::get<1>(zit[1]), "B");
 }
@@ -110,9 +112,9 @@ TEST(ZipIteratorTest, CanRandomAccess) {
 TEST(ZipIteratorTest, CanCompareIterators) {
   std::vector<int> is{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
-  auto const zit = make_zip_iterator(is.begin(), ss.begin());
-  auto const zend = make_zip_iterator(is.end(), ss.end());
-  EXPECT_THAT(zit, testing::Eq(make_zip_iterator(is.begin(), ss.begin())));
+  zip_iterator const zit(is.begin(), ss.begin());
+  zip_iterator const zend(is.end(), ss.end());
+  EXPECT_THAT(zit, testing::Eq(zip_iterator(is.begin(), ss.begin())));
   EXPECT_THAT(zit, testing::Ne(zend));
   EXPECT_THAT(zit, testing::Lt(zend));
   EXPECT_THAT(zit, testing::Le(zend));