RxCpp
The Reactive Extensions for Native (RxCpp) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators in both C and C++.
linq_selectmany.hpp
Go to the documentation of this file.
1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2 
3 #pragma once
4 
5 #include "util.hpp"
6 #include "linq_cursor.hpp"
7 
8 #include <type_traits>
9 
10 namespace cpplinq
11 {
12  namespace detail
13  {
14  struct default_select_many_selector
15  {
16  template <class T1, class T2>
17  auto operator()(T1&& t1, T2&& t2) const
18  -> decltype(std::forward<T2>(t2))
19  {
20  return std::forward<T2>(t2);
21  }
22  };
23  }
24 
25  namespace detail
26  {
27  template <typename Fn, typename Arg>
28  struct resolve_select_many_fn_return_type
29  {
30  typedef decltype(std::declval<Fn>()(std::declval<Arg>())) value;
31  };
32 
33  template <typename TCol>
34  struct value_collection_adapter
35  {
36  value_collection_adapter(const TCol& col)
37  : _collection(col){}
38 
39  value_collection_adapter(const value_collection_adapter& src)
40  : _collection(src._collection) {}
41 
42  value_collection_adapter(value_collection_adapter && src)
43  : _collection(std::move(src._collection)) {}
44 
45  const TCol& get() const
46  {
47  return _collection;
48  }
49 
50  TCol& get()
51  {
52  return _collection;
53  }
54 
55  private:
56  TCol _collection;
57  };
58 
59  template<typename TCol>
60  struct collection_store_type
61  {
62  typedef typename std::remove_reference<TCol>::type collection_type;
63  typedef std::reference_wrapper<collection_type> reference_store_type;
64  typedef value_collection_adapter<collection_type> value_store_type;
65 
66  typedef typename std::conditional<std::is_reference<TCol>::value, reference_store_type, value_store_type>::type store;
67  };
68  }
69 
70  // cur<T> -> (T -> cur<element_type>) -> cur<element_type>
71  template <class Container1, class Fn, class Fn2>
73  {
74  template <class T> static T instance(); // for type inference
75 
76  Container1 c1;
77  Fn fn;
78  Fn2 fn2;
79 
80  typedef typename Container1::cursor Cur1;
81  typedef decltype(from(instance<Fn>()(instance<Cur1>().get()))) Container2;
82  typedef typename Container2::cursor Cur2;
83 
84  typedef typename detail::resolve_select_many_fn_return_type<Fn, typename Cur1::element_type>::value inner_collection;
85 
86  public:
87  class cursor
88  {
89  public:
90  typedef typename util::min_cursor_category<typename Cur1::cursor_category,
91  typename Cur2::cursor_category,
92  forward_cursor_tag>::type
94  typedef typename Cur2::reference_type reference_type;
95  typedef typename Cur2::element_type element_type;
96 
97  typedef detail::collection_store_type<inner_collection> collection_store_type;
98  typedef typename collection_store_type::store collection_store;
99  typedef std::shared_ptr<collection_store> collection_store_ptr;
100 
101  private:
102  // TODO: we need to lazy eval somehow, but this feels wrong.
103  Cur1 cur1;
105  Fn fn;
106  Fn2 fn2;
107  collection_store_ptr store;
108 
109  public:
110  cursor(Cur1 cur1, const Fn& fn, const Fn2& fn2)
111  : cur1(std::move(cur1)), fn(fn), fn2(fn2)
112  {
113  if (!cur1.empty())
114  {
115  store = std::make_shared<collection_store>(fn(cur1.get()));
116  cur2 = from(store->get()).get_cursor();
117  }
118  }
119 
120  bool empty() const
121  {
122  return cur2.empty();
123  }
124 
125  void inc()
126  {
127  cur2.inc();
128  thunk();
129  }
130 
131  reference_type get() const
132  {
133  return fn2(cur1.get(), cur2.get());
134  }
135 
136  private:
137  void thunk()
138  {
139  // refill cur2
140  while (cur2.empty() && !cur1.empty()) {
141  cur1.inc();
142  if (cur1.empty())
143  break;
144 
145  store = std::make_shared<collection_store>(fn(cur1.get()));
146  cur2 = from(store->get()).get_cursor();
147  }
148  }
149  };
150 
151  linq_select_many(Container1 c1, Fn fn, Fn2 fn2)
152  : c1(std::move(c1)), fn(std::move(fn)), fn2(std::move(fn2))
153  {
154  }
155 
157  {
158  return cursor(c1.get_cursor(), fn, fn2);
159  }
160  };
161 }
162 
163 
164 
linq_select_many(Container1 c1, Fn fn, Fn2 fn2)
Definition: linq_selectmany.hpp:151
Definition: linq_cursor.hpp:122
Cur2::element_type element_type
Definition: linq_selectmany.hpp:95
void inc()
Definition: linq_cursor.hpp:276
T get() const
Definition: linq_cursor.hpp:277
Definition: linq_selectmany.hpp:72
Definition: linq_selectmany.hpp:87
cursor get_cursor() const
Definition: linq_selectmany.hpp:156
linq_driver< iter_cursor< typename util::container_traits< TContainer >::iterator > > from(TContainer &c)
Definition: linq.hpp:556
Definition: linq.hpp:186
Definition: linq_cursor.hpp:64
std::shared_ptr< collection_store > collection_store_ptr
Definition: linq_selectmany.hpp:99
bool empty() const
Definition: linq_selectmany.hpp:120
bool empty() const
Definition: linq_cursor.hpp:275
cursor(Cur1 cur1, const Fn &fn, const Fn2 &fn2)
Definition: linq_selectmany.hpp:110
auto get_cursor(const linq_take< Collection > &take) -> decltype(get_cursor_(take, typename Collection::cursor::cursor_category()))
Definition: linq_take.hpp:87
detail::collection_store_type< inner_collection > collection_store_type
Definition: linq_selectmany.hpp:97
Cur2::reference_type reference_type
Definition: linq_selectmany.hpp:94
util::min_cursor_category< typename Cur1::cursor_category, typename Cur2::cursor_category, forward_cursor_tag >::type cursor_category
Definition: linq_selectmany.hpp:93
collection_store_type::store collection_store
Definition: linq_selectmany.hpp:98
void inc()
Definition: linq_selectmany.hpp:125