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 #if !defined(CPPLINQ_LINQ_CURSOR_HPP)
      4 #define CPPLINQ_LINQ_CURSOR_HPP
      5 #pragma once
      6 
      7 #include <cstddef>
      8 
      9 /// cursors
     10 /// ----------
     11 /// It should be noted that CppLinq uses a slightly different iterator concept, one where iterators
     12 /// know their extents. This sacrificed some generality, but it adds convenience and improves
     13 /// some performance in some cases. Notably, captures need only be stored once instead of twice in
     14 /// most use cases.
     15 ///
     16 /// Cursors and Ranges are always classes.
     17 ///
     18 /// To get a cursor from a range:
     19 ///
     20 ///    get_cursor(range) -> cur
     21 ///
     22 /// Unlike boost ranges, CppLinq cursors are mutated directly, and may "shed state" as they are
     23 /// mutated. For example, a GroupBy range will drop references to earlier groups, possibly
     24 /// permitting freeing them.
     25 ///
     26 /// Onepass cursor
     27 /// ===========
     28 /// -   empty(cur) -> bool  : at end of sequence
     29 /// -   inc(cur)
     30 /// -   get(cur) -> T
     31 /// -   copy ctor           : duplicate reference to seek position
     32 ///
     33 /// Forward cursor
     34 /// =============
     35 /// -   copy ctor           : true duplicate of seek position
     36 ///
     37 /// Bidirectional cursor
     38 /// ====================
     39 /// -   forget()            : notes the current element as the new 'begin' point
     40 /// -   atbegin(cur) -> bool
     41 /// -   dec(cur)
     42 ///
     43 /// Random access cursor
     44 /// ====================
     45 /// -   skip(cur, n)
     46 /// -   position(cur) -> n
     47 /// -   size(cur)     -> n
     48 /// -   truncate(n)         : keep only n more elements
     49 ///
     50 /// As well, cursors must define the appropriate type/typedefs:
     51 /// -   cursor_category  :: { onepass_cursor_tag, forward_cursor_tag, bidirectional_cursor_tag, random_access_cursor_tag }
     52 /// -   element_type
     53 /// -   reference_type   : if writable, element_type& or such. else, == element_type
     54 /// -
     55 
     56 
     57 
     58 namespace cpplinq {
     59 
     60     // used to identify cursor-based collections
     61     struct collection_tag {};
     62 
     63     struct onepass_cursor_tag {};
     64     struct forward_cursor_tag : onepass_cursor_tag  {};
     65     struct bidirectional_cursor_tag : forward_cursor_tag {};
     66     struct random_access_cursor_tag : bidirectional_cursor_tag {};
     67 
     68     struct noread_cursor_tag {}; // TODO: remove if not used
     69     struct readonly_cursor_tag : noread_cursor_tag {};
     70     struct readwrite_cursor_tag : readonly_cursor_tag {};
     71 
     72 
     73 
     74     // standard cursor adaptors
     75 
     76     namespace util
     77     {
     78         namespace detail
     79         {
     80             template <std::size_t n> struct type_to_size { char value[n]; };
     81 
     82             type_to_size<1> get_category_from_iterator(std::input_iterator_tag);
     83             type_to_size<2> get_category_from_iterator(std::forward_iterator_tag);
     84             type_to_size<3> get_category_from_iterator(std::bidirectional_iterator_tag);
     85             type_to_size<4> get_category_from_iterator(std::random_access_iterator_tag);
     86         }
     87 
     88         template <std::size_t>
     89         struct iter_to_cursor_category_;
     90 
     91         template <class Iter>
     92         struct iter_to_cursor_category
     93         {
     94             static const std::size_t catIx = sizeof(detail::get_category_from_iterator(typename std::iterator_traits<Iter>::iterator_category()) /*.value*/  );
     95             typedef typename iter_to_cursor_category_<catIx>::type type;
     96         };
     97 
     98         template <> struct iter_to_cursor_category_<1> { typedef onepass_cursor_tag type; };
     99         template <> struct iter_to_cursor_category_<2> { typedef forward_cursor_tag type; };
    100         template <> struct iter_to_cursor_category_<3> { typedef bidirectional_cursor_tag type; };
    101         template <> struct iter_to_cursor_category_<4> { typedef random_access_cursor_tag type; };
    102 
    103 
    104         // Note: returns false if no partial order exists between two
    105         // particular iterator categories, such as with some of the boost categories
    106         template <class Cat1, class Cat2>
    107         struct less_or_equal_cursor_category
    108         {
    109         private:
    110             typedef char yes;
    111             typedef struct { char c1,c2; } no;
    112             static yes invoke(Cat1);
    113             static no invoke(...);
    114         public:
    115             enum { value = (sizeof(invoke(Cat2())) == sizeof(yes)) };
    116         };
    117 
    118         // Return the weaker of the two iterator categories. Make sure
    119         //   a non-standard category is in the second argument position, as
    120         //   this metafunction will default to the first value if the order is undefined
    121         template <class Cat1, class Cat2, class Cat3 = void>
    122         struct min_cursor_category : min_cursor_category<typename min_cursor_category<Cat1, Cat2>::type, Cat3>
    123         {
    124         };
    125 
    126         template <class Cat1, class Cat2>
    127         struct min_cursor_category<Cat1, Cat2>
    128             : std::conditional<
    129                 less_or_equal_cursor_category<Cat2, Cat1>::value,
    130                 Cat2,
    131                 Cat1>
    132         {
    133         };
    134 
    135         template <class Collection>
    136         struct cursor_type {
    137             typedef decltype(cursor(*static_cast<Collection*>(0))) type;
    138         };
    139     }
    140 
    141     // simultaniously models a cursor and a cursor-collection
    142     template <class Iterator>
    143     class iter_cursor : collection_tag {
    144     public:
    145 
    146         typedef iter_cursor cursor;
    147 
    148         typedef typename std::remove_reference<typename std::iterator_traits<Iterator>::value_type>::type
    149             element_type;
    150         typedef typename std::iterator_traits<Iterator>::reference
    151             reference_type;
    152         typedef typename util::iter_to_cursor_category<Iterator>::type
    153             cursor_category;
    154 
    155         void forget() { start = current; }
    156         bool empty() const { return current == fin; }
    157         void inc() {
    158             if (current == fin)
    159                 throw std::logic_error("inc past end");
    160             ++current;
    161         }
    162         typename std::iterator_traits<Iterator>::reference get() const { return *current; }
    163 
    164         bool atbegin() const { return current == start; }
    165         void dec() {
    166             if (current == start)
    167                 throw std::logic_error("dec past begin");
    168             --current;
    169         }
    170 
    171         void skip(std::ptrdiff_t n) { current += n; }
    172         std::size_t size() { return fin-start; }
    173         void position() { return current-start; }
    174         void truncate(std::size_t n) {
    175             if (n > fin-current) {
    176                 fin = current + n;
    177             }
    178         }
    179 
    180 
    181         iter_cursor(Iterator start, Iterator fin)
    182         : current(start)
    183         , start(start)
    184         , fin(std::move(fin))
    185         {
    186         }
    187 
    188         iter_cursor(Iterator start, Iterator fin, Iterator current)
    189         : current(std::move(current))
    190         , start(std::move(start))
    191         , fin(std::move(fin))
    192         {
    193         }
    194 
    195         iter_cursor get_cursor() const { return *this; }
    196 
    197     private:
    198         Iterator current;
    199         Iterator start, fin;
    200     };
    201 
    202 
    203     template <class T>
    204     struct cursor_interface
    205     {
    206         virtual bool empty() const = 0;
    207         virtual void inc() = 0;
    208         virtual cursor_interface* copy() const = 0;
    209 
    210         virtual T get() const = 0;
    211 
    212         virtual ~cursor_interface() {}
    213     };
    214 
    215     template <class T>
    216     class dynamic_cursor : collection_tag
    217     {
    218         template <class Cur>
    219         struct instance : cursor_interface<T>
    220         {
    221             Cur innerCursor;
    222 
    223             instance(Cur cursor) : innerCursor(std::move(cursor))
    224             {
    225             }
    226             virtual bool empty() const
    227             {
    228                 return innerCursor.empty();
    229             }
    230             virtual void inc()
    231             {
    232                 innerCursor.inc();
    233             }
    234             virtual T get() const
    235             {
    236                 return innerCursor.get();
    237             }
    238             virtual cursor_interface<T>* copy() const
    239             {
    240                 return new instance(*this);
    241             }
    242         };
    243 
    244         std::unique_ptr<cursor_interface<T>> myCur;
    245 
    246     public:
    247         typedef forward_cursor_tag cursor_category; // TODO: not strictly true!
    248         typedef typename std::remove_reference<T>::type element_type;
    249         typedef T reference_type;
    250 
    251         dynamic_cursor() {}
    252 
    253         dynamic_cursor(const dynamic_cursor& other)
    254         : myCur(other.myCur ? other.myCur->copy() : nullptr)
    255         {
    256         }
    257 
    258         dynamic_cursor(dynamic_cursor&& other)
    259         : myCur(other.myCur.release())
    260         {
    261         }
    262 
    263         template <class Cursor>
    264         dynamic_cursor(Cursor cursor)
    265         : myCur(new instance<Cursor>(std::move(cursor)))
    266         {
    267         }
    268 
    269         template <class Iterator>
    270         dynamic_cursor(Iterator start, Iterator end)
    271         {
    272             *this = iter_cursor<Iterator>(start, end);
    273         }
    274 
    275         bool empty() const { return !myCur || myCur->empty(); }
    276         void inc() { myCur->inc(); }
    277         T get() const { return myCur->get(); }
    278 
    279         dynamic_cursor& operator=(dynamic_cursor other)
    280         {
    281             std::swap(myCur, other.myCur);
    282             return *this;
    283         }
    284     };
    285 
    286     template <class T>
    287     struct container_interface
    288     {
    289         virtual dynamic_cursor<T> get_cursor() const = 0;
    290     };
    291 
    292     template <class T>
    293     class dynamic_collection
    294     {
    295         std::shared_ptr< container_interface<T> > container;
    296 
    297         template <class Container>
    298         struct instance : container_interface<T>
    299         {
    300             Container c;
    301 
    302             instance(Container c) : c(c)
    303             {
    304             }
    305 
    306             dynamic_cursor<T> get_cursor() const
    307             {
    308                 return c.get_cursor();
    309             }
    310         };
    311 
    312     public:
    313         typedef dynamic_cursor<T> cursor;
    314 
    315         dynamic_collection() {}
    316 
    317         dynamic_collection(const dynamic_collection& other)
    318         : container(other.container)
    319         {
    320         }
    321 
    322         // container or query
    323         template <class Container>
    324         dynamic_collection(Container c)
    325         : container(new instance<Container>(c))
    326         {
    327         }
    328 
    329         // container or query
    330         template <class Iterator>
    331         dynamic_collection(Iterator begin, Iterator end)
    332         : container(new instance< iter_cursor<Iterator> >(iter_cursor<Iterator>(begin, end)))
    333         {
    334         }
    335 
    336         dynamic_cursor<T> get_cursor() const {
    337             return container ? container->get_cursor() : dynamic_cursor<T>();
    338         }
    339     };
    340 }
    341 
    342 #endif // !defined(CPPLINQ_LINQ_CURSOR_HPP
    343