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>
operator ()cpplinq::detail::default_select_many_selector17             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         {
value_collection_adaptercpplinq::detail::value_collection_adapter36             value_collection_adapter(const TCol& col)
37                 : _collection(col){}
38 
value_collection_adaptercpplinq::detail::value_collection_adapter39             value_collection_adapter(const value_collection_adapter& src)
40                 : _collection(src._collection) {}
41 
value_collection_adaptercpplinq::detail::value_collection_adapter42             value_collection_adapter(value_collection_adapter && src)
43                 : _collection(std::move(src._collection)) {}
44 
getcpplinq::detail::value_collection_adapter45             const TCol& get() const
46             {
47                 return _collection;
48             }
49 
getcpplinq::detail::value_collection_adapter50             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>
72     class linq_select_many
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
93                 cursor_category;
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;
104             dynamic_cursor<reference_type>      cur2;
105             Fn                                  fn;
106             Fn2                                 fn2;
107             collection_store_ptr                store;
108 
109         public:
cursor(Cur1 cur1,const Fn & fn,const Fn2 & fn2)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 
empty() const120             bool empty() const
121             {
122                 return cur2.empty();
123             }
124 
inc()125             void inc()
126             {
127                 cur2.inc();
128                 thunk();
129             }
130 
get() const131             reference_type get() const
132             {
133                 return fn2(cur1.get(), cur2.get());
134             }
135 
136         private:
thunk()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 
linq_select_many(Container1 c1,Fn fn,Fn2 fn2)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 
get_cursor() const156         cursor get_cursor() const
157         {
158             return cursor(c1.get_cursor(), fn, fn2);
159         }
160     };
161 }
162 
163 
164 
165