浏览代码

refactor: implement stream::iterator using iterator::facade

Sam Jaffe 2 年之前
父节点
当前提交
42849290da

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "external/iterator"]
+	path = external/iterator
+	url = ssh://git@gogs.sjaffe.name:3000/sjjaffe/cpp-useful-iterators.git

+ 1 - 0
external/iterator

@@ -0,0 +1 @@
+Subproject commit f2cf5bee422d03fffd0621ae27927def2b9529b4

+ 4 - 8
include/stream/streams/source.hpp

@@ -28,12 +28,8 @@ namespace stream { namespace detail {
 
     explicit source_stream(C && cont) : source_(std::forward<C>(cont)) {}
 
-    iterator<reference> begin() {
-      return {source_iterator<_iterator>{source_.begin()}};
-    }
-    iterator<reference> end() {
-      return {source_iterator<_iterator>{source_.end()}};
-    }
+    iterator<reference> begin() { return {source_.begin()}; }
+    iterator<reference> end() { return {source_.end()}; }
 
   private:
     C source_;
@@ -46,8 +42,8 @@ namespace stream { namespace detail {
 
     explicit range_stream(It b, It e) : begin_(b), end_(e) {}
 
-    iterator<reference> begin() { return {source_iterator<It>{begin_}}; }
-    iterator<reference> end() { return {source_iterator<It>{end_}}; }
+    iterator<reference> begin() { return {begin_}; }
+    iterator<reference> end() { return {end_}; }
 
   private:
     It begin_, end_;

+ 156 - 188
include/stream/streams/streams.hpp

@@ -7,20 +7,15 @@
 #include <vector>
 
 #include "detail/ifd_pointer.hpp"
+#include <iterator/facade.h>
 
 namespace stream {
-  template <typename T> class iterator {
-  public:
-    using value_type = typename std::remove_reference<T>::type;
-    using reference = value_type &;
-    using pointer = value_type *;
-    using difference_type = std::ptrdiff_t;
-    using iterator_category = std::forward_iterator_tag;
-
+  template <typename T>
+  class iterator : public ::iterator::facade<iterator<T>> {
   private:
-    T (*dereference)(void *){nullptr};
-    bool (*compare)(void *, void *){nullptr};
-    void (*advance)(void *){nullptr};
+    T (*dereference_)(void *){nullptr};
+    bool (*equal_to_)(void *, void *){nullptr};
+    void (*increment_)(void *){nullptr};
     char const * type_{nullptr};
     detail::ifd_pointer impl_{};
 
@@ -29,193 +24,166 @@ namespace stream {
 
     template <typename Iter>
     iterator(Iter impl)
-        : dereference(&iterator::dereference_<Iter>),
-          compare(&iterator::compare_<Iter>),
-          advance(&iterator::advance_<Iter>), type_(typeid(Iter).name()),
-          impl_(std::forward<Iter>(impl)) {}
-
-    T operator*() const { return dereference(impl_.get()); }
-    iterator & operator++() {
-      advance(impl_.get());
-      return *this;
+        : dereference_([](void * p) -> T { return **((Iter *)p); }),
+          equal_to_(
+              [](void * l, void * r) { return *((Iter *)l) == *((Iter *)r); }),
+          increment_([](void * p) { ++(*(Iter *)(p)); }),
+          type_(typeid(Iter).name()), impl_(std::forward<Iter>(impl)) {}
+
+    T dereference() const { return dereference_(impl_.get()); }
+    void increment() { increment_(impl_.get()); }
+    bool equal_to(iterator other) const {
+      return equal_to_(impl_.get(), other.impl_.get());
     }
+  };
+}
+
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(::stream::iterator);
+
+namespace stream::detail {
+  template <typename T, typename = void> class stream_base_pointer_impl {};
 
-    bool operator==(iterator const & other) const {
-      if (strcmp(type_, other.type_)) { return false; }
-      return compare(impl_.get(), other.impl_.get());
+  template <typename T>
+  class stream_base_pointer_impl<
+      T, std::enable_if_t<traits::is_dereferencable_v<T>>> {
+  private:
+    using self_t = stream_base<T>;
+    using pointer = typename std::remove_reference<T>::type;
+    using element_type = typename std::pointer_traits<pointer>::element_type;
+
+  public:
+    auto deref() const & -> stream_base<element_type &> {
+      return static_cast<self_t const *>(this)->map(
+          [](T const & p) -> element_type & { return *p; });
     }
 
-    bool operator!=(iterator const & other) const {
-      if (strcmp(type_, other.type_)) { return false; }
-      return !compare(impl_.get(), other.impl_.get());
+    auto deref() && -> stream_base<element_type &> {
+      return static_cast<self_t &&>(*this).map(
+          [](T const & p) -> element_type & { return *p; });
     }
+  };
 
+  template <typename T> class stream_base : public stream_base_pointer_impl<T> {
   private:
-    friend void swap(iterator & lhs, iterator & rhs) {
-      using std::swap;
-      swap(lhs.dereference, rhs.dereference);
-      swap(lhs.compare, rhs.compare);
-      swap(lhs.advance, rhs.advance);
-      swap(lhs.type_, rhs.type_);
-      swap(lhs.impl_, rhs.impl_);
+    using value_type = typename std::decay<T>::type;
+
+  public:
+    template <typename Stream> stream_base(std::shared_ptr<Stream> && impl) {
+      do_begin = [](std::shared_ptr<void> p) -> iterator<T> {
+        return std::static_pointer_cast<Stream>(p)->begin();
+      };
+      do_end = [](std::shared_ptr<void> p) -> iterator<T> {
+        return std::static_pointer_cast<Stream>(p)->end();
+      };
+      impl_ = std::static_pointer_cast<void>(impl);
     }
 
-    template <typename It> static T dereference_(void * p) {
-      return **((It *)p);
+    ::stream::iterator<T> begin() const { return do_begin(impl_); }
+    ::stream::iterator<T> end() const { return do_end(impl_); }
+
+    bool empty() const { return begin() == end(); }
+
+    std::vector<value_type> collect() const {
+      std::vector<value_type> coll;
+      collect(coll);
+      return coll;
     }
-    template <typename It> static bool compare_(void * l, void * r) {
-      return *((It *)l) == *((It *)r);
+
+    template <typename C, typename = std::enable_if_t<
+                              !std::is_void_v<typename C::value_type>>>
+    C & collect(C & coll) const {
+      std::copy(begin(), end(), std::inserter(coll, coll.end()));
+      return coll;
+    }
+
+    std::optional<value_type> first() const {
+      return begin() != end() ? std::optional(*begin()) : std::nullopt;
+    }
+
+    template <typename F> bool none(F && pred) const {
+      return std::none_of(begin(), end(), pred);
+    }
+
+    template <typename F> bool all(F && pred) const {
+      return std::all_of(begin(), end(), pred);
+    }
+
+    template <typename F> bool any(F && pred) const {
+      return std::any_of(begin(), end(), pred);
+    }
+
+    template <typename F>
+    value_type accumulate(F && fold, value_type const & accum) const {
+      return std::accumulate(begin(), end(), accum, fold);
     }
-    template <typename It> static void advance_(void * p) { ++(*(It *)(p)); }
-  };
 
-  namespace detail {
-    template <typename T, typename = void> class stream_base_pointer_impl {};
-
-    template <typename T>
-    class stream_base_pointer_impl<
-        T, typename std::enable_if<traits::is_dereferencable<T>::value>::type> {
-    private:
-      using self_t = stream_base<T>;
-      using pointer = typename std::remove_reference<T>::type;
-      using element_type = typename std::pointer_traits<pointer>::element_type;
-
-    public:
-      auto deref() const & -> stream_base<element_type &> {
-        return static_cast<self_t const *>(this)->map(
-            [](T const & p) -> element_type & { return *p; });
-      }
-
-      auto deref() && -> stream_base<element_type &> {
-        return static_cast<self_t &&>(*this).map(
-            [](T const & p) -> element_type & { return *p; });
-      }
-    };
-
-    template <typename T>
-    class stream_base : public stream_base_pointer_impl<T> {
-    private:
-      using value_type = typename std::decay<T>::type;
-
-    public:
-      template <typename Stream> stream_base(std::shared_ptr<Stream> && impl) {
-        do_begin = [](std::shared_ptr<void> p) -> iterator<T> {
-          return std::static_pointer_cast<Stream>(p)->begin();
-        };
-        do_end = [](std::shared_ptr<void> p) -> iterator<T> {
-          return std::static_pointer_cast<Stream>(p)->end();
-        };
-        impl_ = std::static_pointer_cast<void>(impl);
-      }
-
-      ::stream::iterator<T> begin() const { return do_begin(impl_); }
-      ::stream::iterator<T> end() const { return do_end(impl_); }
-
-      bool empty() const { return begin() == end(); }
-
-      std::vector<value_type> collect() const {
-        std::vector<value_type> coll;
-        collect(coll);
-        return coll;
-      }
-
-      template <typename C, typename = std::enable_if_t<
-                                !std::is_void_v<typename C::value_type>>>
-      C & collect(C & coll) const {
-        std::copy(begin(), end(), std::inserter(coll, coll.end()));
-        return coll;
-      }
-
-      std::optional<value_type> first() const {
-        return begin() != end() ? std::optional(*begin()) : std::nullopt;
-      }
-
-      template <typename F> bool none(F && pred) const {
-        return std::none_of(begin(), end(), pred);
-      }
-
-      template <typename F> bool all(F && pred) const {
-        return std::all_of(begin(), end(), pred);
-      }
-
-      template <typename F> bool any(F && pred) const {
-        return std::any_of(begin(), end(), pred);
-      }
-
-      template <typename F>
-      value_type accumulate(F && fold, value_type const & accum) const {
-        return std::accumulate(begin(), end(), accum, fold);
-      }
-
-      template <typename F, typename = std::enable_if_t<
-                                std::is_invocable_v<F, value_type, value_type>>>
-      std::optional<value_type> accumulate(F && fold) const {
-        if (empty()) { return std::nullopt; }
-        value_type first = *begin();
-        return std::accumulate(++begin(), end(), first, fold);
-      }
-
-      value_type accumulate(value_type const & accum) const {
-        return std::accumulate(begin(), end(), accum);
-      }
-
-      template <typename F> void each(F && consumer) const {
-        std::for_each(begin(), end(), consumer);
-      }
-
-      template <typename F>
-      stream_base<traits::mapped_t<T, F>> map(F && func) const &;
-      template <typename F> stream_base<T> filter(F && func) const &;
-      template <typename F>
-      stream_base<traits::fmapped_t<T, F>> flatmap(F && func) const &;
-
-      auto keys() const & {
-        return map([](auto & kv) -> decltype(auto) { return kv.first; });
-      }
-
-      auto values() const & {
-        return map([](auto & kv) -> decltype(auto) { return kv.second; });
-      }
-
-      template <typename F>
-      stream_base<traits::mapped_t<T, F>> map(F && func) &&;
-      template <typename F> stream_base<T> filter(F && func) &&;
-      template <typename F>
-      stream_base<traits::fmapped_t<T, F>> flatmap(F && func) &&;
-
-      template <typename Cast> stream_base<Cast const &> cast() const & {
-        return map([](T const & p) -> Cast const & { return p; });
-      }
-
-      template <typename Cast> stream_base<Cast const &> cast() && {
-        return std::move(*this).map(
-            [](T const & p) -> Cast const & { return p; });
-      }
-
-      template <typename F, typename = traits::is_memvar_t<F>>
-      stream_base<traits::memvar_f<F>> map(F && memvar) const & {
-        return map(map_member_object<F>{memvar});
-      }
-
-      template <typename F, typename = traits::is_memfun_t<F>>
-      stream_base<traits::memfun_f<F>> map(F && memvar) const & {
-        return map(map_member_function<F>{memvar});
-      }
-
-      template <typename F, typename = traits::is_memvar_t<F>>
-      stream_base<traits::memvar_f<F>> map(F && memvar) && {
-        return std::move(*this).map(map_member_object<F>{memvar});
-      }
-
-      template <typename F, typename = traits::is_memfun_t<F>>
-      stream_base<traits::memfun_f<F>> map(F && memvar) && {
-        return std::move(*this).map(map_member_function<F>{memvar});
-      }
-
-    private:
-      iterator<T> (*do_begin)(std::shared_ptr<void>){nullptr};
-      iterator<T> (*do_end)(std::shared_ptr<void>){nullptr};
-      std::shared_ptr<void> impl_{nullptr};
-    };
-  }
+    template <typename F, typename = std::enable_if_t<
+                              std::is_invocable_v<F, value_type, value_type>>>
+    std::optional<value_type> accumulate(F && fold) const {
+      if (empty()) { return std::nullopt; }
+      value_type first = *begin();
+      return std::accumulate(++begin(), end(), first, fold);
+    }
+
+    value_type accumulate(value_type const & accum) const {
+      return std::accumulate(begin(), end(), accum);
+    }
+
+    template <typename F> void each(F && consumer) const {
+      std::for_each(begin(), end(), consumer);
+    }
+
+    template <typename F>
+    stream_base<traits::mapped_t<T, F>> map(F && func) const &;
+    template <typename F> stream_base<T> filter(F && func) const &;
+    template <typename F>
+    stream_base<traits::fmapped_t<T, F>> flatmap(F && func) const &;
+
+    auto keys() const & {
+      return map([](auto & kv) -> decltype(auto) { return kv.first; });
+    }
+
+    auto values() const & {
+      return map([](auto & kv) -> decltype(auto) { return kv.second; });
+    }
+
+    template <typename F> stream_base<traits::mapped_t<T, F>> map(F && func) &&;
+    template <typename F> stream_base<T> filter(F && func) &&;
+    template <typename F>
+    stream_base<traits::fmapped_t<T, F>> flatmap(F && func) &&;
+
+    template <typename Cast> stream_base<Cast const &> cast() const & {
+      return map([](T const & p) -> Cast const & { return p; });
+    }
+
+    template <typename Cast> stream_base<Cast const &> cast() && {
+      return std::move(*this).map(
+          [](T const & p) -> Cast const & { return p; });
+    }
+
+    template <typename F, typename = traits::is_memvar_t<F>>
+    stream_base<traits::memvar_f<F>> map(F && memvar) const & {
+      return map(map_member_object<F>{memvar});
+    }
+
+    template <typename F, typename = traits::is_memfun_t<F>>
+    stream_base<traits::memfun_f<F>> map(F && memvar) const & {
+      return map(map_member_function<F>{memvar});
+    }
+
+    template <typename F, typename = traits::is_memvar_t<F>>
+    stream_base<traits::memvar_f<F>> map(F && memvar) && {
+      return std::move(*this).map(map_member_object<F>{memvar});
+    }
+
+    template <typename F, typename = traits::is_memfun_t<F>>
+    stream_base<traits::memfun_f<F>> map(F && memvar) && {
+      return std::move(*this).map(map_member_function<F>{memvar});
+    }
+
+  private:
+    iterator<T> (*do_begin)(std::shared_ptr<void>){nullptr};
+    iterator<T> (*do_end)(std::shared_ptr<void>){nullptr};
+    std::shared_ptr<void> impl_{nullptr};
+  };
 }

+ 3 - 0
include/stream/streams/traits.hpp

@@ -75,6 +75,9 @@ namespace stream { namespace traits {
                                   decltype(*std::declval<T>())>::value>::type>
       : public std::true_type {};
 
+  template <typename T>
+  constexpr bool is_dereferencable_v = is_dereferencable<T>{};
+
   template <typename T, typename F>
   using mapped_t = decltype(std::declval<F>()(std::declval<T>()));
 

+ 63 - 2
stream.xcodeproj/project.pbxproj

@@ -14,6 +14,27 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+		CD52821429D3B705001A84DE /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD52820B29D3B705001A84DE /* iterator.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = CDCB3BC124E1D3880029B771;
+			remoteInfo = "iterator-test";
+		};
+		CD52821929D3B7B6001A84DE /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD52820B29D3B705001A84DE /* iterator.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = CDA2B60828581255004D5353;
+			remoteInfo = iterator;
+		};
+		CD52821C29D3B7B6001A84DE /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD52820B29D3B705001A84DE /* iterator.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = CDA2B60928581255004D5353;
+			remoteInfo = iterator;
+		};
 		CDEC1D6223514BB50091D9F2 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = CDEC1D5B23514BB50091D9F2 /* GoogleMock.xcodeproj */;
@@ -58,6 +79,7 @@
 		CD5281F629D3B173001A84DE /* source.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = source.hpp; sourceTree = "<group>"; };
 		CD5281F729D3B173001A84DE /* streams.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = streams.hpp; sourceTree = "<group>"; };
 		CD52820029D3B193001A84DE /* libstream.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libstream.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD52820B29D3B705001A84DE /* iterator.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = iterator.xcodeproj; path = external/iterator/iterator.xcodeproj; sourceTree = "<group>"; };
 		CD64CCB926232D6900770A30 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
 		CD9337281E3CD78B00699FF5 /* stream_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stream_test.cxx; sourceTree = "<group>"; };
 		CDAA170121A3A738007BBA11 /* stream */ = {isa = PBXFileReference; lastKnownFileType = folder; name = stream; path = include/stream; sourceTree = "<group>"; };
@@ -91,6 +113,7 @@
 			isa = PBXGroup;
 			children = (
 				CDEC1D5B23514BB50091D9F2 /* GoogleMock.xcodeproj */,
+				CD52820B29D3B705001A84DE /* iterator.xcodeproj */,
 				CDE8545E24DEBEBF006FE7C7 /* README.md */,
 				CDAA170121A3A738007BBA11 /* stream */,
 				CD5281E829D3B173001A84DE /* include */,
@@ -144,6 +167,15 @@
 			path = detail;
 			sourceTree = "<group>";
 		};
+		CD52820C29D3B705001A84DE /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD52821D29D3B7B6001A84DE /* libiterator.a */,
+				CD52821529D3B705001A84DE /* iterator-test.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		CD93372E1E3CD79E00699FF5 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -214,6 +246,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				CD52821A29D3B7B6001A84DE /* PBXTargetDependency */,
 			);
 			name = stream;
 			productName = stream;
@@ -271,6 +304,10 @@
 					ProductGroup = CDEC1D5C23514BB50091D9F2 /* Products */;
 					ProjectRef = CDEC1D5B23514BB50091D9F2 /* GoogleMock.xcodeproj */;
 				},
+				{
+					ProductGroup = CD52820C29D3B705001A84DE /* Products */;
+					ProjectRef = CD52820B29D3B705001A84DE /* iterator.xcodeproj */;
+				},
 			);
 			projectRoot = "";
 			targets = (
@@ -281,6 +318,20 @@
 /* End PBXProject section */
 
 /* Begin PBXReferenceProxy section */
+		CD52821529D3B705001A84DE /* iterator-test.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "iterator-test.xctest";
+			remoteRef = CD52821429D3B705001A84DE /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD52821D29D3B7B6001A84DE /* libiterator.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libiterator.a;
+			remoteRef = CD52821C29D3B7B6001A84DE /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		CDEC1D6323514BB50091D9F2 /* GoogleMock.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
@@ -340,6 +391,14 @@
 		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		CD52821A29D3B7B6001A84DE /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = iterator;
+			targetProxy = CD52821929D3B7B6001A84DE /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin XCBuildConfiguration section */
 		0E5DFDBB1BB4D3190063976E /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -376,7 +435,8 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = ./include/;
+				SYSTEM_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(TARGET_BUILD_DIR)/usr/local/include";
+				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include";
 			};
 			name = Debug;
 		};
@@ -413,7 +473,8 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = ./include/;
+				SYSTEM_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(TARGET_BUILD_DIR)/usr/local/include";
+				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include";
 			};
 			name = Release;
 		};