Przeglądaj źródła

feat: add capture iterator to replace filter<It, Cache> concept

Sam Jaffe 2 lat temu
rodzic
commit
3522061acd

+ 53 - 0
include/iterator/capture_iterator.h

@@ -0,0 +1,53 @@
+//
+//  capture_iterator.h
+//  iterator
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <iterator/detail/facade_traits.h>
+#include <iterator/forwards.h>
+#include <iterator/proxy.h>
+
+#include <iterator/detail/macro.h>
+
+namespace iterator {
+
+template <typename It>
+class capture_iterator : public proxy<It, capture_iterator<It>> {
+public:
+  using super_t = proxy<It, capture_iterator<It>>;
+  using value_type = typename std::iterator_traits<It>::value_type;
+  using difference_type = typename std::iterator_traits<It>::difference_type;
+
+private:
+  value_type cache_;
+
+public:
+  capture_iterator() = default;
+  capture_iterator(It it) : super_t(it) { cache_ = super_t::dereference(); }
+
+  value_type const & dereference() const { return cache_; }
+  SFINAE(detail::has_increment_v<It>, void) increment() {
+    super_t::increment();
+    cache_ = super_t::dereference();
+  }
+  SFINAE(detail::has_decrement_v<It>, void) decrement() {
+    super_t::decrement();
+    cache_ = super_t::dereference();
+  }
+  SFINAE(detail::is_advanceable_iterator_v<It>, void)
+  advance(difference_type off) {
+    super_t::advance(off);
+    cache_ = super_t::dereference();
+  }
+};
+
+template <typename It> capture_iterator(It) -> capture_iterator<It>;
+
+}
+
+#include <iterator/detail/undef.h>

+ 1 - 1
include/iterator/facade.h

@@ -132,7 +132,7 @@ public:
     return (left - right) >= 0;
   }
 
-private:
+protected:
   self_type & self() { return *static_cast<self_type *>(this); }
   self_type const & self() const {
     return *static_cast<self_type const *>(this);

+ 4 - 12
include/iterator/filter_iterator.h

@@ -15,14 +15,12 @@
 #include <iterator/forwards.h>
 
 namespace iterator {
-template <typename Iter, bool Cache>
-class filter_iterator : public facade<filter_iterator<Iter, Cache>> {
+template <typename Iter>
+class filter_iterator : public facade<filter_iterator<Iter>> {
 public:
   using sentinel_type = sentinel_t;
 
   using reference = typename std::iterator_traits<Iter>::reference;
-  using cache_t = std::conditional_t<Cache, detail::value_proxy<reference>,
-                                     detail::ignore_proxy>;
   using predicate_t = std::function<bool(reference)>;
 
 public:
@@ -53,12 +51,9 @@ public:
   }
 
 public:
-  bool should_advance() {
-    return !base_.at_end() && !pred_(cache_ = dereference());
-  }
+  bool should_advance() { return !base_.at_end() && !pred_(dereference()); }
 
   end_aware_iterator<Iter> base_;
-  cache_t cache_;
   predicate_t pred_;
 };
 
@@ -70,7 +65,4 @@ template <typename P, typename It>
 filter_iterator(P, It, It) -> filter_iterator<It>;
 }
 
-template <typename It, bool B>
-struct std::iterator_traits<::iterator::filter_iterator<It, B>>
-    : std::iterator_traits<
-          ::iterator::facade<::iterator::filter_iterator<It, B>>> {};
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::filter_iterator);

+ 8 - 9
include/iterator/forwards.h

@@ -21,20 +21,19 @@ struct sentinel_t;
 
 // Iterator types
 template <typename> class end_aware_iterator;
-template <typename, bool = false> class filter_iterator;
+template <typename It, typename = typename It::sentinel_type>
+class sentinel_iterator;
+template <typename> class filter_iterator;
 template <typename> class joining_iterator;
 template <typename> class unkeyed_iterator;
 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>
+template <typename It> using recursive_iterator = recursive::rimpl<It>;
+template <typename It, std::size_t N>
+using recursive_iterator_n = recursive::rimpl<It, recursive::bounded<N>>;
 
-namespace iterator {
 template <typename> class facade;
-template <typename Iter, typename, typename = detail::category<Iter>>
+template <typename It, typename,
+          typename = typename std::iterator_traits<It>::iterator_category>
 class proxy;
 }

+ 7 - 27
include/iterator/sentinel_iterator.h

@@ -8,40 +8,20 @@
 
 #pragma once
 
-#include <iterator/detail/traits.h>
-#include <iterator/facade.h>
-#include <iterator/iterator_fwd.hpp>
+#include <iterator/forwards.h>
+#include <iterator/proxy.h>
 
 namespace iterator {
-template <typename It>
-class sentinel_iterator : public facade<sentinel_iterator<It>> {
+template <typename It, typename S>
+class sentinel_iterator : public proxy<It, sentinel_iterator<It, S>> {
 public:
+  using super_t = proxy<It, sentinel_iterator<It>>;
   using sentinel_type = sentinel_iterator;
 
 public:
-  sentinel_iterator() = default;
-  sentinel_iterator(It it) : curr_(it) {}
-
-  template <typename C, typename = std::enable_if_t<detail::is_container_v<C>>>
-  sentinel_iterator(C && container) : curr_(std::begin(container)) {
-    static_assert(std::is_reference_v<C>,
-                  "Cannot access iterator of a temporary");
-  }
-
-  decltype(auto) dereference() const { return *curr_; }
-  void increment() { ++curr_; }
-  bool at_end() const { return curr_.at_end() || curr_ == It(); }
-  bool equal_to(sentinel_iterator const & other) const {
-    // TODO: Fix this clause
-    return (at_end() && other.at_end()) || curr_ == other.curr_;
-  }
-
-private:
-  It curr_;
+  using super_t::super_t;
+  bool at_end() const { return super_t::impl() == S(); }
 };
-
-template <typename C> sentinel_iterator(C &&) -> sentinel_iterator<iter<C>>;
-template <typename It> sentinel_iterator(It) -> sentinel_iterator<It>;
 }
 
 MAKE_ITERATOR_FACADE_TYPEDEFS_T(::iterator::sentinel_iterator);

+ 7 - 0
iterator.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CD5AEB3529D897DD00A390A4 /* capture_iterator_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5AEB3429D897DD00A390A4 /* capture_iterator_test.cxx */; };
 		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 */; };
@@ -67,6 +68,8 @@
 		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>"; };
+		CD5AEB3329D8956600A390A4 /* capture_iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capture_iterator.h; sourceTree = "<group>"; };
+		CD5AEB3429D897DD00A390A4 /* capture_iterator_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = capture_iterator_test.cxx; 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>"; };
@@ -145,6 +148,7 @@
 			children = (
 				CD3C6DDB26238F8F00548B64 /* xcode_gtest_helper.h */,
 				CD5AEAE729D86D8100A390A4 /* ranges.h */,
+				CD5AEB3429D897DD00A390A4 /* capture_iterator_test.cxx */,
 				CDCB3BD224E1D5320029B771 /* end_aware_iterator_test.cxx */,
 				CDCB3BD324E1D5320029B771 /* filter_iterator_test.cxx */,
 				CDCB3BD424E1D5320029B771 /* indexed_iterator_test.cxx */,
@@ -176,11 +180,13 @@
 				CDA2B61C2858128C004D5353 /* proxy.h */,
 				CD5A8E7329D7910D008C2A4F /* sentinel.h */,
 				CDA2B6172858128C004D5353 /* detail */,
+				CD5AEB3329D8956600A390A4 /* capture_iterator.h */,
 				CDA2B61B2858128C004D5353 /* end_aware_iterator.h */,
 				CDA2B61E2858128C004D5353 /* filter_iterator.h */,
 				CDA2B6142858128C004D5353 /* indexed_iterator.h */,
 				CDA2B61F2858128C004D5353 /* join_iterator.h */,
 				CDA2B6162858128C004D5353 /* recursive_iterator.h */,
+				CD6EBE2229D5C93A00F387C1 /* sentinel_iterator.h */,
 				CDA2B6152858128C004D5353 /* unkeyed_iterator.h */,
 				CDA2B61D2858128C004D5353 /* zip_iterator.h */,
 			);
@@ -383,6 +389,7 @@
 				CDCB3BDA24E1D5320029B771 /* recursive_iterator_mixed_container_test.cxx in Sources */,
 				CDCB3C0324E33A1E0029B771 /* zip_iterator_test.cxx in Sources */,
 				CDCB3BD924E1D5320029B771 /* recursive_iterator_accessors_test.cxx in Sources */,
+				CD5AEB3529D897DD00A390A4 /* capture_iterator_test.cxx in Sources */,
 				CDCB3BFC24E327CF0029B771 /* recursive_iterator_single_level_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 43 - 0
test/capture_iterator_test.cxx

@@ -0,0 +1,43 @@
+//
+//  capture_iterator_test.cxx
+//  iterator-test
+//
+//  Created by Sam Jaffe on 4/1/23.
+//  Copyright © 2023 Sam Jaffe. All rights reserved.
+//
+
+#include "iterator/capture_iterator.h"
+
+#include "ranges.h"
+#include "xcode_gtest_helper.h"
+
+using iterator::capture_iterator;
+
+using testing::Address;
+using testing::ElementsAre;
+using testing::Not;
+
+struct fake_iterator {
+  using value_type = std::vector<int>;
+  using reference = std::vector<int>;
+  using pointer = void;
+  using difference_type = std::ptrdiff_t;
+  using iterator_category = std::output_iterator_tag;
+
+  std::vector<int> operator*() const { return {0, 1, 2}; }
+  void operator++() {}
+  friend bool operator==(fake_iterator, fake_iterator) { return true; }
+};
+
+TEST(CaptureIterator, UnderlyingReturnsUnique) {
+  auto it = fake_iterator();
+  auto const & result = *it;
+  EXPECT_THAT(*it, Not(Address(&result)));
+}
+
+TEST(CaptureIterator, CachesValue) {
+  auto it = capture_iterator(fake_iterator());
+
+  auto const & result = *it;
+  EXPECT_THAT(*it, Address(&result));
+}

+ 0 - 8
test/end_aware_iterator_test.cxx

@@ -40,14 +40,6 @@ TEST(EndAwareIterator, EmptyIteratorIsEnd) {
   EXPECT_TRUE(end_aware_iterator<std::vector<int>::iterator>().at_end());
 }
 
-TEST(EndAwareIterator, AllEndPointsAreEqual) {
-  std::vector<int> v1{1, 2, 3, 4, 5};
-  std::vector<int> v2{8, 9, 10};
-
-  EXPECT_EQ(end_aware_iterator(v1.end(), v1.end()),
-            end_aware_iterator(v2.end(), v2.end()));
-}
-
 TEST(EndAwareIterator, PreIncrementAdvancesIterator) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator eai(v.begin(), v.end());

+ 1 - 1
test/filter_iterator_test.cxx

@@ -62,5 +62,5 @@ TEST(FilterIterator, CanIterateWithSentinel) {
     ++count;
   }
 
-  EXPECT_EQ(count, 5);
+  EXPECT_EQ(count, 2);
 }

+ 4 - 0
test/xcode_gtest_helper.h

@@ -22,6 +22,10 @@
 #if defined(TARGET_OS_OSX)
 // This is a hack to allow XCode to properly display failures when running
 // unit tests.
+#undef EXPECT_EQ
+#define EXPECT_EQ ASSERT_EQ
+#undef EXPECT_NE
+#define EXPECT_NE ASSERT_NE
 #undef EXPECT_THAT
 #define EXPECT_THAT ASSERT_THAT
 #undef EXPECT_THROW