Home | History | Annotate | Download | only in cpplinq
      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>
     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:
    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 
    156         cursor get_cursor() const
    157         {
    158             return cursor(c1.get_cursor(), fn, fn2);
    159         }
    160     };
    161 }
    162 
    163 
    164 
    165