Browse Source

Improving fluent stream code to actually be fluent, as opposed to even more verbose than the normal function-call version.

Samuel Jaffe 8 years ago
parent
commit
9b80cc3271
2 changed files with 39 additions and 50 deletions
  1. 7 7
      stream_fluent.t.h
  2. 32 43
      streams/fluent.hpp

+ 7 - 7
stream_fluent.t.h

@@ -33,7 +33,7 @@ public:
   void test_map_identity() {
     vec_t v{1, 2, 3, 4, 5};
     auto identity = [](int_t i) { return i; };
-    auto s = stream::make_stream(v) | stream::map<int, int>( identity );
+    auto s = stream::make_stream(v) | identity;
     vec_t o{s.begin(), s.end()};
     TS_ASSERT_EQUALS(v, o);
   }
@@ -42,7 +42,7 @@ public:
     vec_t v{1, 2, 3, 4, 5};
     vec_t expected{3, 5, 7, 9, 11};
     auto fmap = [](int_t i) { return 2*i+1; };
-    auto s = stream::make_stream(v) | stream::map<int, int>( fmap );
+    auto s = stream::make_stream(v) | fmap;
     vec_t o{s.begin(), s.end()};
     TS_ASSERT_EQUALS(expected, o);
   }
@@ -50,7 +50,7 @@ public:
   void test_filter_noop() {
     vec_t v{1, 2, 3, 4, 5};
     auto pass = [](int_t i) { return true; };
-    auto s = stream::make_stream(v) | stream::filter<int>( pass );
+    auto s = stream::make_stream(v) | pass;
     vec_t o{s.begin(), s.end()};
     TS_ASSERT_EQUALS(v, o);
   }
@@ -59,7 +59,7 @@ public:
     vec_t v{1, 2, 3, 4, 5};
     vec_t expected{2, 4};
     auto even = [](int_t i) { return i%2 == 0; };
-    auto s = stream::make_stream(v) | stream::filter<int>( even );
+    auto s = stream::make_stream(v) | even;
     vec_t o{s.begin(), s.end()};
     TS_ASSERT_EQUALS(expected, o);
   }
@@ -68,15 +68,15 @@ public:
     vec_t v{1, 2, 3, 4, 5};
     auto even = [](int_t i) { return i%2 == 0; };
     auto sum =[](int_t lhs, int_t rhs) { return lhs + rhs; };
-    auto s = stream::make_stream(v) | stream::filter<int>( even );
-    TS_ASSERT_EQUALS( 6 , s > stream::fold_left<int>(sum) );
+    auto s = stream::make_stream(v) | even;
+    TS_ASSERT_EQUALS( 6 , s > sum );
   }
   
   void test_flatmap_joins_lists() {
     vec_t vv{1, 2, 3, 4, 5};
     auto next3 = [](int_t i) { return vec_t{i, i+1, i+2}; };
     vec_t expected{1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
-    auto s = stream::make_stream(vv) | stream::flatmap<int, vec_t>( next3 );
+    auto s = stream::make_stream(vv) || next3;
     vec_t o{s.begin(), s.end()};
     TS_ASSERT_EQUALS(expected, o);
   }

+ 32 - 43
streams/fluent.hpp

@@ -9,63 +9,52 @@
 
 #include "../streams.hpp"
 
-#include <numeric>
-
-namespace stream {
-  template <typename T, typename R>
-  struct map {
-    template <typename F>
-    explicit map(F&& f) : func(f) {}
-    std::function<R(const T&)> func;
-  };
+namespace stream { namespace traits {
+  template <typename T, typename P>
+  using is_filter = std::is_same<bool, decltype(std::declval<P>()(std::declval<T>()))>;
   
-  template <typename T, typename R>
-  struct flatmap {
-    template <typename F>
-    explicit flatmap(F&& f) : func(f) {}
-    std::function<R(const T&)> func;
-  };
+  template <typename C, typename T, typename = void>
+  struct is_collection : public std::false_type {};
   
-  template <typename T>
-  struct filter {
-    template <typename F>
-    explicit filter(F&& f) : pred(f) {}
-    std::function<bool(const T&)> pred;
-  };
-
-  template <typename L, typename R = L>
-  struct fold_left {
-    template <typename F>
-    explicit fold_left(F&& f, L const & i = L()) : init(i), fold(f) {}
-    L init;
-    std::function<L(const L&, const R&)> fold;
-  };
-}
+  template <typename C, typename T>
+  struct is_collection<C, T, typename std::enable_if<std::is_constructible<typename C::value_type, T>::value>::type> : public std::true_type {};
+} }
 
 namespace stream { namespace detail {
-  template <typename T, typename R>
-  stream_base<R> operator|(stream_base<T> const&s, ::stream::map<typename std::decay<T>::type, R>&& f) {
-    return s.map(f.func);
+  template <typename T, typename F>
+  auto operator||(stream_base<T> const&s, F && f) -> decltype(s.flatmap(f)) {
+    return s.flatmap(f);
+  }
+  
+  template <typename T, typename F, typename = typename std::enable_if<!traits::is_filter<T, F>::value>::type>
+  auto operator|(stream_base<T> const&s, F && f) -> decltype(s.map(f)) {
+    return s.map(f);
   }
   
-  template <typename T, typename C>
-  stream_base<typename C::value_type> operator|(stream_base<T> const&s, ::stream::flatmap<typename std::decay<T>::type, C>&& f) {
-    return s.flatmap(f.func);
+  template <typename T, typename P, typename = typename std::enable_if<traits::is_filter<T, P>::value>::type>
+  stream_base<T> operator|(stream_base<T> const&s, P && f) {
+    return s.filter(f);
   }
   
-  template <typename T>
-  stream_base<T> operator|(stream_base<T> const&s, ::stream::filter<typename std::decay<T>::type>&& f) {
-    return s.filter(f.pred);
+  template <typename R>
+  R operator >(stream_base<R> const &s, R const & val) {
+    return s.accumulate(val);
   }
 
-  template <typename L, typename R>
-  L operator >(stream_base<R> const &s, ::stream::fold_left<L, typename std::decay<R>::type> && f) {
-    return std::accumulate(s.begin(), s.end(), f.init, f.fold);
+  template <typename L, typename R, typename F>
+  L operator >(stream_base<R> const &s, std::pair<L, F> const & pair) {
+    return s.accumulate(pair.second, pair.first);
+  }
+  
+  template <typename T, typename F, typename = typename std::enable_if<!traits::is_collection<F, T>::value>::type>
+  typename std::decay<T>::type operator >(stream_base<T> const &s, F && f) {
+    return s.accumulate(f, typename std::decay<T>::type());
   }
   
-  template <typename T, typename C>
+  template <typename T, typename C, typename = typename std::enable_if<traits::is_collection<C, T>::value>::type>
   C & operator >(stream_base<T> const & s, C & c) {
     s.collect(c);
     return c;
   }
 } }
+