瀏覽代碼

Complete overhaul to begin mimicing std::ranges

Sam Jaffe 2 年之前
父節點
當前提交
d7fbf79638

+ 86 - 0
include/stream/accumulate.h

@@ -0,0 +1,86 @@
+//
+//  accumulate.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <optional>
+
+#include <stream/detail/identity.h>
+#include <stream/detail/traits.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename It, typename S, typename T, typename F>
+auto fold_left(It it, S end, T init, F reduce) {
+  for (; it != end; ++it) {
+    init = reduce(init, *it);
+  }
+  return init;
+}
+
+template <typename Stream, typename T, typename F>
+auto fold_left(Stream && stream, T init, F reduce) {
+  for (auto && elem : FWD(stream)) {
+    init = reduce(init, FWD(elem));
+  }
+  return init;
+}
+
+template <typename Stream, typename F>
+auto fold_left_with_first(Stream && stream, F reduce)
+    -> std::optional<traits::value_type<Stream>> {
+  if (stream.empty()) { return std::nullopt; }
+  return fold_left(++stream.begin(), stream.end(), *stream.begin(), reduce);
+}
+
+template <typename Stream>
+auto first(Stream && stream) -> std::optional<traits::value_type<Stream>> {
+  return stream.empty() ? std::nullopt : std::optional(*stream.begin());
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool any_of(It it, S end, Pred pred, Proj proj = {}) {
+  for (; it != end; ++it) {
+    if (pred(proj(*it))) { return true; }
+  }
+  return false;
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool any_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return any_of(stream.begin(), stream.end(), pred, proj);
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool none_of(It it, S end, Pred pred, Proj proj = {}) {
+  return !any_of(it, end, pred, proj);
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool none_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return none_of(stream.begin(), stream.end(), pred, proj);
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool all_of(It it, S end, Pred pred, Proj proj = {}) {
+  for (; it != end; ++it) {
+    if (!pred(proj(*it))) { return false; }
+  }
+  return true;
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool all_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return all_of(stream.begin(), stream.end(), pred, proj);
+}
+}
+
+#undef FWD

+ 14 - 0
include/stream/detail/identity.h

@@ -0,0 +1,14 @@
+//
+//  identity.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+namespace stream::detail {
+struct identity {
+  template <typename T> decltype(auto) operator()(T && t) const { return t; }
+};
+}

+ 0 - 53
include/stream/detail/ifd_pointer.hpp

@@ -1,53 +0,0 @@
-#pragma once
-
-namespace stream::detail {
-class ifd_pointer {
-private:
-  void * (*copy)(void *){nullptr};
-  void (*destroy)(void *){nullptr};
-  void * ptr{nullptr};
-
-public:
-  template <typename T>
-  ifd_pointer(T && value)
-      : copy(&ifd_pointer::copy_<T>), destroy(&ifd_pointer::destroy_<T>),
-        ptr(copy(&value)) {}
-
-  ifd_pointer() = default;
-  ifd_pointer(ifd_pointer const & other)
-      : copy(other.copy), destroy(other.destroy),
-        ptr(copy ? copy(other.ptr) : nullptr) {}
-  ifd_pointer(ifd_pointer && other) {
-    *this = std::forward<ifd_pointer>(other);
-  }
-  ifd_pointer & operator=(ifd_pointer const & other) {
-    return *this = ifd_pointer(other);
-  }
-  ~ifd_pointer() {
-    if (destroy) destroy(ptr);
-  }
-
-  ifd_pointer & operator=(ifd_pointer && other) {
-    swap(*this, other);
-    return *this;
-  }
-
-  void * get() const { return ptr; }
-  operator bool() const { return ptr; }
-  friend bool operator==(ifd_pointer const & lhs, ifd_pointer const & rhs) {
-    return lhs.ptr == rhs.ptr;
-  }
-
-private:
-  friend void swap(ifd_pointer & lhs, ifd_pointer & rhs) {
-    using ::std::swap;
-    swap(lhs.copy, rhs.copy);
-    swap(lhs.destroy, rhs.destroy);
-    swap(lhs.ptr, rhs.ptr);
-  }
-  template <typename T> static void * copy_(void * p) {
-    return (void *)new T(*(T *)(p));
-  }
-  template <typename T> static void destroy_(void * p) { delete (T *)(p); }
-};
-}

+ 16 - 0
include/stream/detail/traits.h

@@ -0,0 +1,16 @@
+//
+//  traits.hpp
+//  stream
+//
+//  Created by Sam Jaffe on 6/24/17.
+//
+
+#pragma once
+
+#include <utility>
+
+namespace stream::traits {
+template <typename C> using ref_t = decltype(*std::begin(std::declval<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>>;
+}

+ 21 - 0
include/stream/empty_view.h

@@ -0,0 +1,21 @@
+//
+//  empty.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+namespace stream::ranges {
+template <typename T> struct empty_view {
+  static T * begin() { return nullptr; }
+  static T * end() { return nullptr; }
+  static bool empty() { return true; }
+  static size_t size() { return 0; }
+};
+}
+
+namespace stream::ranges::views {
+template <typename T> inline constexpr empty_view<T> empty;
+}

+ 0 - 36
include/stream/filter.hpp

@@ -1,36 +0,0 @@
-#pragma once
-
-#include <stream/iterator.hpp>
-
-#include <iterator/filter_iterator.hpp>
-
-namespace stream::detail {
-template <typename T> class filter_stream {
-public:
-  using iter_type = ::iterator::filter_iterator<iterator<T>, true>;
-
-public:
-  template <typename F>
-  filter_stream(F && func, stream_base<T> const & sb)
-      : pred_(func), source_(sb) {}
-
-  iterator<T> begin() { return iter_type(pred_, source_); }
-  iterator<T> end() { return iter_type(); }
-
-private:
-  typename iter_type::predicate_t pred_;
-  stream_base<T> source_;
-};
-
-template <typename T>
-template <typename F>
-auto stream_base<T>::filter(F && pred) && -> stream_base<T> {
-  return std::make_shared<filter_stream<T>>(pred, std::move(*this));
-}
-
-template <typename T>
-template <typename F>
-auto stream_base<T>::filter(F && pred) const & -> stream_base<T> {
-  return std::make_shared<filter_stream<T>>(pred, *this);
-}
-}

+ 48 - 0
include/stream/filter_view.h

@@ -0,0 +1,48 @@
+#pragma once
+
+#include <functional>
+
+#include <iterator/filter_iterator.hpp>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename S> class filter_view {
+private:
+  using Pred = std::function<bool(traits::cref_t<S>)>;
+  using iterator = ::iterator::filter_iterator<iter<S>, true>;
+
+private:
+  S stream_;
+  Pred predicate_;
+
+public:
+  template <typename F>
+  filter_view(S && stream, F predicate)
+      : stream_(FWD(stream)), predicate_(predicate) {}
+
+  auto begin() const { return iterator(predicate_, stream_); }
+  auto end() const { return iterator(); }
+};
+
+template <typename S, typename F> filter_view(S &&, F) -> filter_view<S>;
+}
+
+namespace stream::ranges::views {
+template <typename Pred> class filter {
+public:
+  filter(Pred const & predicate) : predicate_(predicate) {}
+  filter(Pred & predicate) : predicate_(std::move(predicate)) {}
+
+  template <typename Stream>
+  friend auto operator|(Stream && stream, filter filt) {
+    // TODO: if constexpr filter_view
+    return filter_view(FWD(stream), std::move(filt.predicate_));
+  }
+
+private:
+  Pred predicate_;
+};
+}
+
+#undef FWD

+ 0 - 68
include/stream/fluent.hpp

@@ -1,68 +0,0 @@
-//
-//  fluent.hpp
-//  stream
-//
-//  Created by Sam Jaffe on 1/28/17.
-//
-
-#pragma once
-
-#include "streams.hpp"
-
-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 C, typename T, typename = void>
-  struct is_collection : public std::false_type {};
-
-  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 F>
-  auto operator||(stream_base<T> const & s, F && f) -> decltype(s.flatmap(f)) {
-    return s.flatmap(f);
-  }
-
-  template <typename T, typename F>
-  auto operator|(stream_base<T> const & s, F && f) -> decltype(s.map(f)) {
-    return s.map(f);
-  }
-
-  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 R> R operator>(stream_base<R> const & s, R const & val) {
-    return s.accumulate(val);
-  }
-
-  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,
-            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;
-  }
-}}

+ 35 - 0
include/stream/for_each.h

@@ -0,0 +1,35 @@
+//
+//  for_each.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename S, typename F> void for_each(S && stream, F func) {
+  for (auto && elem : FWD(stream)) {
+    func(FWD(elem));
+  }
+}
+}
+
+namespace stream::ranges::views {
+template <typename F> class for_each {
+private:
+  F operation_;
+
+public:
+  for_each(F operation) : operation_(operation) {}
+
+  template <typename Stream>
+  friend void operator|(Stream && stream, for_each const & each) {
+    ranges::for_each(FWD(stream), each.operation_);
+  }
+};
+}
+
+#undef FWD

+ 17 - 0
include/stream/forward.h

@@ -0,0 +1,17 @@
+#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 {
+}
+namespace stream::views {
+using namespace stream::ranges::views;
+}

+ 0 - 17
include/stream/forward.hpp

@@ -1,17 +0,0 @@
-#pragma once
-
-#include "traits.hpp"
-
-namespace stream::detail {
-template <typename> class stream_base;
-template <typename> class iterator_impl;
-}
-
-namespace stream {
-template <typename> class iterator;
-template <typename, typename> struct map_t;
-template <typename> struct filter_t;
-template <typename, typename> struct flatmap_t;
-template <typename T>
-using stream = detail::stream_base<T>; // Should I override instead?
-}

+ 62 - 0
include/stream/iota_view.h

@@ -0,0 +1,62 @@
+//
+//  iota_view.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <iterator/facade.h>
+
+namespace stream::ranges {
+template <typename T, typename Bound> struct iota_view {
+public:
+  constexpr static inline bool has_size_v = std::is_constructible_v<T, Bound>;
+
+  class iterator : facade<iterator> {
+  public:
+    using sentinal_type = Bound;
+
+    iterator(T value, Bound bound) : value_(value), bound_(bound) {}
+
+    T const & dereference() const { return value_; }
+    void increment() const { ++value_; }
+    bool at_end() const { return value_ == bound_; }
+    std::ptrdiff_t distance_to(iterator const & other) const {
+      return other.value_ - value_;
+    }
+
+  private:
+    T value_;
+    Bound bound_;
+  };
+
+private:
+  T value_;
+  Bound bound_;
+
+public:
+  iota_view(T value, Bound bound) : value_(value), bound_(bound) {}
+
+  auto begin() const { return iterator(value_, bound_); }
+  auto end() const {
+    if constexpr (has_size_v) {
+      return iterator(bound_, bound_);
+    } else {
+      return bound_;
+    }
+  }
+
+  bool empty() const { return begin().at_end(); }
+  auto size() const -> std::enable_if_t<has_size_v, size_t> {
+    return begin().distance_to(end());
+  }
+};
+}
+
+namespace stream::ranges::views {
+template <typename T, typename Bound> auto iota(T value, Bound bound) {
+  return iota_view(value, bound);
+}
+}

+ 0 - 45
include/stream/iterator.hpp

@@ -1,45 +0,0 @@
-//
-//  iterator.hpp
-//  stream
-//
-//  Created by Sam Jaffe on 3/28/23.
-//
-
-#pragma once
-#include "detail/ifd_pointer.hpp"
-
-#include <iterator/facade.h>
-
-namespace stream {
-template <typename T> class iterator : public ::iterator::facade<iterator<T>> {
-private:
-  T (*dereference_)(void *){nullptr};
-  bool (*equal_to_)(void *, void *){nullptr};
-  void (*increment_)(void *){nullptr};
-  char const * type_{nullptr};
-  detail::ifd_pointer impl_{};
-
-public:
-  iterator() = default;
-
-  template <typename Iter>
-  iterator(Iter impl)
-      : 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 const & other) const {
-    return impl_ == other.impl_ ||
-           (impl_ && other.impl_ && equal_to_(impl_.get(), other.impl_.get()));
-  }
-};
-
-template <typename Iter>
-iterator(Iter) -> iterator<typename std::iterator_traits<Iter>::reference>;
-}
-
-MAKE_ITERATOR_FACADE_TYPEDEFS_T(::stream::iterator);

+ 0 - 40
include/stream/join.hpp

@@ -1,40 +0,0 @@
-#pragma once
-
-#include <iterator>
-
-#include <iterator/join_iterator.hpp>
-#include <stream/iterator.hpp>
-
-namespace stream::detail {
-template <typename C> class join_stream {
-public:
-  using iter_type = ::iterator::joining_iterator<iterator<C>>;
-
-  using T = typename C::value_type;
-  explicit join_stream(stream_base<C> const & sb) : source_(sb) {}
-
-  iterator<T> begin() { return iter_type(source_); }
-  iterator<T> end() { return iter_type(); }
-
-private:
-  stream_base<C> source_;
-};
-
-template <typename C>
-stream_base<typename C::value_type> make_join(stream_base<C> const & sb) {
-  return std::make_shared<join_stream<C>>(sb);
-}
-
-template <typename T>
-template <typename F>
-stream_base<traits::fmapped_t<T, F>> stream_base<T>::flatmap(F && func) && {
-  return make_join(std::move(*this).map(func));
-}
-
-template <typename T>
-template <typename F>
-stream_base<traits::fmapped_t<T, F>>
-stream_base<T>::flatmap(F && func) const & {
-  return make_join(map(func));
-}
-}

+ 32 - 0
include/stream/join_view.h

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <iterator/join_iterator.hpp>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename S> struct join_view {
+private:
+  using iterator = ::iterator::joining_iterator<iter<S>>;
+
+private:
+  S stream_;
+
+public:
+  join_view(S && stream) : stream_(FWD(stream)) {}
+
+  auto begin() const { return iterator(stream_); }
+  auto end() const { return iterator(); }
+  bool empty() const { return begin() == end(); }
+};
+}
+
+namespace stream::ranges::views {
+struct join {
+  template <typename Stream> friend auto operator|(Stream && stream, join) {
+    return join_view(FWD(stream));
+  }
+};
+}
+
+#undef FWD

+ 0 - 48
include/stream/map.hpp

@@ -1,48 +0,0 @@
-#pragma once
-
-#include <iterator/proxy.h>
-
-namespace stream::detail {
-template <typename T, typename R>
-class map_iterator : public ::iterator::proxy<iterator<T>, map_iterator<T, R>> {
-public:
-  using projection_t = std::function<R(T const &)>;
-  using super_t = ::iterator::proxy<iterator<T>, map_iterator<T, R>>;
-
-public:
-  map_iterator(projection_t proj, iterator<T> && impl)
-      : proj_(std::move(proj)), super_t(std::move(impl)) {}
-
-  R dereference() const { return proj_(super_t::dereference()); }
-
-private:
-  std::function<R(T const &)> proj_;
-};
-
-template <typename T, typename R> class map_stream {
-public:
-  template <typename F>
-  map_stream(F && func, stream_base<T> const & sb) : fun_(func), source_(sb) {}
-
-  iterator<R> begin() { return {map_iterator<T, R>{fun_, source_.begin()}}; }
-
-  iterator<R> end() { return {map_iterator<T, R>{fun_, source_.end()}}; }
-
-private:
-  std::function<R(T const &)> fun_;
-  stream_base<T> source_;
-};
-
-template <typename T>
-template <typename F>
-stream_base<traits::mapped_t<T, F>> stream_base<T>::map(F && func) && {
-  return std::make_shared<map_stream<T, traits::mapped_t<T, F>>>(
-      func, std::move(*this));
-}
-
-template <typename T>
-template <typename F>
-stream_base<traits::mapped_t<T, F>> stream_base<T>::map(F && func) const & {
-  return std::make_shared<map_stream<T, traits::mapped_t<T, F>>>(func, *this);
-}
-}

+ 27 - 0
include/stream/owning_view.h

@@ -0,0 +1,27 @@
+//
+//  owning_view.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+namespace stream::ranges {
+template <typename C> class owning_view {
+private:
+  C container_;
+
+public:
+  owning_view(C && container) : container_(std::move(container)) {}
+
+  auto begin() const { return end_aware_iterator(container_); }
+  auto end() const { return end_aware_iterator<iter<C>>(); }
+  bool empty() const { return container_.empty(); }
+  size_t size() const { return container_.size(); }
+};
+
+}
+
+namespace stream::ranges::views {
+}

+ 38 - 0
include/stream/ref_view.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include <iterator>
+
+#include <stream/forward.h>
+#include <stream/owning_view.h>
+
+namespace stream::ranges {
+template <typename It> class ref_view {
+private:
+  It begin_;
+  It end_;
+
+public:
+  ref_view(It begin, It end) : begin_(begin), end_(end) {}
+  template <typename C>
+  ref_view(C & container)
+      : begin_(std::begin(container)), end_(std::end(container)) {}
+
+  auto begin() const { return end_aware_iterator(begin_, end_); }
+  auto end() const { return end_aware_iterator<It>(); }
+  bool empty() const { return begin_ == end_; }
+  size_t size() const { return std::distance(begin_, end_); }
+};
+
+template <typename It> ref_view(It, It) -> ref_view<It>;
+template <typename C> ref_view(C &) -> ref_view<iter<C>>;
+}
+
+namespace stream::ranges::views {
+template <typename C> auto all(C & container) { return ref_view(container); }
+template <typename C> auto all(C const & container) {
+  return ref_view(container);
+}
+template <typename C> auto all(C && container) {
+  return owning_view(std::move(container));
+}
+}

+ 24 - 0
include/stream/reference_view.h

@@ -0,0 +1,24 @@
+//
+//  reference.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <stream/transform_view.h>
+
+namespace stream::ranges::views {
+inline auto deref() {
+  return transform([](auto const & ptr) -> decltype(auto) { return *ptr; });
+}
+
+inline auto as_const() {
+  return transform([](auto & ref) -> auto const & { return ref; });
+}
+
+inline auto as_rvalue() {
+  return transform([](auto & ref) -> auto && { return ref; });
+}
+}

+ 29 - 0
include/stream/single_view.h

@@ -0,0 +1,29 @@
+//
+//  single.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+namespace stream::ranges {
+template <typename T> class single_view {
+private:
+  T value_;
+
+public:
+  single_view(T const & value) : value_(value) {}
+
+  T const * begin() const { return &value_; }
+  T const * end() const { return 1 + &value_; }
+  static bool empty() { return false; }
+  static size_t size() { return 1; }
+};
+}
+
+namespace stream::ranges::views {
+template <typename T> auto single(T const & value) {
+  return single_view(value);
+}
+}

+ 0 - 89
include/stream/source.hpp

@@ -1,89 +0,0 @@
-#pragma once
-
-#include <iterator>
-
-namespace stream::detail {
-template <typename C> class source_stream {
-public:
-  explicit source_stream(C && cont) : source_(std::forward<C>(cont)) {}
-
-  auto begin() { return iterator(source_.begin()); }
-  auto end() { return iterator(source_.end()); }
-
-private:
-  C source_;
-};
-
-template <typename It> class range_stream {
-public:
-  explicit range_stream(It b, It e) : begin_(b), end_(e) {}
-
-  auto begin() { return iterator(begin_); }
-  auto end() { return iterator(end_); }
-
-private:
-  It begin_, end_;
-};
-}
-
-namespace stream {
-/**
- * Construct a stream out of the given container. If C && is an rvalue
- * reference, the returned object will take ownership of cont. Otherwise, we
- * capture cont by reference to maximise performance.
- */
-template <typename C>
-auto of(C && cont) -> detail::stream_base<decltype(*cont.begin())> {
-  return std::make_shared<detail::source_stream<C>>(std::forward<C>(cont));
-}
-
-template <typename T> detail::stream_base<T &> empty() {
-  return std::make_shared<detail::range_stream<T *>>(nullptr, nullptr);
-}
-
-/**
- * Construct a single element stream containing the pointer given
- */
-template <typename T> detail::stream_base<T &> of(T * ptr) {
-  return std::make_shared<detail::range_stream<T *>>(ptr, ptr + 1);
-}
-
-/**
- * Construct a stream from input iterators representing the start and end
- * Requirements: It must be an iterator type that provides the trait
- * 'reference'
- */
-template <typename It>
-detail::stream_base<typename It::reference> of(It begin, It end) {
-  return std::make_shared<detail::range_stream<It>>(begin, end);
-}
-
-/**
- * Construct a stream given certain start and end bounds.
- * e.g. stream::make_range_stream(0, 10)
- */
-template <typename T> detail::stream_base<T &> range(T start, T const & end) {
-  std::vector<T> vec;
-  vec.reserve(end - start);
-  while (start < end) {
-    vec.emplace_back(start++);
-  }
-  return make_stream(std::move(vec));
-}
-
-/**
- * Construct a stream given certain start and end bounds, as well as an
- * increment amount. e.g. stream::make_range_stream(0, 10, 2)
- */
-template <typename T>
-detail::stream_base<T &> range(T start, T const & end, T const & increment) {
-  int elems{(end - start) / increment};
-  if (elems < 0 || end == start) { return {}; }
-  std::vector<T> vec{start};
-  vec.reserve(elems + 1);
-  while (elems-- > 0) {
-    vec.emplace_back(start += increment);
-  }
-  return make_stream(std::move(vec));
-}
-}

+ 0 - 161
include/stream/stream_base.hpp

@@ -1,161 +0,0 @@
-#pragma once
-
-#include <algorithm>
-#include <memory>
-#include <numeric>
-#include <optional>
-#include <vector>
-
-#include "iterator.hpp"
-
-namespace stream::detail {
-template <typename T, typename = void> class stream_base_pointer_impl {};
-
-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; });
-  }
-
-  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);
-  }
-
-  template <typename Stream>
-  stream_base(Stream && impl)
-      : stream_base(std::make_shared<std::remove_reference_t<Stream>>(
-            std::forward<Stream>(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};
-};
-}

+ 17 - 7
include/stream/streams.hpp

@@ -1,12 +1,22 @@
 #pragma once
 
-#include <stream/forward.hpp>
+#include <stream/forward.h>
 
-#include <stream/iterator.hpp>
-#include <stream/stream_base.hpp>
+#include <stream/empty_view.h>
+#include <stream/owning_view.h>
+#include <stream/ref_view.h>
+#include <stream/single_view.h>
 
-#include <stream/source.hpp>
+#include <stream/iota_view.h>
 
-#include <stream/filter.hpp>
-#include <stream/join.hpp>
-#include <stream/map.hpp>
+#include <stream/filter_view.h>
+#include <stream/join_view.h>
+#include <stream/reference_view.h>
+#include <stream/transform_view.h>
+#include <stream/tuple_view.h>
+
+#include <stream/view.h>
+
+#include <stream/accumulate.h>
+#include <stream/for_each.h>
+#include <stream/to.h>

+ 38 - 0
include/stream/to.h

@@ -0,0 +1,38 @@
+//
+//  to.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+
+namespace stream::ranges {
+template <typename C> class store {
+private:
+  C & to_;
+
+public:
+  store(C & to) : to_(to) {}
+
+  template <typename Stream>
+  friend void operator|(Stream && stream, store && store) {
+    store.to_.emplace(store.to_.end(), stream.begin(), stream.end());
+  }
+};
+
+template <template <typename...> class C> struct to_range {
+  template <typename Stream> friend auto operator|(Stream && stream, to_range) {
+    return C(stream.begin(), stream.end());
+  }
+};
+
+template <template <typename...> class C> auto to() { return to_range<C>(); }
+auto to_vector() { return to<std::vector>(); }
+auto to_map() { return to<std::map>(); }
+auto to_set() { return to<std::set>(); }
+}

+ 0 - 68
include/stream/traits.hpp

@@ -1,68 +0,0 @@
-//
-//  traits.hpp
-//  stream
-//
-//  Created by Sam Jaffe on 6/24/17.
-//
-
-#pragma once
-
-#include <utility>
-
-namespace stream::detail {
-template <typename F> struct map_member_object;
-template <typename T, typename R> struct map_member_object<R T::*> {
-  using type = R const &;
-  type operator()(T const & val) const { return val.*mem; }
-  R T::*mem;
-};
-
-template <typename F> struct map_member_function;
-template <typename T, typename R> struct map_member_function<R (T::*)() const> {
-  using type = R;
-  type operator()(T const & val) const { return (val.*mem)(); }
-  R (T::*mem)() const;
-};
-
-template <typename F> struct map_member_function;
-template <typename T, typename R>
-struct map_member_function<R (T::*)() const noexcept> {
-  using type = R;
-  type operator()(T const & val) const noexcept { return (val.*mem)(); }
-  R (T::*mem)() const;
-};
-}
-
-namespace stream::traits {
-template <typename F>
-using memvar_f = typename detail::map_member_object<F>::type;
-template <typename F>
-using memfun_f = typename detail::map_member_function<F>::type;
-
-template <typename F>
-using is_memvar_t =
-    typename std::enable_if<std::is_member_object_pointer<F>::value>::type;
-template <typename F>
-using is_memfun_t =
-    typename std::enable_if<std::is_member_function_pointer<F>::value>::type;
-
-template <typename T, typename = void>
-struct is_dereferencable : public std::false_type {};
-
-template <> struct is_dereferencable<void *> : public std::false_type {};
-
-template <typename T>
-struct is_dereferencable<T, typename std::enable_if<!std::is_void<
-                                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>()));
-
-template <typename T, typename F>
-using fmapped_t =
-    typename decltype(std::declval<F>()(std::declval<T>()))::value_type;
-}

+ 104 - 0
include/stream/transform_view.h

@@ -0,0 +1,104 @@
+#pragma once
+
+#include <functional>
+
+#include <iterator/proxy.h>
+#include <stream/detail/traits.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename It, typename Proj>
+class transform_iterator : public proxy<It, transform_iterator<It, Proj>> {
+private:
+  using super_t = proxy<It, transform_iterator<It, Proj>>;
+
+private:
+  Proj projection_;
+
+public:
+  transform_iterator() = default;
+  transform_iterator(It iter, Proj projection)
+      : super_t(std::move(iter)), projection_(projection) {}
+
+  decltype(auto) dereference() const {
+    return projection_(super_t::dereference());
+  }
+};
+
+template <typename S, typename T> class transform_view {
+public:
+  using Proj = std::function<T(traits::cref_t<S>)>;
+
+private:
+  S stream_;
+  Proj projection_;
+
+public:
+  transform_view(S && stream, Proj projection)
+      : stream_(FWD(stream)), projection_(projection) {}
+  template <typename F>
+  transform_view(S && stream, F projection)
+      : stream_(FWD(stream)), projection_(projection) {}
+
+  auto begin() const {
+    return transform_iterator(stream_.begin(), projection_);
+  }
+  auto end() const { return transform_iterator(stream_.end(), projection_); }
+  bool empty() const { return stream_.empty(); }
+  size_t size() const { return stream_.size(); }
+};
+
+template <typename S, typename R, typename T>
+transform_view(S &&, std::function<R(T)>) -> transform_view<S, R>;
+template <typename S, typename F>
+transform_view(S &&, F)
+    -> transform_view<S, std::invoke_result_t<F, traits::cref_t<S>>>;
+}
+
+MAKE_ITERATOR_FACADE_TYPEDEFS_T(stream::ranges::transform_iterator);
+
+namespace stream::ranges::views {
+template <typename Proj> class transform {
+public:
+  transform(Proj const & projection) : projection_(projection) {}
+
+  template <typename T, typename R>
+  transform(R (*ptr)(T))
+      : projection_([ptr](auto & t) -> R { return ptr(t); }) {}
+
+  template <typename T, typename R>
+  transform(R T::*ptr)
+      : projection_([ptr](auto & t) -> R const & { return t.*ptr; }) {}
+
+  template <typename T, typename R>
+  transform(R (T::*ptr)() const)
+      : projection_([ptr](auto & t) -> R { return (t.*ptr)(); }) {}
+
+  template <typename T, typename R>
+  transform(R (T::*ptr)() const noexcept)
+      : projection_([ptr](auto & t) -> R { return (t.*ptr)(); }) {}
+
+  template <typename Stream>
+  friend auto operator|(Stream && stream, transform map) {
+    // TODO: if constexpr transform_view
+    return transform_view(FWD(stream), std::move(map.projection_));
+  }
+
+private:
+  Proj projection_;
+};
+
+template <typename Proj> transform(Proj const &) -> transform<Proj>;
+template <typename T, typename R>
+transform(R (*ptr)(T)) -> transform<std::function<R(T const &)>>;
+template <typename T, typename R>
+transform(R T::*ptr) -> transform<std::function<R const &(T const &)>>;
+template <typename T, typename R>
+transform(R (T::*ptr)() const) -> transform<std::function<R(T const &)>>;
+template <typename T, typename R>
+transform(R (T::*ptr)() const noexcept)
+    -> transform<std::function<R(T const &)>>;
+}
+
+#undef FWD

+ 20 - 0
include/stream/tuple_view.h

@@ -0,0 +1,20 @@
+//
+//  tuple_view.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <stream/transform_view.h>
+
+namespace stream::ranges::views {
+template <size_t I> auto elements() {
+  return transform(
+      [](auto & tuple) -> decltype(auto) { return std::get<I>(tuple); });
+}
+
+inline auto keys() { return elements<0>(); }
+inline auto values() { return elements<1>(); }
+}

+ 66 - 0
include/stream/view.h

@@ -0,0 +1,66 @@
+//
+//  view.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+namespace stream::ranges {
+
+template <typename T> class view {
+public:
+  class iterator : public facade<iterator> {
+  private:
+    T (*dereference_)(void *){nullptr};
+    bool (*equal_to_)(void *, void *){nullptr};
+    void (*increment_)(void *){nullptr};
+    std::shared_ptr<void> impl_{nullptr};
+
+  public:
+    template <typename It>
+    iterator(It impl)
+        : dereference_([](void * p) -> T { return **((It *)p); }),
+          equal_to_(
+              [](void * l, void * r) { return *((It *)l) == *((It *)r); }),
+          increment_([](void * p) { ++(*(It *)(p)); }),
+          impl_(std::make_shared<It>(impl)) {}
+
+    T dereference() const { return dereference_(impl_.get()); }
+    void increment() { increment_(impl_.get()); }
+    bool equal_to(iterator const & other) const {
+      return impl_ == other.impl_ ||
+             (impl_ && other.impl_ &&
+              equal_to_(impl_.get(), other.impl_.get()));
+    }
+  };
+
+private:
+  std::shared_ptr<void> impl_{nullptr};
+  iterator (*begin_)(void *){nullptr};
+  iterator (*end_)(void *){nullptr};
+
+public:
+  template <typename S>
+  view(std::shared_ptr<S> impl)
+      : impl_(impl), begin_([](void * p) -> iterator {
+          return static_cast<S *>(p)->begin();
+        }),
+        end_([](void * p) -> iterator { return static_cast<S *>(p)->end(); }) {}
+
+  template <typename S>
+  view(S & impl) : view(std::shared_ptr<S>(&impl, [](void *) {})) {}
+
+  template <typename S>
+  view(S const & impl) : view(std::shared_ptr<S const>(&impl, [](void *) {})) {}
+
+  template <typename S>
+  view(S && impl) : view(std::make_shared<S>(std::forward<S>(impl))) {}
+
+  auto begin() const { return begin_(impl_.get()); }
+  auto end() const { return end_(impl_.get()); }
+  bool empty() const { return begin() == end(); }
+};
+
+}

+ 34 - 24
stream.xcodeproj/project.pbxproj

@@ -9,7 +9,6 @@
 /* Begin PBXBuildFile section */
 		CD52820429D3B1AB001A84DE /* stream in Headers */ = {isa = PBXBuildFile; fileRef = CDAA170121A3A738007BBA11 /* stream */; settings = {ATTRIBUTES = (Public, ); }; };
 		CDEC1D7623514BEB0091D9F2 /* stream_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD9337281E3CD78B00699FF5 /* stream_test.cxx */; };
-		CDEC1D7723514BEB0091D9F2 /* stream_fluent_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDF9374E1E3D9AD7003E5D5C /* stream_fluent_test.cxx */; };
 		CDEC1D7923514BF80091D9F2 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDEC1D6323514BB50091D9F2 /* GoogleMock.framework */; };
 /* End PBXBuildFile section */
 
@@ -66,19 +65,26 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		CD5281EB29D3B173001A84DE /* join.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = join.hpp; sourceTree = "<group>"; };
-		CD5281EC29D3B173001A84DE /* traits.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = traits.hpp; sourceTree = "<group>"; };
-		CD5281ED29D3B173001A84DE /* map.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = map.hpp; sourceTree = "<group>"; };
-		CD5281EE29D3B173001A84DE /* fluent.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = fluent.hpp; sourceTree = "<group>"; };
-		CD5281EF29D3B173001A84DE /* stream_base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = stream_base.hpp; sourceTree = "<group>"; };
-		CD5281F029D3B173001A84DE /* forward.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = forward.hpp; sourceTree = "<group>"; };
-		CD5281F329D3B173001A84DE /* ifd_pointer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ifd_pointer.hpp; sourceTree = "<group>"; };
-		CD5281F529D3B173001A84DE /* filter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filter.hpp; sourceTree = "<group>"; };
-		CD5281F629D3B173001A84DE /* source.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = source.hpp; sourceTree = "<group>"; };
+		CD5281EB29D3B173001A84DE /* join_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = join_view.h; sourceTree = "<group>"; };
+		CD5281EC29D3B173001A84DE /* traits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = traits.h; sourceTree = "<group>"; };
+		CD5281ED29D3B173001A84DE /* transform_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = transform_view.h; sourceTree = "<group>"; };
+		CD5281F029D3B173001A84DE /* forward.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = forward.h; sourceTree = "<group>"; };
+		CD5281F529D3B173001A84DE /* filter_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = filter_view.h; sourceTree = "<group>"; };
+		CD5281F629D3B173001A84DE /* ref_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ref_view.h; 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>"; };
-		CD52822729D3BCE4001A84DE /* iterator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = iterator.hpp; sourceTree = "<group>"; };
+		CD52827729D4EED6001A84DE /* empty_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = empty_view.h; sourceTree = "<group>"; };
+		CD52827829D4EF0E001A84DE /* single_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = single_view.h; sourceTree = "<group>"; };
+		CD52827929D4EF43001A84DE /* owning_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = owning_view.h; sourceTree = "<group>"; };
+		CD52827A29D4EFBE001A84DE /* iota_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iota_view.h; sourceTree = "<group>"; };
+		CD52827B29D4F764001A84DE /* view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = view.h; sourceTree = "<group>"; };
+		CD52827C29D4FB11001A84DE /* to.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = to.h; sourceTree = "<group>"; };
+		CD52827D29D50081001A84DE /* for_each.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = for_each.h; sourceTree = "<group>"; };
+		CD52827E29D50BFA001A84DE /* reference_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = reference_view.h; sourceTree = "<group>"; };
+		CD52827F29D50E24001A84DE /* accumulate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = accumulate.h; sourceTree = "<group>"; };
+		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>"; };
 		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>"; };
@@ -86,7 +92,6 @@
 		CDEC1D5B23514BB50091D9F2 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CDEC1D6E23514BC60091D9F2 /* stream-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "stream-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDEC1D7223514BC60091D9F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		CDF9374E1E3D9AD7003E5D5C /* stream_fluent_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stream_fluent_test.cxx; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -135,15 +140,21 @@
 			isa = PBXGroup;
 			children = (
 				CD5281F229D3B173001A84DE /* detail */,
-				CD5281EB29D3B173001A84DE /* join.hpp */,
-				CD5281EC29D3B173001A84DE /* traits.hpp */,
-				CD5281ED29D3B173001A84DE /* map.hpp */,
-				CD5281EE29D3B173001A84DE /* fluent.hpp */,
-				CD5281EF29D3B173001A84DE /* stream_base.hpp */,
-				CD52822729D3BCE4001A84DE /* iterator.hpp */,
-				CD5281F029D3B173001A84DE /* forward.hpp */,
-				CD5281F529D3B173001A84DE /* filter.hpp */,
-				CD5281F629D3B173001A84DE /* source.hpp */,
+				CD52827729D4EED6001A84DE /* empty_view.h */,
+				CD52827829D4EF0E001A84DE /* single_view.h */,
+				CD52827929D4EF43001A84DE /* owning_view.h */,
+				CD5281F629D3B173001A84DE /* ref_view.h */,
+				CD52827A29D4EFBE001A84DE /* iota_view.h */,
+				CD5281ED29D3B173001A84DE /* transform_view.h */,
+				CD52828129D5133C001A84DE /* tuple_view.h */,
+				CD5281F529D3B173001A84DE /* filter_view.h */,
+				CD5281EB29D3B173001A84DE /* join_view.h */,
+				CD52827E29D50BFA001A84DE /* reference_view.h */,
+				CD52827D29D50081001A84DE /* for_each.h */,
+				CD52827C29D4FB11001A84DE /* to.h */,
+				CD52827F29D50E24001A84DE /* accumulate.h */,
+				CD52827B29D4F764001A84DE /* view.h */,
+				CD5281F029D3B173001A84DE /* forward.h */,
 				CD5281F729D3B173001A84DE /* streams.hpp */,
 			);
 			path = stream;
@@ -152,7 +163,8 @@
 		CD5281F229D3B173001A84DE /* detail */ = {
 			isa = PBXGroup;
 			children = (
-				CD5281F329D3B173001A84DE /* ifd_pointer.hpp */,
+				CD52828029D51166001A84DE /* identity.h */,
+				CD5281EC29D3B173001A84DE /* traits.h */,
 			);
 			path = detail;
 			sourceTree = "<group>";
@@ -180,7 +192,6 @@
 			children = (
 				CD64CCB926232D6900770A30 /* xcode_gtest_helper.h */,
 				CD9337281E3CD78B00699FF5 /* stream_test.cxx */,
-				CDF9374E1E3D9AD7003E5D5C /* stream_fluent_test.cxx */,
 			);
 			path = test;
 			sourceTree = "<group>";
@@ -375,7 +386,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				CDEC1D7623514BEB0091D9F2 /* stream_test.cxx in Sources */,
-				CDEC1D7723514BEB0091D9F2 /* stream_fluent_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 0 - 83
test/stream_fluent_test.cxx

@@ -1,83 +0,0 @@
-//
-//  stream_fluent.t.h
-//  stream
-//
-//  Created by Sam Jaffe on 1/28/17.
-//
-
-#include "xcode_gtest_helper.h"
-
-#include <vector>
-
-#include "stream/fluent.hpp"
-
-using ::testing::ElementsAreArray;
-using ::testing::Eq;
-
-TEST(FluentStreamTest, CollectToObjectPreservesElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = stream::of(input);
-  std::set<int> out{};
-
-  EXPECT_THAT(s > out, ElementsAreArray(input));
-}
-
-TEST(FluentStreamTest, MapToSelfIsSelfs) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto identity = [](int i) { return i; };
-  auto s = stream::of(input) | identity;
-
-  EXPECT_THAT(s.collect(), Eq(input));
-}
-
-TEST(FluentStreamTest, MapCanAlterValues) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<int> expected{3, 5, 7, 9, 11};
-  auto fmap = [](int i) { return 2 * i + 1; };
-  auto s = stream::of(input) | fmap;
-
-  EXPECT_THAT(s.collect(), Eq(expected));
-}
-
-TEST(FluentStreamTest, CanMapElementToBool) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<bool> expected{false, true, false, true, false};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto s = stream::of(input) | even;
-
-  EXPECT_THAT(s.collect(), Eq(expected));
-}
-
-TEST(FluentStreamTest, CanFilterOutElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<int> expected{2, 4};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto s = stream::of(input) % even;
-
-  EXPECT_THAT(s.collect(), Eq(expected));
-}
-
-TEST(FluentStreamTest, NoOpFilterReturnOriginal) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto pass = [](int) { return true; };
-  auto s = stream::of(input) % pass;
-
-  EXPECT_THAT(s.collect(), Eq(input));
-}
-
-TEST(FluentStreamTest, AccumulateDefaultsToAdd) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto sum = [](int lhs, int rhs) { return lhs + rhs; };
-  auto s = stream::of(input) % even;
-  EXPECT_THAT(s > sum, Eq(6));
-}
-
-TEST(FluentStreamTest, FlatmapJoinsIterableOutputs) {
-  std::vector<int> vv{1, 2, 3, 4, 5};
-  auto next3 = [](int i) { return std::vector<int>{i, i + 1, i + 2}; };
-  std::vector<int> expected{1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
-  auto s = stream::of(vv) || next3;
-
-  EXPECT_THAT(s.collect(), Eq(expected));
-}

+ 64 - 62
test/stream_test.cxx

@@ -16,12 +16,15 @@
 using ::testing::ElementsAreArray;
 using ::testing::Eq;
 
+namespace views = stream::views;
+namespace ranges = stream::ranges;
+
 // Workaround for OSX and pointer-to-member-functions
 template class std::basic_string<char>;
 
 TEST(StreamTest, IteratorPreservesElements) {
   std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = stream::of(input);
+  auto s = views::all(input);
   std::vector<int> out{s.begin(), s.end()};
 
   EXPECT_THAT(out, Eq(input));
@@ -29,7 +32,7 @@ TEST(StreamTest, IteratorPreservesElements) {
 
 TEST(MapStreamTest, IteratorPreservesElements) {
   std::map<int, int> input{{1, 1}, {2, 2}};
-  auto s = stream::of(input);
+  auto s = views::all(input);
   std::map<int, int> out{s.begin(), s.end()};
 
   EXPECT_THAT(out, Eq(input));
@@ -37,17 +40,15 @@ TEST(MapStreamTest, IteratorPreservesElements) {
 
 TEST(StreamTest, CollectPreservesElements) {
   std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = stream::of(input);
-  std::vector<int> out{s.collect()};
+  std::vector<int> out = views::all(input) | ranges::to_vector();
 
   EXPECT_THAT(out, Eq(input));
 }
 
 TEST(StreamTest, CollectToObjectPreservesElements) {
   std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = stream::of(input);
-  std::set<int> out{};
-  s.collect(out);
+  auto s = views::all(input);
+  std::set<int> out = s | ranges::to_set();
 
   EXPECT_THAT(out, ElementsAreArray(input));
 }
@@ -55,18 +56,18 @@ TEST(StreamTest, CollectToObjectPreservesElements) {
 TEST(StreamTest, MapToSelfIsSelfs) {
   std::vector<int> input{1, 2, 3, 4, 5};
   auto identity = [](int i) { return i; };
-  auto s = stream::of(input).map(identity);
+  auto out = input | views::transform(identity) | ranges::to_vector();
 
-  EXPECT_THAT(s.collect(), Eq(input));
+  EXPECT_THAT(out, Eq(input));
 }
 
 TEST(StreamTest, MapCanAlterValues) {
   std::vector<int> input{1, 2, 3, 4, 5};
   std::vector<int> expected{3, 5, 7, 9, 11};
   auto fmap = [](int i) { return 2 * i + 1; };
-  auto s = stream::of(input).map(fmap);
+  auto out = input | views::transform(fmap) | ranges::to_vector();
 
-  EXPECT_THAT(s.collect(), Eq(expected));
+  EXPECT_THAT(out, Eq(expected));
 }
 
 template <typename T> struct nocopy {
@@ -88,7 +89,7 @@ TEST(MapStreamTest, MapToValue) {
     return tmp;
   }();
   auto fmap = [](auto & pair) -> auto & { return pair.second; };
-  auto s = stream::of(input).map(fmap);
+  auto s = input | views::transform(fmap);
   std::vector<int> out(s.begin(), s.end());
   std::vector<int> const expected{1, 2};
 
@@ -98,68 +99,69 @@ TEST(MapStreamTest, MapToValue) {
 TEST(StreamTest, CanBuildFromSingleElement) {
   int value = 11;
   auto even = [](int i) { return i % 2 == 0; };
-  auto s = stream::of(&value).filter(even);
+  auto s = views::single(value) | views::filter(even);
 
-  EXPECT_TRUE(s.empty());
+  EXPECT_THAT(std::distance(s.begin(), s.end()), 0);
 }
 
 TEST(StreamTest, CanBuildFromIterators) {
   std::vector<int> input{1, 2, 3, 4, 5};
   std::vector<int> expected{5, 7};
   auto fmap = [](int i) { return 2 * i + 1; };
-  auto s = stream::of(input.begin() + 1, input.begin() + 3).map(fmap);
+  auto out = ranges::ref_view(input.begin() + 1, input.begin() + 3) |
+             views::transform(fmap) | ranges::to_vector();
 
-  EXPECT_THAT(s.collect(), Eq(expected));
+  EXPECT_THAT(out, Eq(expected));
 }
 
 TEST(StreamTest, NoOpFilterReturnOriginal) {
   std::vector<int> input{1, 2, 3, 4, 5};
   auto pass = [](int) { return true; };
-  auto s = stream::of(input).filter(pass);
+  auto out = input | views::filter(pass) | ranges::to_vector();
 
-  EXPECT_THAT(s.collect(), Eq(input));
+  EXPECT_THAT(out, Eq(input));
 }
 
 TEST(StreamTest, CanFilterOutElements) {
   std::vector<int> input{1, 2, 3, 4, 5};
   std::vector<int> expected{2, 4};
   auto even = [](int i) { return i % 2 == 0; };
-  auto s = stream::of(input).filter(even);
-
-  EXPECT_THAT(s.collect(), Eq(expected));
-}
-
-TEST(StreamTest, AccumulateDefaultsToAdd) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto s = stream::of(input).filter(even);
+  auto out = input | views::filter(even) | ranges::to_vector();
 
-  EXPECT_THAT(s.accumulate(0), Eq(6));
+  EXPECT_THAT(out, Eq(expected));
 }
 
-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 = stream::of(input).filter(even);
-
-  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(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, FlatmapJoinsIterableOutputs) {
   std::vector<int> vv{1, 2, 3, 4, 5};
   auto next3 = [](int i) { return std::vector<int>{i, i + 1, i + 2}; };
   std::vector<int> expected{1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
-  auto s = stream::of(vv).flatmap(next3);
+  auto out = vv | views::transform(next3) | views::join() | ranges::to_vector();
 
-  EXPECT_THAT(s.collect(), Eq(expected));
+  EXPECT_THAT(out, Eq(expected));
 }
 
 TEST(StreamTest, CanDereferenceElements) {
   int val = 5;
   std::vector<int *> input{&val};
-  auto data = stream::of(input).deref().collect();
+  auto data = input | views::deref() | ranges::to_vector();
 
   EXPECT_THAT(data.front(), Eq(val));
 }
@@ -167,7 +169,7 @@ TEST(StreamTest, CanDereferenceElements) {
 TEST(StreamTest, CanForEachConsume) {
   int hits = 0;
   std::vector<int> input{1, 2, 3, 4, 5};
-  stream::of(input).each([&hits](int) { ++hits; });
+  input | views::for_each([&hits](int) { ++hits; });
 
   EXPECT_THAT(hits, Eq(5));
 }
@@ -178,7 +180,7 @@ TEST(StreamTest, CanFetchMemPtr) {
   };
   std::vector<test> input{{1}, {3}, {2}};
   std::vector<int> expected{1, 3, 2};
-  auto out = stream::of(input).map(&test::val).collect();
+  auto out = input | views::transform(&test::val) | ranges::to_vector();
 
   EXPECT_THAT(out, Eq(expected));
 }
@@ -186,26 +188,26 @@ TEST(StreamTest, CanFetchMemPtr) {
 TEST(StreamTest, CanMapToMemFn) {
   std::vector<std::string> input{"hello", "goodbye"};
   std::vector<std::string::size_type> expected{5, 7};
-  auto out = stream::of(input).map(&std::string::size).collect();
+  auto out = input | views::transform(&std::string::size) | ranges::to_vector();
 
   EXPECT_THAT(out, Eq(expected));
 }
-
-TEST(StreamTest, CastStreamToParentType) {
-  struct base {
-    char cat[4] = "cat";
-  };
-  struct test : base {
-    test(int v) : val(v) {}
-    int val;
-  };
-  std::vector<test> input{{1}, {3}, {2}};
-
-  auto addressof_void = [](auto const & p) { return (void *)&p; };
-
-  auto strm = stream::of(input).cast<base>();
-  auto first = stream::of(input).map(addressof_void).collect();
-  auto second = strm.map(addressof_void).collect();
-
-  EXPECT_THAT(first, second);
-}
+//
+// TEST(StreamTest, CastStreamToParentType) {
+//  struct base {
+//    char cat[4] = "cat";
+//  };
+//  struct test : base {
+//    test(int v) : val(v) {}
+//    int val;
+//  };
+//  std::vector<test> input{{1}, {3}, {2}};
+//
+//  auto addressof_void = [](auto const & p) { return (void *)&p; };
+//
+//  auto strm = stream::of(input).cast<base>();
+//  auto first = stream::of(input).map(addressof_void).collect();
+//  auto second = strm.map(addressof_void).collect();
+//
+//  EXPECT_THAT(first, second);
+//}