• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
forget()155         void forget() { start = current; }
empty() const156         bool empty() const { return current == fin; }
inc()157         void inc() {
158             if (current == fin)
159                 throw std::logic_error("inc past end");
160             ++current;
161         }
get() const162         typename std::iterator_traits<Iterator>::reference get() const { return *current; }
163 
atbegin() const164         bool atbegin() const { return current == start; }
dec()165         void dec() {
166             if (current == start)
167                 throw std::logic_error("dec past begin");
168             --current;
169         }
170 
skip(std::ptrdiff_t n)171         void skip(std::ptrdiff_t n) { current += n; }
size()172         std::size_t size() { return fin-start; }
position()173         void position() { return current-start; }
truncate(std::size_t n)174         void truncate(std::size_t n) {
175             if (n > fin-current) {
176                 fin = current + n;
177             }
178         }
179 
180 
iter_cursor(Iterator start,Iterator fin)181         iter_cursor(Iterator start, Iterator fin)
182         : current(start)
183         , start(start)
184         , fin(std::move(fin))
185         {
186         }
187 
iter_cursor(Iterator start,Iterator fin,Iterator current)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 
get_cursor() const195         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 
~cursor_interfacecpplinq::cursor_interface212         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 
instancecpplinq::dynamic_cursor::instance223             instance(Cur cursor) : innerCursor(std::move(cursor))
224             {
225             }
emptycpplinq::dynamic_cursor::instance226             virtual bool empty() const
227             {
228                 return innerCursor.empty();
229             }
inccpplinq::dynamic_cursor::instance230             virtual void inc()
231             {
232                 innerCursor.inc();
233             }
getcpplinq::dynamic_cursor::instance234             virtual T get() const
235             {
236                 return innerCursor.get();
237             }
copycpplinq::dynamic_cursor::instance238             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 
dynamic_cursor()251         dynamic_cursor() {}
252 
dynamic_cursor(const dynamic_cursor & other)253         dynamic_cursor(const dynamic_cursor& other)
254         : myCur(other.myCur ? other.myCur->copy() : nullptr)
255         {
256         }
257 
dynamic_cursor(dynamic_cursor && other)258         dynamic_cursor(dynamic_cursor&& other)
259         : myCur(other.myCur.release())
260         {
261         }
262 
263         template <class Cursor>
dynamic_cursor(Cursor cursor)264         dynamic_cursor(Cursor cursor)
265         : myCur(new instance<Cursor>(std::move(cursor)))
266         {
267         }
268 
269         template <class Iterator>
dynamic_cursor(Iterator start,Iterator end)270         dynamic_cursor(Iterator start, Iterator end)
271         {
272             *this = iter_cursor<Iterator>(start, end);
273         }
274 
empty() const275         bool empty() const { return !myCur || myCur->empty(); }
inc()276         void inc() { myCur->inc(); }
get() const277         T get() const { return myCur->get(); }
278 
operator =(dynamic_cursor other)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 
instancecpplinq::dynamic_collection::instance302             instance(Container c) : c(c)
303             {
304             }
305 
get_cursorcpplinq::dynamic_collection::instance306             dynamic_cursor<T> get_cursor() const
307             {
308                 return c.get_cursor();
309             }
310         };
311 
312     public:
313         typedef dynamic_cursor<T> cursor;
314 
dynamic_collection()315         dynamic_collection() {}
316 
dynamic_collection(const dynamic_collection & other)317         dynamic_collection(const dynamic_collection& other)
318         : container(other.container)
319         {
320         }
321 
322         // container or query
323         template <class Container>
dynamic_collection(Container c)324         dynamic_collection(Container c)
325         : container(new instance<Container>(c))
326         {
327         }
328 
329         // container or query
330         template <class Iterator>
dynamic_collection(Iterator begin,Iterator end)331         dynamic_collection(Iterator begin, Iterator end)
332         : container(new instance< iter_cursor<Iterator> >(iter_cursor<Iterator>(begin, end)))
333         {
334         }
335 
get_cursor() const336         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