浏览代码

refactor: extract facade traits to a separate header

test: add simple ranges functions for constructing objects and using sentinel values with iterators
Sam Jaffe 2 年之前
父节点
当前提交
59f7b92269

+ 86 - 0
include/iterator/detail/facade_traits.h

@@ -0,0 +1,86 @@
+//
+//  facade_traits.h
+//  iterator
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+#include <iterator/detail/traits.h>
+
+#define _val(type) std::declval<type>()
+#define exists(expr) std::void_t<decltype(expr)>
+
+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, std::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 T> using distance_to_t = typename distance_to<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 It>
+constexpr bool is_single_pass_iterator_v = is_single_pass_iterator<It>{};
+
+template <typename It> struct facade_category {
+  constexpr static bool has_random_access =
+      has_distance_to_v<It> && is_advanceable_iterator_v<It>;
+  constexpr static bool has_bidirectional =
+      has_decrement_v<It> && has_increment_v<It> && has_equal_to_v<It>;
+
+  using type = std::conditional_t<
+      has_random_access, std::random_access_iterator_tag,
+      std::conditional_t<has_bidirectional, std::bidirectional_iterator_tag,
+                         std::conditional_t<is_single_pass_iterator_v<It>,
+                                            std::input_iterator_tag,
+                                            std::forward_iterator_tag>>>;
+};
+
+template <typename It>
+using facade_category_t = typename facade_category<It>::type;
+}

+ 36 - 8
include/iterator/detail/traits.h

@@ -1,11 +1,16 @@
 #pragma once
 
-#define _val(type) std::declval<type>()
-#define exists(expr) std::void_t<decltype(expr)>
+#include <iterator>
+#include <type_traits>
 
-#include <iterator/iterator_fwd.hpp>
+namespace iterator {
+struct sentinel_t;
+}
 
 namespace iterator::detail {
+template <typename C> using iter = decltype(std::begin(std::declval<C>()));
+
+// Type Helper for deducing reference types
 template <typename T, typename = void> struct reference_helper {
   using type = decltype(*std::declval<T>());
 };
@@ -14,6 +19,7 @@ struct reference_helper<T, std::void_t<typename T::reference>> {
   using type = typename T::reference;
 };
 
+// Type Helper for deducing value types
 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>>;
@@ -23,16 +29,38 @@ struct value_type_helper<T, std::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;
-
+// Type Helper for identifying container-like objects
 template <typename C, typename = void> struct is_container : std::false_type {};
 template <typename C>
 struct is_container<C, std::void_t<iter<C>>> : std::true_type {};
 
+template <typename T> using value_type = typename value_type_helper<T>::type;
+template <typename T> using reference = typename reference_helper<T>::type;
+
 template <typename C> constexpr bool is_container_v = is_container<C>{};
 
 template <typename Iter>
-constexpr bool is_rvalue_iterator_v =
-    !std::is_reference_v<decltype(*std::declval<Iter>())>;
+constexpr bool is_rvalue_iterator_v = !std::is_reference_v<reference<Iter>>;
+
+template <typename Iter>
+using category = typename std::iterator_traits<Iter>::iterator_category;
+
+template <typename Iter>
+constexpr bool is_random_access_v =
+    std::is_same_v<category<Iter>, std::random_access_iterator_tag>;
+
+template <typename Iter>
+constexpr bool is_bidirectional_v =
+    std::is_same_v<category<Iter>, std::bidirectional_iterator_tag> ||
+    is_random_access_v<Iter>;
+
+template <typename Iter>
+constexpr bool is_forward_v =
+    std::is_same_v<category<Iter>, std::forward_iterator_tag> ||
+    is_bidirectional_v<Iter>;
+
+template <typename Iter> constexpr bool is_single_pass_v = !is_forward_v<Iter>;
+
+template <typename It, typename S>
+using sentinel_type = std::conditional_t<std::is_same_v<It, S>, sentinel_t, S>;
 }

+ 8 - 5
include/iterator/end_aware_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  end_aware_iterator.hpp
+//  end_aware_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 2/7/17.
@@ -9,7 +9,7 @@
 
 #include <iterator/detail/traits.h>
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 #include <iterator/sentinel.h>
 
 namespace iterator {
@@ -43,10 +43,12 @@ public:
       : curr_(other.curr_), end_(other.end_) {}
 
   decltype(auto) dereference() const { return *curr_; }
-  void increment() { ++curr_; }
+  void increment() {
+    if (!at_end()) { ++curr_; }
+  }
   bool at_end() const { return curr_ == end_; }
   bool equal_to(end_aware_iterator const & other) const {
-    // TODO: Fix this clause
+    // TODO: This needs to exist to pass IterDistanceIsSumOfInnerContainerSizes?
     return (at_end() && other.at_end()) || curr_ == other.curr_;
   }
 
@@ -55,7 +57,8 @@ private:
   It curr_, end_;
 };
 
-template <typename C> end_aware_iterator(C &&) -> end_aware_iterator<iter<C>>;
+template <typename C>
+end_aware_iterator(C &&) -> end_aware_iterator<detail::iter<C>>;
 template <typename It> end_aware_iterator(It, It) -> end_aware_iterator<It>;
 template <typename It>
 end_aware_iterator(end_aware_iterator<It>, end_aware_iterator<It>)

+ 2 - 71
include/iterator/facade.h

@@ -4,78 +4,9 @@
 #include <type_traits>
 
 #include <iterator/detail/arrow_proxy.h>
+#include <iterator/detail/facade_traits.h>
 #include <iterator/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, std::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 T> using distance_to_t = typename distance_to<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 =
@@ -216,7 +147,7 @@ template <typename I> struct std::iterator_traits<::iterator::facade<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>;
+  using iterator_category = ::iterator::detail::facade_category_t<I>;
 };
 
 #define MAKE_ITERATOR_FACADE_TYPEDEFS(type)                                    \

+ 4 - 4
include/iterator/filter_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  filter_iterator.hpp
+//  filter_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 6/17/17.
@@ -10,9 +10,9 @@
 #include <functional>
 
 #include <iterator/detail/traits.h>
-#include <iterator/end_aware_iterator.hpp>
+#include <iterator/end_aware_iterator.h>
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator {
 template <typename Iter, bool Cache>
@@ -63,7 +63,7 @@ public:
 };
 
 template <typename P, typename C>
-filter_iterator(P, C &&) -> filter_iterator<iter<C>>;
+filter_iterator(P, C &&) -> filter_iterator<detail::iter<C>>;
 template <typename P, typename It>
 filter_iterator(P, end_aware_iterator<It>) -> filter_iterator<It>;
 template <typename P, typename It>

+ 9 - 5
include/iterator/iterator_fwd.hpp

@@ -1,5 +1,5 @@
 //
-//  iterator_fwd.hpp
+//  iterator_fwd.h
 //  iterator
 //
 //  Created by Sam Jaffe on 2/18/17.
@@ -17,6 +17,9 @@ template <typename, typename = unbounded> class rimpl;
 }
 
 namespace iterator {
+struct sentinel_t;
+
+// Iterator types
 template <typename> class end_aware_iterator;
 template <typename, bool = false> class filter_iterator;
 template <typename> class joining_iterator;
@@ -26,11 +29,12 @@ template <typename...> 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>>;
+}
+
+#include <iterator/detail/traits.h>
 
+namespace iterator {
 template <typename> class facade;
-template <typename Iter, typename,
-          typename = typename std::iterator_traits<Iter>::iterator_category>
+template <typename Iter, typename, typename = detail::category<Iter>>
 class proxy;
-
-template <typename C> using iter = decltype(std::begin(std::declval<C>()));
 }

+ 2 - 2
include/iterator/indexed_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  indexed_iterator.hpp
+//  indexed_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 3/5/17.
@@ -10,7 +10,7 @@
 #include <iterator>
 
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator {
 template <typename It>

+ 9 - 8
include/iterator/join_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  join_iterator.hpp
+//  join_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 2/7/17.
@@ -11,9 +11,9 @@
 #include <memory>
 #include <utility>
 
-#include <iterator/end_aware_iterator.hpp>
+#include <iterator/end_aware_iterator.h>
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator::joining {
 template <typename Iter, typename = void> class iterator {
@@ -34,7 +34,7 @@ public:
   iterator(end_aware_iterator<Iter> join) : iterator(join, {}, true) {}
 
   iterator(end_aware_iterator<Iter> join,
-           end_aware_iterator<iter<inner_t>> elem, bool update = false)
+           end_aware_iterator<detail::iter<inner_t>> elem, bool update = false)
       : joiner_(join), element_(elem) {
     if (update) { update_iterator(); }
   }
@@ -50,7 +50,7 @@ protected:
 protected:
   template <typename, typename> friend class iterator;
   end_aware_iterator<Iter> joiner_;
-  end_aware_iterator<iter<inner_t>> element_;
+  end_aware_iterator<detail::iter<inner_t>> element_;
 };
 
 template <typename Iter>
@@ -75,7 +75,7 @@ protected:
   }
 
 private:
-  end_aware_iterator<iter<inner_t>> const & sync() {
+  end_aware_iterator<detail::iter<inner_t>> const & sync() {
     if (joiner_.at_end()) { return element_ = {}; }
     cache_ = std::make_shared<inner_t>(*joiner_);
     element_ = end_aware_iterator(*cache_);
@@ -85,7 +85,7 @@ private:
 protected:
   end_aware_iterator<Iter> joiner_;
   std::shared_ptr<inner_t> cache_;
-  end_aware_iterator<iter<inner_t>> element_;
+  end_aware_iterator<detail::iter<inner_t>> element_;
 };
 }
 
@@ -124,7 +124,8 @@ public:
   auto const & element_iterator() const { return super_t::element_; }
 };
 
-template <typename C> joining_iterator(C &&) -> joining_iterator<iter<C>>;
+template <typename C>
+joining_iterator(C &&) -> joining_iterator<detail::iter<C>>;
 template <typename JI>
 joining_iterator(end_aware_iterator<JI>) -> joining_iterator<JI>;
 }

+ 1 - 1
include/iterator/proxy.h

@@ -1,7 +1,7 @@
 #pragma once
 
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator {
 template <typename Iter, typename Self>

+ 7 - 5
include/iterator/recursive_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  recursive_iterator.hpp
+//  recursive_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 2/17/17.
@@ -12,9 +12,9 @@
 #include <utility>
 
 #include <iterator/detail/recursive_traits.h>
-#include <iterator/end_aware_iterator.hpp>
+#include <iterator/end_aware_iterator.h>
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator::recursive {
 template <size_t N, size_t I> struct bounded {
@@ -84,6 +84,7 @@ struct tuple<It, Bnd, recursion_type::ASSOC> {
 template <typename It, typename Bnd>
 class rimpl : public facade<rimpl<It, Bnd>> {
 public:
+  using sentinel_type = sentinel_t;
   using iters_t = typename tuple<It, Bnd>::iter;
   static constexpr size_t n_iters =
       std::tuple_size_v<typename tuple<It, Bnd>::iter>;
@@ -194,10 +195,11 @@ auto get(::iterator::recursive_iterator_n<It, N> const & iter) {
 }
 
 template <typename C> auto make_recursive_iterator(C && collect) {
-  return iterator::recursive_iterator<iterator::iter<C>>(collect);
+  return iterator::recursive_iterator<iterator::detail::iter<C>>(collect);
 }
 
 template <std::size_t Max, typename C>
 auto make_recursive_iterator(C && collect) {
-  return iterator::recursive_iterator_n<iterator::iter<C>, Max>(collect);
+  return iterator::recursive_iterator_n<iterator::detail::iter<C>, Max>(
+      collect);
 }

+ 2 - 0
include/iterator/sentinel.h

@@ -8,6 +8,8 @@
 
 #pragma once
 
+#include <iterator/forwards.h>
+
 namespace iterator {
 struct sentinel_t {};
 inline constexpr sentinel_t sentinel;

+ 2 - 2
include/iterator/unkeyed_iterator.hpp

@@ -1,5 +1,5 @@
 //
-//  unkeyed_iterator.hpp
+//  unkeyed_iterator.h
 //  iterator
 //
 //  Created by Sam Jaffe on 2/20/17.
@@ -7,7 +7,7 @@
 
 #pragma once
 
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 #include <iterator/proxy.h>
 
 namespace iterator {

+ 1 - 1
include/iterator/zip_iterator.hpp

@@ -4,7 +4,7 @@
 #include <tuple>
 
 #include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
 
 namespace iterator::detail {
 template <typename Tuple, typename IS> class zip_iterator_impl;

+ 20 - 16
iterator.xcodeproj/project.pbxproj

@@ -63,21 +63,23 @@
 /* Begin PBXFileReference section */
 		CD3C6DDB26238F8F00548B64 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
 		CD5A8E7329D7910D008C2A4F /* sentinel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sentinel.h; sourceTree = "<group>"; };
+		CD5AEAE729D86D8100A390A4 /* ranges.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ranges.h; sourceTree = "<group>"; };
+		CD5AEAE829D86DCD00A390A4 /* facade_traits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = facade_traits.h; sourceTree = "<group>"; };
 		CD6EBE2229D5C93A00F387C1 /* sentinel_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sentinel_iterator.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>"; };
+		CDA2B6122858128C004D5353 /* forwards.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = forwards.h; 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>"; };
+		CDA2B6142858128C004D5353 /* indexed_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = indexed_iterator.h; sourceTree = "<group>"; };
+		CDA2B6152858128C004D5353 /* unkeyed_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = unkeyed_iterator.h; sourceTree = "<group>"; };
+		CDA2B6162858128C004D5353 /* recursive_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = recursive_iterator.h; 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>"; };
+		CDA2B61B2858128C004D5353 /* end_aware_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = end_aware_iterator.h; 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>"; };
+		CDA2B61D2858128C004D5353 /* zip_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zip_iterator.h; sourceTree = "<group>"; };
+		CDA2B61E2858128C004D5353 /* filter_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = filter_iterator.h; sourceTree = "<group>"; };
+		CDA2B61F2858128C004D5353 /* join_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = join_iterator.h; 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>"; };
@@ -140,6 +142,7 @@
 			isa = PBXGroup;
 			children = (
 				CD3C6DDB26238F8F00548B64 /* xcode_gtest_helper.h */,
+				CD5AEAE729D86D8100A390A4 /* ranges.h */,
 				CDCB3BD224E1D5320029B771 /* end_aware_iterator_test.cxx */,
 				CDCB3BD324E1D5320029B771 /* filter_iterator_test.cxx */,
 				CDCB3BD424E1D5320029B771 /* indexed_iterator_test.cxx */,
@@ -166,19 +169,19 @@
 		CDA2B6112858128C004D5353 /* iterator */ = {
 			isa = PBXGroup;
 			children = (
-				CDA2B6122858128C004D5353 /* iterator_fwd.hpp */,
+				CDA2B6122858128C004D5353 /* forwards.h */,
 				CDA2B6132858128C004D5353 /* facade.h */,
-				CDA2B6142858128C004D5353 /* indexed_iterator.hpp */,
-				CDA2B6152858128C004D5353 /* unkeyed_iterator.hpp */,
-				CDA2B6162858128C004D5353 /* recursive_iterator.hpp */,
+				CDA2B6142858128C004D5353 /* indexed_iterator.h */,
+				CDA2B6152858128C004D5353 /* unkeyed_iterator.h */,
+				CDA2B6162858128C004D5353 /* recursive_iterator.h */,
 				CDA2B6172858128C004D5353 /* detail */,
-				CDA2B61B2858128C004D5353 /* end_aware_iterator.hpp */,
+				CDA2B61B2858128C004D5353 /* end_aware_iterator.h */,
 				CD6EBE2229D5C93A00F387C1 /* sentinel_iterator.h */,
 				CDA2B61C2858128C004D5353 /* proxy.h */,
 				CD5A8E7329D7910D008C2A4F /* sentinel.h */,
-				CDA2B61D2858128C004D5353 /* zip_iterator.hpp */,
-				CDA2B61E2858128C004D5353 /* filter_iterator.hpp */,
-				CDA2B61F2858128C004D5353 /* join_iterator.hpp */,
+				CDA2B61D2858128C004D5353 /* zip_iterator.h */,
+				CDA2B61E2858128C004D5353 /* filter_iterator.h */,
+				CDA2B61F2858128C004D5353 /* join_iterator.h */,
 			);
 			path = iterator;
 			sourceTree = "<group>";
@@ -188,6 +191,7 @@
 			children = (
 				CDA2B6182858128C004D5353 /* recursive_traits.h */,
 				CDA2B6192858128C004D5353 /* traits.h */,
+				CD5AEAE829D86DCD00A390A4 /* facade_traits.h */,
 				CDA2B61A2858128C004D5353 /* arrow_proxy.h */,
 			);
 			path = detail;

+ 43 - 26
test/end_aware_iterator_test.cxx

@@ -1,70 +1,87 @@
-#include "iterator/end_aware_iterator.hpp"
+#include "iterator/end_aware_iterator.h"
 
 #include <vector>
 
 #include "xcode_gtest_helper.h"
 
-using end_aware_iterator =
-    ::iterator::end_aware_iterator<std::vector<int>::iterator>;
+using iterator::end_aware_iterator;
 
 // TODO: This ought to be implemented as a compiles-test
-TEST(EndAwareIteratorTest, CanCastCompatibleIterators) {
+TEST(EndAwareIterator, CanCastCompatibleIterators) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator eai(v.begin(), v.end());
-  ::iterator::end_aware_iterator<std::vector<int>::const_iterator>{eai};
+
+  end_aware_iterator<std::vector<int>::const_iterator>{eai};
 }
 
-TEST(EndAwareIteratorTest, BeginWrapperIsEqualToBegin) {
+TEST(EndAwareIterator, BeginWrapperIsEqualToBegin) {
   std::vector<int> v{1, 2, 3, 4, 5};
-  EXPECT_THAT(*v.begin(), *end_aware_iterator(v.begin(), v.end()));
+
+  EXPECT_EQ(*v.begin(), *end_aware_iterator(v.begin(), v.end()));
 }
 
-TEST(EndAwareIteratorTest, MutableActionsArePassthrough) {
+TEST(EndAwareIterator, MutableActionsArePassthrough) {
   std::vector<int> v{1, 2, 3, 4, 5};
   *end_aware_iterator(v.begin(), v.end()) = -1;
-  EXPECT_THAT(v[0], -1);
+
+  EXPECT_EQ(v[0], -1);
 }
 
-TEST(EndAwareIteratorTest, CanTellYouThatItsReachedEnd) {
+TEST(EndAwareIterator, CanTellYouThatItsReachedEnd) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator it{v.end() - 1, v.end()};
   EXPECT_FALSE(it.at_end());
   ++it;
-  EXPECT_THAT(it, end_aware_iterator(v.end(), v.end()));
+  EXPECT_EQ(it, end_aware_iterator(v.end(), v.end()));
   EXPECT_TRUE(it.at_end());
 }
 
-TEST(EndAwareIteratorTest, EmptyIteratorIsEnd) {
-  EXPECT_TRUE(end_aware_iterator().at_end());
+TEST(EndAwareIterator, EmptyIteratorIsEnd) {
+  EXPECT_TRUE(end_aware_iterator<std::vector<int>::iterator>().at_end());
 }
 
-TEST(EndAwareIteratorTest, AllEndPointsAreEqual) {
+TEST(EndAwareIterator, AllEndPointsAreEqual) {
   std::vector<int> v1{1, 2, 3, 4, 5};
   std::vector<int> v2{8, 9, 10};
-  EXPECT_THAT(end_aware_iterator(v1.end(), v1.end()),
-              end_aware_iterator(v2.end(), v2.end()));
+
+  EXPECT_EQ(end_aware_iterator(v1.end(), v1.end()),
+            end_aware_iterator(v2.end(), v2.end()));
 }
 
-TEST(EndAwareIteratorTest, PreIncrementAdvancesIterator) {
+TEST(EndAwareIterator, PreIncrementAdvancesIterator) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator eai(v.begin(), v.end());
-  EXPECT_THAT(*eai, 1);
-  EXPECT_THAT(*++eai, 2);
-  EXPECT_THAT(*eai, 2);
+
+  EXPECT_EQ(*eai, 1);
+  EXPECT_EQ(*++eai, 2);
+  EXPECT_EQ(*eai, 2);
 }
 
-TEST(EndAwareIteratorTest, PostIncrementReturnsCopyOfPrev) {
+TEST(EndAwareIterator, PostIncrementReturnsCopyOfPrev) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator eai(v.begin(), v.end());
-  EXPECT_THAT(*eai, 1);
-  EXPECT_THAT(*eai++, 1);
-  EXPECT_THAT(*eai, 2);
+
+  EXPECT_EQ(*eai, 1);
+  EXPECT_EQ(*eai++, 1);
+  EXPECT_EQ(*eai, 2);
 }
 
-TEST(EndAwareIteratorTest, IncrementOnEndIsUnsafe) {
+TEST(EndAwareIterator, IncrementOnEndIsGuarded) {
   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_NE(it, cp);
+  EXPECT_EQ(it, cp);
+}
+
+TEST(EndAwareIterator, CanIterateWithSentinel) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  size_t count{0};
+  for (end_aware_iterator it(v); it != iterator::sentinel; ++it) {
+    ++count;
+  }
+
+  EXPECT_EQ(count, 5);
 }

+ 41 - 31
test/filter_iterator_test.cxx

@@ -1,56 +1,66 @@
-#include "iterator/filter_iterator.hpp"
+#include "iterator/filter_iterator.h"
 
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
 using iterator::filter_iterator;
 
-TEST(FilterIteratorTest, CanPerformSkipsOnData) {
+bool is_even(int i) { return i % 2 == 0; }
+
+TEST(FilterIterator, CanPerformSkipsOnData) {
   std::vector<int> const data = {1, 2, 3, 4, 5};
-  auto pred = [](int i) { return i % 2 == 0; };
-  filter_iterator it(pred, data);
-  decltype(it) end = {};
-  EXPECT_THAT(std::distance(it, end), 2);
-  EXPECT_THAT(*it++, 2);
-  EXPECT_THAT(*it++, 4);
+  filter_iterator it(is_even, data);
+
+  EXPECT_EQ(ranges::distance(it, iterator::sentinel), 2);
+  EXPECT_EQ(*it++, 2);
+  EXPECT_EQ(*it++, 4);
 }
 
 // TODO: Maybe this should actually move
-TEST(FilterIteratorTest, MutatingContainerDoesNotMoveIterator) {
+TEST(FilterIterator, MutatingContainerDoesNotMoveIterator) {
   std::vector<int> data = {1, 2, 3, 4, 5};
-  auto pred = [](int i) { return i % 2 == 0; };
-  filter_iterator it(pred, data);
-  decltype(it) end = {};
-  EXPECT_THAT(std::distance(it, end), 2);
+  filter_iterator it(is_even, data);
+
+  EXPECT_EQ(ranges::distance(it, iterator::sentinel), 2);
+
   *it = 1;
-  EXPECT_THAT(std::distance(it, end), 2);
-  EXPECT_THAT(*it, 1);
+  EXPECT_EQ(ranges::distance(it, iterator::sentinel), 2);
+  EXPECT_EQ(*it, 1);
 }
 
-TEST(FilterIteratorTest, CanConstructFilterFromSubRange) {
+TEST(FilterIterator, CanConstructFilterFromSubRange) {
   std::vector<int> data = {1, 2, 3, 4, 5};
-  auto pred = [](int i) { return i % 2 == 0; };
-  filter_iterator it(pred, data.begin(), data.begin() + 3);
-  decltype(it) end = {};
-  EXPECT_THAT(std::distance(it, end), 1);
+  filter_iterator it(is_even, data.begin(), data.begin() + 3);
+
+  EXPECT_EQ(ranges::distance(it, iterator::sentinel), 1);
 }
 
-TEST(FilterIteratorTest, IfNonMatchThenStartIsEnd) {
+TEST(FilterIterator, IfNonMatchThenStartIsEnd) {
   std::vector<int> const data = {1, 3, 5};
-  auto pred = [](int i) { return i % 2 == 0; };
-  filter_iterator it(pred, data);
-  decltype(it) end = {};
-  EXPECT_THAT(it, end);
+  filter_iterator it(is_even, data);
+
+  EXPECT_EQ(it, iterator::sentinel);
 }
 
-TEST(FilterIteratorTest, IncrementOnEndIsUnsafe) {
+TEST(FilterIterator, CapsIterationAtEnd) {
   std::vector<int> const data = {1, 2, 3, 4, 5};
-  auto pred = [](int i) { return i % 2 == 0; };
-  filter_iterator it(pred, data);
-  decltype(it) end = {};
+  filter_iterator it(is_even, data);
+
   ++ ++it;
-  EXPECT_THAT(it, end);
+  EXPECT_EQ(it, iterator::sentinel);
+
   ++it;
-  EXPECT_NE(it, end);
+  EXPECT_EQ(it, iterator::sentinel);
+}
+
+TEST(FilterIterator, CanIterateWithSentinel) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  size_t count{0};
+  for (filter_iterator it(is_even, v); it != iterator::sentinel; ++it) {
+    ++count;
+  }
+
+  EXPECT_EQ(count, 5);
 }

+ 20 - 13
test/indexed_iterator_test.cxx

@@ -1,22 +1,27 @@
-#include "iterator/indexed_iterator.hpp"
+#include "iterator/indexed_iterator.h"
 
 #include <map>
 #include <vector>
 
-#include "xcode_gtest_helper.h"
+#include "iterator/end_aware_iterator.h"
 
-#include "iterator/end_aware_iterator.hpp"
+#include "ranges.h"
+#include "xcode_gtest_helper.h"
 
 using iterator::end_aware_iterator;
 using iterator::indexed_iterator;
 
+using testing::Ge;
+using testing::Gt;
+using testing::Le;
+using testing::Lt;
 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};
   indexed_iterator eai(v.begin());
-  ::iterator::indexed_iterator<std::vector<int>::const_iterator>{eai};
+  indexed_iterator<std::vector<int>::const_iterator>{eai};
 }
 
 TEST(IndexedIteratorTest, CanLieAboutIndex) {
@@ -29,7 +34,7 @@ TEST(IndexedIteratorTest, FakeIndexDoesntEffectEqualityCheck) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
   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)));
+              Ne(indexed_iterator(vec.begin(), 3)));
 }
 
 TEST(IndexedIteratorTest, DoesNotTrackByIndex) {
@@ -41,6 +46,7 @@ TEST(IndexedIteratorTest, DoesNotTrackByIndex) {
 TEST(IndexedIteratorTest, IteratorPropagatesAtEnd) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
   indexed_iterator end(end_aware_iterator(vec.end(), vec.end()));
+
   EXPECT_THAT(decltype(end){}, end);
 }
 
@@ -48,12 +54,13 @@ TEST(IndexedIteratorTest, CanCompareIteratorOrder) {
   std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
   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));
-  EXPECT_THAT(it, testing::Gt(begin));
-  EXPECT_THAT(it, testing::Ge(begin));
-  EXPECT_THAT(it, testing::Ge(it));
+
+  EXPECT_THAT(begin, Lt(it));
+  EXPECT_THAT(begin, Le(it));
+  EXPECT_THAT(it, Le(it));
+  EXPECT_THAT(it, Gt(begin));
+  EXPECT_THAT(it, Ge(begin));
+  EXPECT_THAT(it, Ge(it));
 }
 
 TEST(IndexedIteratorTest, PreIncrementAdvancesIterator) {
@@ -93,7 +100,7 @@ TEST(IndexedIteratorTest, CanWalkNStepsForward) {
   indexed_iterator const begin(vec.begin());
   indexed_iterator it = begin;
   it += 4;
-  EXPECT_THAT(std::distance(begin, it), 4);
+  EXPECT_THAT(ranges::distance(begin, it), 4);
 }
 
 TEST(IndexedIteratorTest, CanWalkNStepsBackwards) {
@@ -101,7 +108,7 @@ TEST(IndexedIteratorTest, CanWalkNStepsBackwards) {
   indexed_iterator const end(vec.end());
   indexed_iterator it = end;
   it -= 4;
-  EXPECT_THAT(std::distance(it, end), 4);
+  EXPECT_THAT(ranges::distance(it, end), 4);
 }
 
 TEST(IndexedIteratorTest, RandomAccessIsPassthrough) {

+ 17 - 16
test/join_iterator_test.cxx

@@ -1,4 +1,4 @@
-#include "iterator/join_iterator.hpp"
+#include "iterator/join_iterator.h"
 
 #include <vector>
 
@@ -11,20 +11,21 @@ using testing::Ne;
 
 TEST(JoinIteratorTest, FirstDereferencedElemIsTheFirstInTheChain) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  EXPECT_THAT(*joining_iterator(mv), mv[0][0]);
+  EXPECT_EQ(*joining_iterator(mv), mv[0][0]);
 }
 
 TEST(JoinIteratorTest, HoldsReferenceToContainedElements) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
-  EXPECT_THAT(joining_iterator(mv).operator->(), &mv[0][0]);
+  EXPECT_EQ(joining_iterator(mv).operator->(), &mv[0][0]);
 }
 
-TEST(JoinIteratorTest, DefaultCtorIsEnd) {
+TEST(JoinIteratorTest, EmptyContainerBeginIsEnd) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   joining_iterator it(mv);
-  EXPECT_THAT(it, Ne(decltype(it)()));
+  EXPECT_NE(it, iterator::sentinel);
+
   mv.clear();
-  EXPECT_THAT(joining_iterator(mv), decltype(it)());
+  EXPECT_EQ(joining_iterator(mv), iterator::sentinel);
 }
 
 // TODO: This ought to be implemented as a compiles-test
@@ -38,41 +39,41 @@ TEST(JoinIteratorTest, CanAccessInternalIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   auto eai = end_aware_iterator(mv);
   joining_iterator it(eai);
-  EXPECT_THAT(it.join_iterator(), eai);
+  EXPECT_EQ(it.join_iterator(), eai);
 }
 
 TEST(JoinIteratorTest, CanAccessChildIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   joining_iterator it(mv);
-  EXPECT_THAT(it.element_iterator(), end_aware_iterator(mv[0]));
+  EXPECT_EQ(it.element_iterator(), end_aware_iterator(mv[0]));
 }
 
 TEST(JoinIteratorTest, PreIncrementAdvancesIterator) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   joining_iterator it(mv);
-  EXPECT_THAT(*it, 1);
-  EXPECT_THAT(*++it, 2);
-  EXPECT_THAT(*it, 2);
+  EXPECT_EQ(*it, 1);
+  EXPECT_EQ(*++it, 2);
+  EXPECT_EQ(*it, 2);
 }
 
 TEST(JoinIteratorTest, PostIncrementReturnsCopyOfPrev) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   joining_iterator it(mv);
-  EXPECT_THAT(*it, 1);
-  EXPECT_THAT(*it++, 1);
-  EXPECT_THAT(*it, 2);
+  EXPECT_EQ(*it, 1);
+  EXPECT_EQ(*it++, 1);
+  EXPECT_EQ(*it, 2);
 }
 
 TEST(JoinIteratorTest, MovesFromListToListWhenReachingEnd) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   joining_iterator it(mv);
   std::advance(it, 2);
-  EXPECT_THAT(*++it, mv[1][0]);
+  EXPECT_EQ(*++it, mv[1][0]);
 }
 
 TEST(JoinIteratorTest, SkipsOverEmptyElements) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {}, {4, 5, 6}};
   joining_iterator it(mv);
   std::advance(it, 2);
-  EXPECT_THAT(*++it, mv[2][0]);
+  EXPECT_EQ(*++it, mv[2][0]);
 }

+ 40 - 0
test/ranges.h

@@ -0,0 +1,40 @@
+//
+//  ranges.h
+//  iterator
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+namespace ranges {
+template <typename It, typename S, typename F>
+auto for_each(It it, S end, F consumer) {
+  for (; it != end; ++it) {
+    std::invoke(consumer, *it);
+  }
+  return std::make_pair(it, consumer);
+}
+
+template <typename It, typename S> auto distance(It it, S end) {
+  typename std::iterator_traits<It>::difference_type diff{0};
+  for_each(it, end, [&diff](auto const &) { ++diff; });
+  return diff;
+}
+
+template <template <typename...> class C, typename It, typename S>
+auto to(It it, S end) {
+  decltype(C(it, it)) rval;
+  for_each(it, end,
+           [&rval](auto const & elem) { rval.insert(rval.end(), elem); });
+  return rval;
+}
+
+template <typename C, typename It, typename S> auto to(It it, S end) {
+  C rval;
+  for_each(it, end,
+           [&rval](auto const & elem) { rval.insert(rval.end(), elem); });
+  return rval;
+}
+}

+ 41 - 33
test/recursive_iterator_accessors_test.cxx

@@ -1,45 +1,50 @@
-#include "iterator/recursive_iterator.hpp"
+#include "iterator/recursive_iterator.h"
 
 #include <map>
 #include <string>
 #include <tuple>
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
 using iterator::end_aware_iterator;
 
+using testing::StaticAssertTypeEq;
+
 TEST(RecursiveIteratorTest, DoesNotUnwrapString) {
   std::vector<std::string> obj{"A", "B", "C", "D"};
   auto rit = make_recursive_iterator(obj);
-  testing::StaticAssertTypeEq<decltype(rit.operator->()), std::string *>();
+  StaticAssertTypeEq<decltype(rit.operator->()), std::string *>();
 }
 
 TEST(RecursiveIteratorTest, CanArrowMultiVector) {
   std::vector<std::vector<int>> obj{{{0, 1}}, {{2, 3}}};
   auto rit = make_recursive_iterator(obj);
-  testing::StaticAssertTypeEq<decltype(rit.operator->()), int *>();
-  EXPECT_THAT(rit.operator->(), &obj[0][0]);
+  StaticAssertTypeEq<decltype(rit.operator->()), int *>();
+  EXPECT_EQ(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>();
+//  StaticAssertTypeEq<decltype(rit.operator->()), void>();
 //}
 
 TEST(RecursiveIteratorTest, CanAccessOuterterator) {
   std::map<int, std::vector<int>> obj{{1, {{0, 1}}}, {2, {{2, 3}}}};
   auto rit = make_recursive_iterator(obj);
-  iterator::end_aware_iterator<decltype(obj)::iterator> inner = rit;
-  EXPECT_THAT(&std::get<0>(*rit), &(inner->first));
+
+  end_aware_iterator<decltype(obj)::iterator> inner = rit;
+  EXPECT_EQ(&std::get<0>(*rit), &(inner->first));
 }
 
 TEST(RecursiveIteratorTest, CanAccessInnerIterator) {
   std::map<int, std::vector<int>> obj{{1, {{0, 1}}}, {2, {{2, 3}}}};
   auto rit = make_recursive_iterator(obj);
-  iterator::end_aware_iterator<std::vector<int>::iterator> inner = rit;
-  EXPECT_THAT(&std::get<1>(*rit), &*inner);
+
+  end_aware_iterator<std::vector<int>::iterator> inner = rit;
+  EXPECT_EQ(&std::get<1>(*rit), &*inner);
 }
 
 TEST(RecursiveIteratorTest, CanStdGetToAllLayersOfInternalIteration) {
@@ -49,16 +54,16 @@ TEST(RecursiveIteratorTest, CanStdGetToAllLayersOfInternalIteration) {
   };
   auto rit = make_recursive_iterator(obj);
   using mvm_iterator = std::map<int, std::vector<std::map<int, int>>>::iterator;
-  testing::StaticAssertTypeEq<decltype(std::get<0>(rit)),
-                              iterator::end_aware_iterator<mvm_iterator>>();
+  StaticAssertTypeEq<decltype(std::get<0>(rit)),
+                     end_aware_iterator<mvm_iterator>>();
   using vm_iterator = std::vector<std::map<int, int>>::iterator;
-  testing::StaticAssertTypeEq<decltype(std::get<1>(rit)),
-                              iterator::end_aware_iterator<vm_iterator>>();
+  StaticAssertTypeEq<decltype(std::get<1>(rit)),
+                     end_aware_iterator<vm_iterator>>();
   using m_iterator = std::map<int, int>::iterator;
-  testing::StaticAssertTypeEq<decltype(std::get<2>(rit)),
-                              iterator::end_aware_iterator<m_iterator>>();
+  StaticAssertTypeEq<decltype(std::get<2>(rit)),
+                     end_aware_iterator<m_iterator>>();
   using tup_i_i_i = std::tuple<int const &, int const &, int &>;
-  testing::StaticAssertTypeEq<decltype(*rit), tup_i_i_i>();
+  StaticAssertTypeEq<decltype(*rit), tup_i_i_i>();
 }
 
 TEST(RecursiveIteratorTest, CanAccessInternalIteratorsWithGet) {
@@ -67,9 +72,9 @@ TEST(RecursiveIteratorTest, CanAccessInternalIteratorsWithGet) {
       {2, {{{3, 3}, {4, 4}}}}    // 1 2-element map
   };
   auto rit = make_recursive_iterator(obj);
-  EXPECT_THAT(std::get<0>(rit), end_aware_iterator(obj));
-  EXPECT_THAT(std::get<1>(rit), end_aware_iterator(obj[1]));
-  EXPECT_THAT(std::get<2>(rit), end_aware_iterator(obj[1][0]));
+  EXPECT_EQ(std::get<0>(rit), end_aware_iterator(obj));
+  EXPECT_EQ(std::get<1>(rit), end_aware_iterator(obj[1]));
+  EXPECT_EQ(std::get<2>(rit), end_aware_iterator(obj[1][0]));
 }
 
 // TODO: This ought to be implemented as a compiles-test
@@ -88,10 +93,12 @@ TEST(RecursiveIteratorTest, EmptyCtorIsEnd) {
       {2, {{{3, 3}, {4, 4}}}}    // 1 2-element map
   };
   auto rit = make_recursive_iterator(obj);
-  EXPECT_THAT(rit, testing::Ne(decltype(rit)()));
-  EXPECT_THAT(std::distance(rit, decltype(rit)()), 4);
+
+  EXPECT_NE(rit, iterator::sentinel);
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 4);
+
   std::advance(rit, 4);
-  EXPECT_THAT(rit, decltype(rit)());
+  EXPECT_EQ(rit, iterator::sentinel);
 }
 
 TEST(BoundedRecursiveIteratorTest, CanStdGetToNLayersOfInternalIteration) {
@@ -101,13 +108,13 @@ TEST(BoundedRecursiveIteratorTest, CanStdGetToNLayersOfInternalIteration) {
   };
   auto rit = make_recursive_iterator<2>(obj);
   using mvm_iterator = std::map<int, std::vector<std::map<int, int>>>::iterator;
-  testing::StaticAssertTypeEq<decltype(std::get<0>(rit)),
-                              iterator::end_aware_iterator<mvm_iterator>>();
+  StaticAssertTypeEq<decltype(std::get<0>(rit)),
+                     end_aware_iterator<mvm_iterator>>();
   using vm_iterator = std::vector<std::map<int, int>>::iterator;
-  testing::StaticAssertTypeEq<decltype(std::get<1>(rit)),
-                              iterator::end_aware_iterator<vm_iterator>>();
+  StaticAssertTypeEq<decltype(std::get<1>(rit)),
+                     end_aware_iterator<vm_iterator>>();
   using tup_i_mii = std::tuple<int const &, std::map<int, int> &>;
-  testing::StaticAssertTypeEq<decltype(*rit), tup_i_mii>();
+  StaticAssertTypeEq<decltype(*rit), tup_i_mii>();
 }
 
 TEST(BoundedRecursiveIteratorTest, CanAccessInternalIteratorsWithGet) {
@@ -116,8 +123,8 @@ TEST(BoundedRecursiveIteratorTest, CanAccessInternalIteratorsWithGet) {
       {2, {{{3, 3}, {4, 4}}}}    // 1 2-element map
   };
   auto rit = make_recursive_iterator<2>(obj);
-  EXPECT_THAT(std::get<0>(rit), end_aware_iterator(obj));
-  EXPECT_THAT(std::get<1>(rit), end_aware_iterator(obj[1]));
+  EXPECT_EQ(std::get<0>(rit), end_aware_iterator(obj));
+  EXPECT_EQ(std::get<1>(rit), end_aware_iterator(obj[1]));
 }
 
 // TODO: This ought to be implemented as a compiles-test
@@ -136,10 +143,11 @@ TEST(BoundedRecursiveIteratorTest, EmptyCtorIsEnd) {
       {2, {{{3, 3}, {4, 4}}}}    // 1 2-element map
   };
   auto rit = make_recursive_iterator<3>(obj);
-  EXPECT_THAT(rit, testing::Ne(decltype(rit)()));
-  EXPECT_THAT(std::distance(rit, decltype(rit)()), 4);
+  EXPECT_NE(rit, iterator::sentinel);
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 4);
+
   std::advance(rit, 4);
-  EXPECT_THAT(rit, decltype(rit)());
+  EXPECT_EQ(rit, iterator::sentinel);
 }
 
 TEST(BoundedRecursiveIteratorTest, CanFetchInnerCollections) {
@@ -148,5 +156,5 @@ TEST(BoundedRecursiveIteratorTest, CanFetchInnerCollections) {
       {{{{3, 3}, {4, 4}}}}    // 1 2-element map
   };
   auto rit = make_recursive_iterator<2>(obj);
-  EXPECT_THAT(*rit, obj[0][0]);
+  EXPECT_EQ(*rit, obj[0][0]);
 }

+ 29 - 24
test/recursive_iterator_map_test.cxx

@@ -1,36 +1,38 @@
-#include "iterator/recursive_iterator.hpp"
+#include "iterator/recursive_iterator.h"
 
 #include <map>
 #include <tuple>
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
+using testing::IsEmpty;
+
 TEST(RecursiveIteratorMapTest, PreIncrementAdvancesIterator) {
   std::map<int, std::map<int, std::map<int, int>>> const map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator(map);
-  EXPECT_THAT(std::get<3>(*rit), 1);
-  EXPECT_THAT(std::get<3>(*++rit), 2);
-  EXPECT_THAT(std::get<3>(*rit), 2);
+  EXPECT_EQ(std::get<3>(*rit), 1);
+  EXPECT_EQ(std::get<3>(*++rit), 2);
+  EXPECT_EQ(std::get<3>(*rit), 2);
 }
 
 TEST(RecursiveIteratorMapTest, PostIncrementReturnsCopyOfPrev) {
   std::map<int, std::map<int, std::map<int, int>>> const map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator(map);
-  EXPECT_THAT(std::get<3>(*rit), 1);
-  EXPECT_THAT(std::get<3>(*rit++), 1);
-  EXPECT_THAT(std::get<3>(*rit), 2);
+  EXPECT_EQ(std::get<3>(*rit), 1);
+  EXPECT_EQ(std::get<3>(*rit++), 1);
+  EXPECT_EQ(std::get<3>(*rit), 2);
 }
 
 TEST(RecursiveIteratorMapTest, IterDistanceIsSumOfInnerContainerSizes) {
   std::map<int, std::map<int, std::map<int, int>>> const map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator(map);
-  decltype(rit) end{};
-  // TODO: Actually perform the summation?
-  EXPECT_THAT(std::distance(rit, end), 4);
+
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 4);
 }
 
 TEST(RecursiveIteratorMapTest, ElementsAreUnwrappedAsATuple) {
@@ -39,8 +41,10 @@ TEST(RecursiveIteratorMapTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::tuple<int, int, int, int>> const expected{
       {1, 1, 1, 1}, {2, 2, 2, 2}, {2, 3, 3, 3}, {2, 3, 4, 4}};
   auto rit = make_recursive_iterator(map);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ((ranges::to<std::vector<std::tuple<int, int, int, int>>>(
+                rit, iterator::sentinel)),
+            expected);
 }
 
 TEST(RecursiveIteratorMapTest, CanMutatePointedToData) {
@@ -48,34 +52,33 @@ TEST(RecursiveIteratorMapTest, CanMutatePointedToData) {
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator(map);
   std::get<3>(*rit) = 4;
-  EXPECT_THAT(map[1][1][1], 4);
+  EXPECT_EQ(map[1][1][1], 4);
 }
 
 TEST(BoundRecursiveIteratorMapTest, PreIncrementAdvancesIterator) {
   std::map<int, std::map<int, std::map<int, int>>> map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator<2>(map);
-  EXPECT_THAT(std::get<2>(*rit), map[1][1]);
-  EXPECT_THAT(std::get<2>(*++rit), map[2][2]);
-  EXPECT_THAT(std::get<2>(*rit), map[2][2]);
+  EXPECT_EQ(std::get<2>(*rit), map[1][1]);
+  EXPECT_EQ(std::get<2>(*++rit), map[2][2]);
+  EXPECT_EQ(std::get<2>(*rit), map[2][2]);
 }
 
 TEST(BoundRecursiveIteratorMapTest, PostIncrementReturnsCopyOfPrev) {
   std::map<int, std::map<int, std::map<int, int>>> map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator<2>(map);
-  EXPECT_THAT(std::get<2>(*rit), map[1][1]);
-  EXPECT_THAT(std::get<2>(*rit++), map[1][1]);
-  EXPECT_THAT(std::get<2>(*rit), map[2][2]);
+  EXPECT_EQ(std::get<2>(*rit), map[1][1]);
+  EXPECT_EQ(std::get<2>(*rit++), map[1][1]);
+  EXPECT_EQ(std::get<2>(*rit), map[2][2]);
 }
 
 TEST(BoundRecursiveIteratorMapTest, IterDistanceSumOnNLayersSize) {
   std::map<int, std::map<int, std::map<int, int>>> const map{
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator<2>(map);
-  decltype(rit) end{};
 
-  EXPECT_THAT(std::distance(rit, end), 3);
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 3);
 }
 
 TEST(BoundRecursiveIteratorMapTest, ElementsAreUnwrappedAsATuple) {
@@ -84,8 +87,10 @@ TEST(BoundRecursiveIteratorMapTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::tuple<int, int, std::map<int, int>>> const expected{
       {1, 1, {{1, 1}}}, {2, 2, {{2, 2}}}, {2, 3, {{3, 3}, {4, 4}}}};
   auto rit = make_recursive_iterator<2>(map);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ((ranges::to<std::vector<std::tuple<int, int, std::map<int, int>>>>(
+                rit, iterator::sentinel)),
+            expected);
 }
 
 TEST(BoundedRecursiveIteratorMapTest, CanMutatePointedToData) {
@@ -93,5 +98,5 @@ TEST(BoundedRecursiveIteratorMapTest, CanMutatePointedToData) {
       {1, {{1, {{1, 1}}}}}, {2, {{2, {{2, 2}}}, {3, {{3, 3}, {4, 4}}}}}};
   auto rit = make_recursive_iterator<2>(map);
   std::get<2>(*rit).clear();
-  EXPECT_THAT(map[1][1], testing::IsEmpty());
+  EXPECT_THAT(map[1][1], IsEmpty());
 }

+ 32 - 26
test/recursive_iterator_mixed_container_test.cxx

@@ -1,16 +1,16 @@
-#include "iterator/recursive_iterator.hpp"
+#include "iterator/recursive_iterator.h"
 
 #include <map>
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
 TEST(RecursiveIteratorMapVectorTest, IterDistanceIsSumOfInnerContainerSizes) {
   std::map<int, std::vector<int>> const obj{{1, {1, 2}}, {2, {3, 4, 5}}};
   auto rit = make_recursive_iterator(obj);
-  decltype(rit) end{};
-  // TODO: Actually perform the summation?
-  EXPECT_THAT(std::distance(rit, end), 5);
+
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 5);
 }
 
 TEST(RecursiveIteratorMapVectorTest, ElementsAreUnwrappedAsATuple) {
@@ -18,15 +18,17 @@ TEST(RecursiveIteratorMapVectorTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::tuple<int, int>> const expected{
       {1, 1}, {1, 2}, {2, 3}, {2, 4}, {2, 5}};
   auto rit = make_recursive_iterator(obj);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ(
+      (ranges::to<std::vector<std::tuple<int, int>>>(rit, iterator::sentinel)),
+      expected);
 }
 
 TEST(RecursiveIteratorMapVectorTest, CanMutatePointedToData) {
   std::map<int, std::vector<int>> obj{{1, {1, 2}}, {2, {3, 4, 5}}};
   auto rit = make_recursive_iterator(obj);
   std::get<1>(*rit) = 6;
-  EXPECT_THAT(obj[1][0], 6);
+  EXPECT_EQ(obj[1][0], 6);
 }
 
 TEST(RecursiveIteratorMapMapVectorTest, CanMutatePointedToData) {
@@ -34,26 +36,27 @@ TEST(RecursiveIteratorMapMapVectorTest, CanMutatePointedToData) {
                                                      {2, {{1, {3, 4, 5}}}}};
   auto rit = make_recursive_iterator(obj);
   std::get<2>(*rit) = 6;
-  EXPECT_THAT(obj[1][1][0], 6);
+  EXPECT_EQ(obj[1][1][0], 6);
 }
 
 TEST(RecursiveIteratorVectorMapTest, IterDistanceIsSumOfInnerContainerSizes) {
   std::vector<std::map<int, int>> const obj{{{1, 1}, {2, 2}},
                                             {{3, 3}, {4, 4}, {5, 5}}};
   auto rit = make_recursive_iterator(obj);
-  decltype(rit) end{};
-  // TODO: Actually perform the summation?
-  EXPECT_THAT(std::distance(rit, end), 5);
+
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 5);
 }
 
 TEST(RecursiveIteratorVectorMapTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::map<int, int>> const obj{{{1, 1}, {2, 2}},
                                             {{3, 3}, {4, 4}, {5, 5}}};
-  std::vector<std::pair<int const, int>> const expected{
+  std::vector<std::pair<int, int>> const expected{
       {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
   auto rit = make_recursive_iterator(obj);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ(
+      (ranges::to<std::vector<std::pair<int, int>>>(rit, iterator::sentinel)),
+      expected);
 }
 
 TEST(RecursiveIteratorVectorMapTest, CanMutatePointedToData) {
@@ -61,7 +64,7 @@ TEST(RecursiveIteratorVectorMapTest, CanMutatePointedToData) {
                                       {{3, 3}, {4, 4}, {5, 5}}};
   auto rit = make_recursive_iterator(obj);
   std::get<1>(*rit) = 6;
-  EXPECT_THAT(obj[0][1], 6);
+  EXPECT_EQ(obj[0][1], 6);
 }
 
 TEST(RecursiveIteratorMapVecMapTest, IterDistanceIsSumOfInnerContainerSizes) {
@@ -69,8 +72,8 @@ TEST(RecursiveIteratorMapVecMapTest, IterDistanceIsSumOfInnerContainerSizes) {
       {1, {{{1, 1}, {2, 2}}}}};
   std::vector<std::tuple<int, int, int>> const expected{{1, 1, 1}, {1, 2, 2}};
   auto rit = make_recursive_iterator(obj);
-  decltype(rit) end{};
-  EXPECT_THAT(std::distance(rit, end), expected.size());
+
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), expected.size());
 }
 
 TEST(RecursiveIteratorMapVecMapTest, ElementsAreUnwrappedAsATuple) {
@@ -78,24 +81,25 @@ TEST(RecursiveIteratorMapVecMapTest, ElementsAreUnwrappedAsATuple) {
       {1, {{{1, 1}, {2, 2}}}}};
   std::vector<std::tuple<int, int, int>> const expected{{1, 1, 1}, {1, 2, 2}};
   auto rit = make_recursive_iterator(obj);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ((ranges::to<std::vector<std::tuple<int, int, int>>>(
+                rit, iterator::sentinel)),
+            expected);
 }
 
 TEST(RecursiveIteratorMapVecMapTest, CanMutatePointedToData) {
   std::map<int, std::vector<std::map<int, int>>> obj{{1, {{{1, 1}, {2, 2}}}}};
   auto rit = make_recursive_iterator(obj);
   std::get<2>(*rit) = 4;
-  EXPECT_THAT(obj[1][0][1], 4);
+  EXPECT_EQ(obj[1][0][1], 4);
 }
 
 TEST(RecursiveIteratorVecMapVecTest, IterDistanceIsSumOfInnerContainerSizes) {
   std::vector<std::map<int, std::vector<int>>> const obj{
       {{1, {1, 2}}, {2, {3, 4, 5}}}, {{1, {3, 4}}}};
   auto rit = make_recursive_iterator(obj);
-  decltype(rit) end{};
-  // TODO: Actually perform the summation?
-  EXPECT_THAT(std::distance(rit, end), 7);
+
+  EXPECT_EQ(ranges::distance(rit, iterator::sentinel), 7);
 }
 
 TEST(RecursiveIteratorVecMapVecTest, ElementsAreUnwrappedAsATuple) {
@@ -104,8 +108,10 @@ TEST(RecursiveIteratorVecMapVecTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::tuple<int, int>> const expected{
       {1, 1}, {1, 2}, {2, 3}, {2, 4}, {2, 5}, {1, 3}, {1, 4}};
   auto rit = make_recursive_iterator(obj);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  EXPECT_EQ(
+      (ranges::to<std::vector<std::tuple<int, int>>>(rit, iterator::sentinel)),
+      expected);
 }
 
 TEST(RecursiveIteratorVecMapVecTest, CanMutatePointedToData) {
@@ -113,5 +119,5 @@ TEST(RecursiveIteratorVecMapVecTest, CanMutatePointedToData) {
       {{1, {1, 2}}, {2, {3, 4, 5}}}, {{1, {3, 4}}}};
   auto rit = make_recursive_iterator(obj);
   std::get<1>(*rit) = 6;
-  EXPECT_THAT(obj[0][1][0], 6);
+  EXPECT_EQ(obj[0][1][0], 6);
 }

+ 10 - 9
test/recursive_iterator_single_level_test.cxx

@@ -6,25 +6,26 @@
 //  Copyright © 2020 Sam Jaffe. All rights reserved.
 //
 
-#include "iterator/recursive_iterator.hpp"
+#include "iterator/recursive_iterator.h"
 
 #include <map>
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
 TEST(RecursiveIteratorSingleVectorTest, IterDistanceIsContainerSize) {
   std::vector<int> const vec{1, 2, 3, 4, 5};
   auto rit = make_recursive_iterator(vec);
-  decltype(rit) end{};
-  EXPECT_THAT(std::distance(rit, end), vec.size());
+
+  EXPECT_THAT(ranges::distance(rit, iterator::sentinel), vec.size());
 }
 
 TEST(RecursiveIteratorSingleVectorTest, DataMatchesContainerIterator) {
   std::vector<int> const vec{1, 2, 3, 4, 5};
   auto rit = make_recursive_iterator(vec);
-  decltype(vec) result(rit, decltype(rit)());
-  EXPECT_THAT(result, vec);
+
+  EXPECT_THAT(ranges::to<std::vector<int>>(rit, iterator::sentinel), vec);
 }
 
 TEST(RecursiveIteratorSingleVectorTest, CanMutatePointedToData) {
@@ -37,15 +38,15 @@ TEST(RecursiveIteratorSingleVectorTest, CanMutatePointedToData) {
 TEST(RecursiveIteratorSingleMapTest, IterDistanceIsContainerSize) {
   std::map<int, int> const map{{1, 1}, {2, 2}, {3, 3}};
   auto rit = make_recursive_iterator(map);
-  decltype(rit) end{};
-  EXPECT_THAT(std::distance(rit, end), map.size());
+
+  EXPECT_THAT(ranges::distance(rit, iterator::sentinel), map.size());
 }
 
 TEST(RecursiveIteratorSingleMapTest, DataMatchesContainerIterator) {
   std::map<int, int> const map{{1, 1}, {2, 2}, {3, 3}};
   auto rit = make_recursive_iterator(map);
-  decltype(map) result(rit, decltype(rit)());
-  EXPECT_THAT(result, map);
+
+  EXPECT_THAT((ranges::to<std::map<int, int>>(rit, iterator::sentinel)), map);
 }
 
 TEST(RecursiveIteratorSingleMapTest, CanMutatePointedToData) {

+ 15 - 13
test/recursive_iterator_vector_test.cxx

@@ -1,9 +1,12 @@
-#include "iterator/recursive_iterator.hpp"
+#include "iterator/recursive_iterator.h"
 
 #include <vector>
 
+#include "ranges.h"
 #include "xcode_gtest_helper.h"
 
+using testing::IsEmpty;
+
 TEST(RecursiveIteratorVecTest, PreIncrementAdvancesIterator) {
   std::vector<std::vector<std::vector<int>>> const vec{{{1, 2}}, {{3}, {4, 5}}};
   auto rit = make_recursive_iterator(vec);
@@ -23,18 +26,16 @@ TEST(RecursiveIteratorVecTest, PostIncrementReturnsCopyOfPrev) {
 TEST(RecursiveIteratorVecTest, IterDistanceIsSumOfInnerContainerSizes) {
   std::vector<std::vector<std::vector<int>>> const vec{{{1, 2}}, {{3}, {4, 5}}};
   auto rit = make_recursive_iterator(vec);
-  decltype(rit) end{};
 
-  // TODO: Actually perform the summation?
-  EXPECT_THAT(std::distance(rit, end), 5);
+  EXPECT_THAT(ranges::distance(rit, iterator::sentinel), 5);
 }
 
 TEST(RecursiveIteratorVecTest, FlattensVectorDataLikeJoinIterator) {
   std::vector<std::vector<std::vector<int>>> const vec{{{1, 2}}, {{3}, {4, 5}}};
-  std::vector<int> const expected{1, 2, 3, 4, 5};
   auto rit = make_recursive_iterator(vec);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  std::vector<int> const expected{1, 2, 3, 4, 5};
+  EXPECT_THAT(ranges::to<std::vector<int>>(rit, iterator::sentinel), expected);
 }
 
 TEST(RecursiveIteratorVecTest, CanMutatePointedToData) {
@@ -63,22 +64,23 @@ TEST(BoundedRecursiveIteratorVecTest, PostIncrementReturnsCopyOfPrev) {
 TEST(BoundedRecursiveIteratorVecTest, IterDistanceSumOnNLayersSize) {
   std::vector<std::vector<std::vector<int>>> const vec{{{1, 2}}, {{3}, {4, 5}}};
   auto rit = make_recursive_iterator<2>(vec);
-  decltype(rit) end{};
 
-  EXPECT_THAT(std::distance(rit, end), 3);
+  EXPECT_THAT(ranges::distance(rit, iterator::sentinel), 3);
 }
 
 TEST(BoundedRecursiveIteratorVecTest, ElementsAreUnwrappedAsATuple) {
   std::vector<std::vector<std::vector<int>>> const vec{{{1, 2}}, {{3}, {4, 5}}};
-  std::vector<std::vector<int>> const expected{{1, 2}, {3}, {4, 5}};
   auto rit = make_recursive_iterator<2>(vec);
-  decltype(expected) result(rit, decltype(rit)());
-  EXPECT_THAT(result, expected);
+
+  std::vector<std::vector<int>> const expected{{1, 2}, {3}, {4, 5}};
+  EXPECT_THAT(
+      ranges::to<std::vector<std::vector<int>>>(rit, iterator::sentinel),
+      expected);
 }
 
 TEST(BoundedRecursiveIteratorVecTest, CanMutatePointedToData) {
   std::vector<std::vector<std::vector<int>>> vec{{{1, 2}}, {{3}, {4, 5}}};
   auto rit = make_recursive_iterator<2>(vec);
   rit->clear();
-  EXPECT_THAT(vec[0][0], testing::IsEmpty());
+  EXPECT_THAT(vec[0][0], IsEmpty());
 }

+ 2 - 2
test/unkeyed_iterator_test.cxx

@@ -1,11 +1,11 @@
-#include "iterator/unkeyed_iterator.hpp"
+#include "iterator/unkeyed_iterator.h"
 
 #include <map>
 #include <vector>
 
 #include "xcode_gtest_helper.h"
 
-#include "iterator/end_aware_iterator.hpp"
+#include "iterator/end_aware_iterator.h"
 
 using iterator::end_aware_iterator;
 using iterator::unkeyed_iterator;

+ 26 - 22
test/zip_iterator_test.cxx

@@ -6,7 +6,7 @@
 //  Copyright © 2020 Sam Jaffe. All rights reserved.
 //
 
-#include "iterator/zip_iterator.hpp"
+#include "iterator/zip_iterator.h"
 
 #include <forward_list>
 #include <list>
@@ -16,6 +16,14 @@
 
 using iterator::zip_iterator;
 
+using testing::Eq;
+using testing::Ge;
+using testing::Gt;
+using testing::Le;
+using testing::Lt;
+using testing::Ne;
+using testing::StaticAssertTypeEq;
+
 TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
   std::vector<int> rnd{1, 2, 3};
   std::vector<std::string> ss{"A", "B", "C"};
@@ -23,27 +31,23 @@ TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
   std::forward_list<int> fwd{1, 2, 3};
   {
     zip_iterator zit(rnd.begin(), ss.begin());
-    testing::StaticAssertTypeEq<
-        std::iterator_traits<decltype(zit)>::iterator_category,
-        std::random_access_iterator_tag>();
+    StaticAssertTypeEq<std::iterator_traits<decltype(zit)>::iterator_category,
+                       std::random_access_iterator_tag>();
   }
   {
     zip_iterator zit(rnd.begin(), bid.begin());
-    testing::StaticAssertTypeEq<
-        std::iterator_traits<decltype(zit)>::iterator_category,
-        std::bidirectional_iterator_tag>();
+    StaticAssertTypeEq<std::iterator_traits<decltype(zit)>::iterator_category,
+                       std::bidirectional_iterator_tag>();
   }
   {
     zip_iterator zit(rnd.begin(), fwd.begin());
-    testing::StaticAssertTypeEq<
-        std::iterator_traits<decltype(zit)>::iterator_category,
-        std::forward_iterator_tag>();
+    StaticAssertTypeEq<std::iterator_traits<decltype(zit)>::iterator_category,
+                       std::forward_iterator_tag>();
   }
   {
     zip_iterator zit(bid.begin(), fwd.begin());
-    testing::StaticAssertTypeEq<
-        std::iterator_traits<decltype(zit)>::iterator_category,
-        std::forward_iterator_tag>();
+    StaticAssertTypeEq<std::iterator_traits<decltype(zit)>::iterator_category,
+                       std::forward_iterator_tag>();
   }
 }
 
@@ -51,7 +55,7 @@ TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
 //  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>();
+//  StaticAssertTypeEq<decltype(zit.operator->()), void>();
 //}
 
 TEST(ZipIteratorTest, CanCombineParallelObjects) {
@@ -114,12 +118,12 @@ TEST(ZipIteratorTest, CanCompareIterators) {
   std::vector<std::string> ss{"A", "B", "C"};
   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));
-  EXPECT_THAT(zit, testing::Le(zit));
-  EXPECT_THAT(zend, testing::Gt(zit));
-  EXPECT_THAT(zend, testing::Ge(zit));
-  EXPECT_THAT(zend, testing::Ge(zend));
+  EXPECT_THAT(zit, Eq(zip_iterator(is.begin(), ss.begin())));
+  EXPECT_THAT(zit, Ne(zend));
+  EXPECT_THAT(zit, Lt(zend));
+  EXPECT_THAT(zit, Le(zend));
+  EXPECT_THAT(zit, Le(zit));
+  EXPECT_THAT(zend, Gt(zit));
+  EXPECT_THAT(zend, Ge(zit));
+  EXPECT_THAT(zend, Ge(zend));
 }