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