Browse Source

feat: allow propagation of sentinels

Sam Jaffe 2 years ago
parent
commit
7b90d9df68

+ 11 - 11
include/iterator/detail/facade_traits.h

@@ -11,27 +11,25 @@
 #include <iterator>
 #include <type_traits>
 
+#include <iterator/detail/macro.h>
 #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 {};
+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 {};
+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 {};
+struct is_advanceable_iterator<T, EXISTS(VAL(T).advance({}))> : std::true_type {
+};
 
 template <typename, typename = void>
 struct is_single_pass_iterator : std::false_type {};
@@ -41,11 +39,11 @@ struct is_single_pass_iterator<T, std::void_t<typename T::single_pass_iterator>>
 
 template <typename, typename = void> struct has_increment : std::false_type {};
 template <typename T>
-struct has_increment<T, exists(_val(T).increment())> : std::true_type {};
+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 {};
+struct has_decrement<T, EXISTS(VAL(T).decrement())> : std::true_type {};
 
 template <typename, typename = void> struct distance_to {
   using type = std::ptrdiff_t;
@@ -53,7 +51,7 @@ template <typename, typename = void> struct distance_to {
 
 template <typename T>
 struct distance_to<T, std::enable_if_t<has_distance_to<T>{}>> {
-  using type = decltype(_val(T).distance_to(_val(T)));
+  using type = decltype(VAL(T).distance_to(VAL(T)));
 };
 
 template <typename T> using distance_to_t = typename distance_to<T>::type;
@@ -84,3 +82,5 @@ template <typename It> struct facade_category {
 template <typename It>
 using facade_category_t = typename facade_category<It>::type;
 }
+
+#include <iterator/detail/undef.h>

+ 20 - 0
include/iterator/detail/macro.h

@@ -0,0 +1,20 @@
+//
+//  macro.h
+//  iterator
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#ifndef _ITERATOR_MACRO_H
+#define _ITERATOR_MACRO_H
+
+#define SFINAE(trait, rval)                                                    \
+  template <bool _ = true> std::enable_if_t<trait<It> && _, rval>
+
+#define VAL(X) std::declval<X>()
+#define DEREF_TYPE(X) decltype(*VAL(X))
+#define TYPE(X, EXPR) decltype(VAL(X).EXPR)
+#define EXISTS(EXPR) std::void_t<decltype(EXPR)>
+
+#endif

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

@@ -34,6 +34,12 @@ 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 It, typename = void>
+struct has_sentinel_type : std::false_type {};
+template <typename It>
+struct has_sentinel_type<It, std::void_t<typename It::sentinel_type>>
+    : 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;
 
@@ -61,6 +67,9 @@ constexpr bool is_forward_v =
 
 template <typename Iter> constexpr bool is_single_pass_v = !is_forward_v<Iter>;
 
+template <typename It>
+constexpr bool has_sentinel_type_v = has_sentinel_type<It>{};
+
 template <typename It, typename S>
 using sentinel_type = std::conditional_t<std::is_same_v<It, S>, sentinel_t, S>;
 }

+ 18 - 0
include/iterator/detail/undef.h

@@ -0,0 +1,18 @@
+//
+//  undef.h
+//  iterator
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#ifdef _ITERATOR_MACRO_H
+#undef _ITERATOR_MACRO_H
+
+#undef SFINAE
+#undef EXISTS
+#undef TYPE
+#undef DEREF_TYPE
+#undef VAL
+
+#endif

+ 1 - 2
include/iterator/end_aware_iterator.h

@@ -48,8 +48,7 @@ public:
   }
   bool at_end() const { return curr_ == end_; }
   bool equal_to(end_aware_iterator const & other) const {
-    // TODO: This needs to exist to pass IterDistanceIsSumOfInnerContainerSizes?
-    return (at_end() && other.at_end()) || curr_ == other.curr_;
+    return curr_ == other.curr_;
   }
 
 private:

+ 6 - 2
include/iterator/facade.h

@@ -140,12 +140,14 @@ private:
 };
 }
 
+#include <iterator/detail/macro.h>
+
 // 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 reference = DEREF_TYPE(I);
   using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
-  using pointer = decltype(std::declval<I>().operator->());
+  using pointer = TYPE(I, operator->());
   using difference_type = ::iterator::detail::distance_to_t<I>;
   using iterator_category = ::iterator::detail::facade_category_t<I>;
 };
@@ -159,3 +161,5 @@ template <typename I> struct std::iterator_traits<::iterator::facade<I>> {
   template <typename... T>                                                     \
   struct std::iterator_traits<type<T...>>                                      \
       : std::iterator_traits<::iterator::facade<type<T...>>> {}
+
+#include <iterator/detail/undef.h>

+ 1 - 1
include/iterator/filter_iterator.h

@@ -28,7 +28,7 @@ public:
 public:
   filter_iterator() = default;
 
-  template <typename C>
+  template <typename C, typename = std::enable_if_t<detail::is_container_v<C>>>
   filter_iterator(predicate_t pred, C && c)
       : filter_iterator(std::move(pred), std::begin(c), std::end(c)) {}
 

+ 5 - 0
include/iterator/indexed_iterator.h

@@ -12,6 +12,8 @@
 #include <iterator/facade.h>
 #include <iterator/forwards.h>
 
+#include <iterator/detail/macro.h>
+
 namespace iterator {
 template <typename It>
 class indexed_iterator : public facade<indexed_iterator<It>> {
@@ -44,6 +46,9 @@ public:
   difference_type distance_to(indexed_iterator const & other) const {
     return other.base_ - base_;
   }
+  SFINAE(detail::has_sentinel_type_v, bool) at_end() const {
+    return base_ == typename It::sentinel_type();
+  }
 
 private:
   template <typename O> friend class indexed_iterator;

+ 6 - 2
include/iterator/join_iterator.h

@@ -15,10 +15,12 @@
 #include <iterator/facade.h>
 #include <iterator/forwards.h>
 
+#include <iterator/detail/macro.h>
+
 namespace iterator::joining {
 template <typename Iter, typename = void> class iterator {
 protected:
-  using inner_t = decltype(*std::declval<Iter>());
+  using inner_t = DEREF_TYPE(Iter);
 
 public:
   iterator() = default;
@@ -56,7 +58,7 @@ protected:
 template <typename Iter>
 class iterator<Iter, std::enable_if_t<detail::is_rvalue_iterator_v<Iter>>> {
 protected:
-  using inner_t = decltype(*std::declval<Iter>());
+  using inner_t = DEREF_TYPE(Iter);
 
 public:
   iterator() = default;
@@ -131,3 +133,5 @@ joining_iterator(end_aware_iterator<JI>) -> joining_iterator<JI>;
 }
 
 MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::joining_iterator);
+
+#include <iterator/detail/undef.h>

+ 37 - 17
include/iterator/proxy.h

@@ -3,53 +3,64 @@
 #include <iterator/facade.h>
 #include <iterator/forwards.h>
 
+#include <iterator/detail/macro.h>
+
 namespace iterator {
-template <typename Iter, typename Self>
-class proxy<Iter, Self, std::input_iterator_tag> : public facade<Self> {
+template <typename It, typename Self, typename Cat>
+class proxy : public facade<Self> {
 public:
   using single_pass_iterator = void;
 
 private:
-  Iter impl_;
+  It impl_;
 
 public:
-  proxy(Iter impl = {}) : impl_(impl) {}
+  proxy() = default;
+  proxy(It impl) : impl_(impl) {}
   template <typename... Args>
   proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
 
   decltype(auto) dereference() const { return *impl_; }
   void increment() { ++impl_; }
   bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+  SFINAE(detail::has_sentinel_type_v, bool) at_end() const {
+    return impl() == typename It::sentinel_type();
+  }
 
 protected:
   auto & impl() const { return impl_; }
 };
 
-template <typename Iter, typename Self>
-class proxy<Iter, Self, std::forward_iterator_tag> : public facade<Self> {
+template <typename It, typename Self>
+class proxy<It, Self, std::forward_iterator_tag> : public facade<Self> {
 private:
-  Iter impl_;
+  It impl_;
 
 public:
-  proxy(Iter impl = {}) : impl_(impl) {}
+  proxy() = default;
+  proxy(It impl) : impl_(impl) {}
   template <typename... Args>
   proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
 
   decltype(auto) dereference() const { return *impl_; }
   void increment() { ++impl_; }
   bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+  SFINAE(detail::has_sentinel_type_v, bool) at_end() const {
+    return impl() == typename It::sentinel_type();
+  }
 
 protected:
   auto & impl() const { return impl_; }
 };
 
-template <typename Iter, typename Self>
-class proxy<Iter, Self, std::bidirectional_iterator_tag> : public facade<Self> {
+template <typename It, typename Self>
+class proxy<It, Self, std::bidirectional_iterator_tag> : public facade<Self> {
 private:
-  Iter impl_;
+  It impl_;
 
 public:
-  proxy(Iter impl = {}) : impl_(impl) {}
+  proxy() = default;
+  proxy(It impl) : impl_(impl) {}
   template <typename... Args>
   proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
 
@@ -57,21 +68,25 @@ public:
   void increment() { ++impl_; }
   void decrement() { --impl_; }
   bool equal_to(Self const & other) const { return impl_ == other.impl_; }
+  SFINAE(detail::has_sentinel_type_v, bool) at_end() const {
+    return impl() == typename It::sentinel_type();
+  }
 
 protected:
   auto & impl() const { return impl_; }
 };
 
-template <typename Iter, typename Self>
-class proxy<Iter, Self, std::random_access_iterator_tag> : public facade<Self> {
+template <typename It, typename Self>
+class proxy<It, Self, std::random_access_iterator_tag> : public facade<Self> {
 public:
-  using difference_type = typename std::iterator_traits<Iter>::difference_type;
+  using difference_type = typename std::iterator_traits<It>::difference_type;
 
 private:
-  Iter impl_;
+  It impl_;
 
 public:
-  proxy(Iter impl = {}) : impl_(impl) {}
+  proxy() = default;
+  proxy(It impl) : impl_(impl) {}
   template <typename... Args>
   proxy(Args &&... args) : impl_(std::forward<Args>(args)...) {}
 
@@ -83,8 +98,13 @@ public:
   difference_type distance_to(Self const & other) const {
     return other.impl_ - impl_;
   }
+  SFINAE(detail::has_sentinel_type_v, bool) at_end() const {
+    return impl() == typename It::sentinel_type();
+  }
 
 protected:
   auto & impl() const { return impl_; }
 };
 }
+
+#include <iterator/detail/undef.h>

+ 7 - 3
include/iterator/recursive_iterator.h

@@ -16,6 +16,8 @@
 #include <iterator/facade.h>
 #include <iterator/forwards.h>
 
+#include <iterator/detail/macro.h>
+
 namespace iterator::recursive {
 template <size_t N, size_t I> struct bounded {
   template <typename It>
@@ -49,11 +51,11 @@ struct tuple<It, Bnd, recursion_type::END> {
 };
 
 template <typename... Ts>
-using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));
+using tuple_cat_t = decltype(std::tuple_cat(VAL(Ts)...));
 
 template <typename It, typename Bnd>
 struct tuple<It, Bnd, recursion_type::THRU> {
-  using next = decltype(std::begin(*std::declval<It>()));
+  using next = decltype(std::begin(*VAL(It)));
   using iter = tuple_cat_t<std::tuple<end_aware_iterator<It>>,
                            typename tuple<next, typename Bnd::next>::iter>;
   auto get(It) const { return std::make_tuple(); }
@@ -61,7 +63,7 @@ struct tuple<It, Bnd, recursion_type::THRU> {
 
 template <typename It, typename Bnd>
 struct tuple<It, Bnd, recursion_type::ASSOC> {
-  using next = decltype(std::begin(std::declval<It>()->second));
+  using next = decltype(std::begin(VAL(It)->second));
   using iter = tuple_cat_t<std::tuple<end_aware_iterator<It>>,
                            typename tuple<next, typename Bnd::next>::iter>;
   auto get(It iter) const { return std::tie(iter->first); };
@@ -203,3 +205,5 @@ auto make_recursive_iterator(C && collect) {
   return iterator::recursive_iterator_n<iterator::detail::iter<C>, Max>(
       collect);
 }
+
+#include <iterator/detail/undef.h>

+ 4 - 0
iterator.xcodeproj/project.pbxproj

@@ -65,6 +65,8 @@
 		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>"; };
+		CD5AEB3129D8885400A390A4 /* macro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = macro.h; sourceTree = "<group>"; };
+		CD5AEB3229D8886200A390A4 /* undef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = undef.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 /* forwards.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = forwards.h; sourceTree = "<group>"; };
@@ -191,6 +193,8 @@
 			children = (
 				CDA2B6182858128C004D5353 /* recursive_traits.h */,
 				CDA2B6192858128C004D5353 /* traits.h */,
+				CD5AEB3129D8885400A390A4 /* macro.h */,
+				CD5AEB3229D8886200A390A4 /* undef.h */,
 				CD5AEAE829D86DCD00A390A4 /* facade_traits.h */,
 				CDA2B61A2858128C004D5353 /* arrow_proxy.h */,
 			);

+ 1 - 1
test/indexed_iterator_test.cxx

@@ -47,7 +47,7 @@ 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);
+  EXPECT_EQ(end, iterator::sentinel);
 }
 
 TEST(IndexedIteratorTest, CanCompareIteratorOrder) {

+ 1 - 7
test/unkeyed_iterator_test.cxx

@@ -66,16 +66,10 @@ TEST(UnkeyedIteratorTest, EqualityIsPassthrough) {
               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, PropagatesAtEnd) {
   std::map<int, int> map{{1, 2}, {2, 3}};
   unkeyed_iterator end(end_aware_iterator(map.end(), map.end()));
-  EXPECT_THAT(decltype(end)(), end);
+  EXPECT_EQ(end, iterator::sentinel);
 }
 
 TEST(UnkeyedIteratorTest, CanPointToObject) {