Преглед изворни кода

feat: add common_view

feat: add free-function distance/size/empty

refactor: replace iterator::sentinal with ranges::sentinal, because of a template deduction issue
Sam Jaffe пре 2 година
родитељ
комит
7e1e368606

+ 1 - 1
external/iterator

@@ -1 +1 @@
-Subproject commit 84d6c7f62399488cb8b02689c2a00294736feecb
+Subproject commit ef9f846c790f6538dbf6bec1b00a90a95da89206

+ 38 - 0
include/stream/common_view.h

@@ -0,0 +1,38 @@
+//
+//  common_view.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+#include <iterator/sentinal_iterator.h>
+#include <stream/forward.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename S> class common_view {
+private:
+  S stream_;
+
+public:
+  common_view(S && stream) : stream_(FWD(stream)) {}
+
+  auto begin() const { return iterator::sentinal_iterator(stream_.begin()); }
+  auto end() const { return decltype(begin())(); }
+  bool empty() const { return stream_.empty(); }
+  size_t size() const { return stream_.size(); }
+};
+}
+
+namespace stream::ranges::views {
+struct common {
+  template <typename Stream> friend auto operator|(Stream && stream, common) {
+    return common_view(FWD(stream));
+  }
+};
+}
+
+#undef FWD

+ 25 - 0
include/stream/detail/sentinal.h

@@ -0,0 +1,25 @@
+//
+//  sentinal.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+namespace stream::detail {
+struct sentinal_type {
+  template <typename It>
+  friend bool operator==(It const & iter, sentinal_type) {
+    return iter.at_end();
+  }
+  template <typename It>
+  friend bool operator!=(It const & iter, sentinal_type) {
+    return !iter.at_end();
+  }
+};
+}
+
+namespace stream::ranges {
+constexpr inline detail::sentinal_type sentinal;
+}

+ 33 - 1
include/stream/detail/traits.h

@@ -7,10 +7,42 @@
 
 #pragma once
 
+#include <type_traits>
 #include <utility>
 
+#include <iterator/detail/traits.h>
+#include <iterator/facade.h>
+
+#include <stream/forward.h>
+
+#define _val(type) std::declval<type>()
+#define exists(expr) std::void_t<decltype(expr)>
+
 namespace stream::traits {
-template <typename C> using ref_t = decltype(*std::begin(std::declval<C>()));
+template <typename C> using begin_t = decltype(std::begin(_val(C)));
+template <typename C> using end_t = decltype(std::end(_val(C)));
+
+template <typename C> using ref_t = decltype(*_val(begin_t<C>));
 template <typename C> using value_type = std::decay_t<ref_t<C>>;
 template <typename C> using cref_t = std::add_const_t<ref_t<C>>;
+
+template <typename, typename = void> struct has_size : std::false_type {};
+template <typename, typename = void> struct has_empty : std::false_type {};
+
+template <typename C>
+struct has_size<C, exists(_val(C).size())> : std::true_type {};
+template <typename C>
+struct has_empty<C, exists(_val(C).empty())> : std::true_type {};
+
+template <typename C> constexpr bool has_size_v = has_size<C>{};
+template <typename C> constexpr bool has_empty_v = has_empty<C>{};
+
+using iterator::detail::sentinal_type_t;
+template <typename S>
+constexpr bool is_sentinal_v =
+    std::is_void_v<sentinal_type_t<ranges::iter<S>>> &&
+    !std::is_same_v<begin_t<S>, end_t<S>>;
 }
+
+#undef _val
+#undef exists

+ 4 - 1
include/stream/filter_view.h

@@ -4,6 +4,9 @@
 
 #include <iterator/filter_iterator.hpp>
 
+#include <stream/detail/sentinal.h>
+#include <stream/detail/traits.h>
+
 #define FWD(x) std::forward<decltype(x)>(x)
 
 namespace stream::ranges {
@@ -22,7 +25,7 @@ public:
       : stream_(FWD(stream)), predicate_(predicate) {}
 
   auto begin() const { return iterator(predicate_, stream_); }
-  auto end() const { return iterator(); }
+  auto end() const { return sentinal; }
 };
 
 template <typename S, typename F> filter_view(S &&, F) -> filter_view<S>;

+ 1 - 1
include/stream/forward.h

@@ -1,13 +1,13 @@
 #pragma once
 
 #include <iterator/iterator_fwd.hpp>
-#include <stream/detail/traits.h>
 
 namespace stream::ranges {
 using ::iterator::end_aware_iterator;
 using ::iterator::facade;
 using ::iterator::iter;
 using ::iterator::proxy;
+
 template <typename> class view;
 }
 namespace stream::ranges::views {

+ 3 - 1
include/stream/join_view.h

@@ -2,6 +2,8 @@
 
 #include <iterator/join_iterator.hpp>
 
+#include <stream/detail/sentinal.h>
+
 #define FWD(x) std::forward<decltype(x)>(x)
 
 namespace stream::ranges {
@@ -16,7 +18,7 @@ public:
   join_view(S && stream) : stream_(FWD(stream)) {}
 
   auto begin() const { return iterator(stream_); }
-  auto end() const { return iterator(); }
+  auto end() const { return sentinal; }
   bool empty() const { return begin() == end(); }
 };
 }

+ 40 - 0
include/stream/size.h

@@ -0,0 +1,40 @@
+//
+//  size.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+#include <stream/detail/traits.h>
+
+namespace stream::ranges {
+template <typename It, typename S> std::ptrdiff_t distance(It it, S end) {
+  if constexpr (std::is_same_v<It, S>) {
+    return std::distance(it, end);
+  } else {
+    std::ptrdiff_t accum = 0;
+    while (it++ != end) {
+      ++accum;
+    }
+    return accum;
+  }
+}
+
+template <typename Stream> size_t size(Stream const & stream) {
+  if constexpr (traits::has_size_v<Stream>) {
+    return stream.size();
+  } else {
+    return distance(stream.begin(), stream.end());
+  }
+}
+
+template <typename Stream> bool empty(Stream const & stream) {
+  if constexpr (traits::has_empty_v<Stream>) {
+    return stream.empty();
+  } else {
+    return size(stream) == 0;
+  }
+}
+}

+ 2 - 0
include/stream/streams.hpp

@@ -9,6 +9,7 @@
 
 #include <stream/iota_view.h>
 
+#include <stream/common_view.h>
 #include <stream/filter_view.h>
 #include <stream/join_view.h>
 #include <stream/reference_view.h>
@@ -19,4 +20,5 @@
 
 #include <stream/accumulate.h>
 #include <stream/for_each.h>
+#include <stream/size.h>
 #include <stream/to.h>

+ 13 - 1
include/stream/to.h

@@ -11,6 +11,11 @@
 #include <set>
 #include <vector>
 
+#include <stream/common_view.h>
+#include <stream/forward.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
 namespace stream::ranges {
 template <typename C> class store {
 private:
@@ -27,7 +32,12 @@ public:
 
 template <template <typename...> class C> struct to_range {
   template <typename Stream> friend auto operator|(Stream && stream, to_range) {
-    return C(stream.begin(), stream.end());
+    if constexpr (traits::is_sentinal_v<Stream>) {
+      common_view common(FWD(stream));
+      return C(common.begin(), common.end());
+    } else {
+      return C(stream.begin(), stream.end());
+    }
   }
 };
 
@@ -36,3 +46,5 @@ auto to_vector() { return to<std::vector>(); }
 auto to_map() { return to<std::map>(); }
 auto to_set() { return to<std::set>(); }
 }
+
+#undef FWD

+ 9 - 1
include/stream/transform_view.h

@@ -4,6 +4,7 @@
 
 #include <iterator/proxy.h>
 #include <stream/detail/traits.h>
+#include <stream/forward.h>
 
 #define FWD(x) std::forward<decltype(x)>(x)
 
@@ -27,6 +28,7 @@ public:
 };
 
 template <typename S, typename Proj> class transform_view {
+private:
 private:
   S stream_;
   Proj projection_;
@@ -36,7 +38,13 @@ public:
       : stream_(FWD(stream)), projection_(projection) {}
 
   auto begin() const { return transform_iterator(stream_.begin(), invoke()); }
-  auto end() const { return transform_iterator(stream_.end(), invoke()); }
+  auto end() const {
+    if constexpr (traits::is_sentinal_v<S>) {
+      return iter<S>::sentinal;
+    } else {
+      return transform_iterator(stream_.end(), invoke());
+    }
+  }
   bool empty() const { return stream_.empty(); }
   size_t size() const { return stream_.size(); }
 

+ 6 - 0
stream.xcodeproj/project.pbxproj

@@ -86,6 +86,9 @@
 		CD52828029D51166001A84DE /* identity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = identity.h; sourceTree = "<group>"; };
 		CD52828129D5133C001A84DE /* tuple_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tuple_view.h; sourceTree = "<group>"; };
 		CD64CCB926232D6900770A30 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
+		CD6EBE1C29D5C61700F387C1 /* common_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common_view.h; sourceTree = "<group>"; };
+		CD6EBE2329D5CAD900F387C1 /* size.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = size.h; sourceTree = "<group>"; };
+		CD6EBE2429D5CE6500F387C1 /* sentinal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sentinal.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>"; };
 		CDE8545E24DEBEBF006FE7C7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@@ -145,11 +148,13 @@
 				CD52827929D4EF43001A84DE /* owning_view.h */,
 				CD5281F629D3B173001A84DE /* ref_view.h */,
 				CD52827A29D4EFBE001A84DE /* iota_view.h */,
+				CD6EBE1C29D5C61700F387C1 /* common_view.h */,
 				CD5281ED29D3B173001A84DE /* transform_view.h */,
 				CD52828129D5133C001A84DE /* tuple_view.h */,
 				CD5281F529D3B173001A84DE /* filter_view.h */,
 				CD5281EB29D3B173001A84DE /* join_view.h */,
 				CD52827E29D50BFA001A84DE /* reference_view.h */,
+				CD6EBE2329D5CAD900F387C1 /* size.h */,
 				CD52827D29D50081001A84DE /* for_each.h */,
 				CD52827C29D4FB11001A84DE /* to.h */,
 				CD52827F29D50E24001A84DE /* accumulate.h */,
@@ -165,6 +170,7 @@
 			children = (
 				CD52828029D51166001A84DE /* identity.h */,
 				CD5281EC29D3B173001A84DE /* traits.h */,
+				CD6EBE2429D5CE6500F387C1 /* sentinal.h */,
 			);
 			path = detail;
 			sourceTree = "<group>";

+ 18 - 18
test/stream_test.cxx

@@ -101,7 +101,7 @@ TEST(StreamTest, CanBuildFromSingleElement) {
   auto even = [](int i) { return i % 2 == 0; };
   auto s = views::single(value) | views::filter(even);
 
-  EXPECT_THAT(std::distance(s.begin(), s.end()), 0);
+  EXPECT_THAT(ranges::size(s), 0);
 }
 
 TEST(StreamTest, CanBuildFromIterators) {
@@ -131,23 +131,23 @@ TEST(StreamTest, CanFilterOutElements) {
   EXPECT_THAT(out, Eq(expected));
 }
 
-// TEST(StreamTest, AccumulateDefaultsToAdd) {
-//   std::vector<int> input{1, 2, 3, 4, 5};
-//   auto even = [](int i) { return i % 2 == 0; };
-//   auto s = input | views::filter(even) | ranges::to_vector();
-//
-//   EXPECT_THAT(s.accumulate(0), Eq(6));
-// }
-//
-// TEST(StreamTest, AccumulateCanTakeCustomAccumulator) {
-//   std::vector<int> input{1, 2, 3, 4, 5};
-//   auto even = [](int i) { return i % 2 == 0; };
-//   auto prod = [](int lhs, int rhs) { return lhs * rhs; };
-//   auto s = input | views::filter(even) | ranges::to_vector();
-//
-//   EXPECT_THAT(s.accumulate(prod, 0), Eq(0));
-//   EXPECT_THAT(s.accumulate(prod, 1), Eq(8));
-// }
+TEST(StreamTest, AccumulateDefaultsToAdd) {
+  std::vector<int> input{1, 2, 3, 4, 5};
+  auto even = [](int i) { return i % 2 == 0; };
+  auto s = input | views::filter(even) | ranges::to_vector();
+
+  EXPECT_THAT(ranges::fold_left(s, 0, std::plus<>()), Eq(6));
+}
+
+TEST(StreamTest, AccumulateCanTakeCustomAccumulator) {
+  std::vector<int> input{1, 2, 3, 4, 5};
+  auto even = [](int i) { return i % 2 == 0; };
+  auto prod = [](int lhs, int rhs) { return lhs * rhs; };
+  auto s = input | views::filter(even) | ranges::to_vector();
+
+  EXPECT_THAT(ranges::fold_left(s, 0, prod), Eq(0));
+  EXPECT_THAT(ranges::fold_left(s, 1, prod), Eq(8));
+}
 
 TEST(StreamTest, FlatmapJoinsIterableOutputs) {
   std::vector<int> vv{1, 2, 3, 4, 5};