// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. #if !defined(CPPLINQ_LINQ_CURSOR_HPP) #define CPPLINQ_LINQ_CURSOR_HPP #pragma once #include /// cursors /// ---------- /// It should be noted that CppLinq uses a slightly different iterator concept, one where iterators /// know their extents. This sacrificed some generality, but it adds convenience and improves /// some performance in some cases. Notably, captures need only be stored once instead of twice in /// most use cases. /// /// Cursors and Ranges are always classes. /// /// To get a cursor from a range: /// /// get_cursor(range) -> cur /// /// Unlike boost ranges, CppLinq cursors are mutated directly, and may "shed state" as they are /// mutated. For example, a GroupBy range will drop references to earlier groups, possibly /// permitting freeing them. /// /// Onepass cursor /// =========== /// - empty(cur) -> bool : at end of sequence /// - inc(cur) /// - get(cur) -> T /// - copy ctor : duplicate reference to seek position /// /// Forward cursor /// ============= /// - copy ctor : true duplicate of seek position /// /// Bidirectional cursor /// ==================== /// - forget() : notes the current element as the new 'begin' point /// - atbegin(cur) -> bool /// - dec(cur) /// /// Random access cursor /// ==================== /// - skip(cur, n) /// - position(cur) -> n /// - size(cur) -> n /// - truncate(n) : keep only n more elements /// /// As well, cursors must define the appropriate type/typedefs: /// - cursor_category :: { onepass_cursor_tag, forward_cursor_tag, bidirectional_cursor_tag, random_access_cursor_tag } /// - element_type /// - reference_type : if writable, element_type& or such. else, == element_type /// - namespace cpplinq { // used to identify cursor-based collections struct collection_tag {}; struct onepass_cursor_tag {}; struct forward_cursor_tag : onepass_cursor_tag {}; struct bidirectional_cursor_tag : forward_cursor_tag {}; struct random_access_cursor_tag : bidirectional_cursor_tag {}; struct noread_cursor_tag {}; // TODO: remove if not used struct readonly_cursor_tag : noread_cursor_tag {}; struct readwrite_cursor_tag : readonly_cursor_tag {}; // standard cursor adaptors namespace util { namespace detail { template struct type_to_size { char value[n]; }; type_to_size<1> get_category_from_iterator(std::input_iterator_tag); type_to_size<2> get_category_from_iterator(std::forward_iterator_tag); type_to_size<3> get_category_from_iterator(std::bidirectional_iterator_tag); type_to_size<4> get_category_from_iterator(std::random_access_iterator_tag); } template struct iter_to_cursor_category_; template struct iter_to_cursor_category { static const std::size_t catIx = sizeof(detail::get_category_from_iterator(typename std::iterator_traits::iterator_category()) /*.value*/ ); typedef typename iter_to_cursor_category_::type type; }; template <> struct iter_to_cursor_category_<1> { typedef onepass_cursor_tag type; }; template <> struct iter_to_cursor_category_<2> { typedef forward_cursor_tag type; }; template <> struct iter_to_cursor_category_<3> { typedef bidirectional_cursor_tag type; }; template <> struct iter_to_cursor_category_<4> { typedef random_access_cursor_tag type; }; // Note: returns false if no partial order exists between two // particular iterator categories, such as with some of the boost categories template struct less_or_equal_cursor_category { private: typedef char yes; typedef struct { char c1,c2; } no; static yes invoke(Cat1); static no invoke(...); public: enum { value = (sizeof(invoke(Cat2())) == sizeof(yes)) }; }; // Return the weaker of the two iterator categories. Make sure // a non-standard category is in the second argument position, as // this metafunction will default to the first value if the order is undefined template struct min_cursor_category : min_cursor_category::type, Cat3> { }; template struct min_cursor_category : std::conditional< less_or_equal_cursor_category::value, Cat2, Cat1> { }; template struct cursor_type { typedef decltype(cursor(*static_cast(0))) type; }; } // simultaniously models a cursor and a cursor-collection template class iter_cursor : collection_tag { public: typedef iter_cursor cursor; typedef typename std::remove_reference::value_type>::type element_type; typedef typename std::iterator_traits::reference reference_type; typedef typename util::iter_to_cursor_category::type cursor_category; void forget() { start = current; } bool empty() const { return current == fin; } void inc() { if (current == fin) throw std::logic_error("inc past end"); ++current; } typename std::iterator_traits::reference get() const { return *current; } bool atbegin() const { return current == start; } void dec() { if (current == start) throw std::logic_error("dec past begin"); --current; } void skip(std::ptrdiff_t n) { current += n; } std::size_t size() { return fin-start; } void position() { return current-start; } void truncate(std::size_t n) { if (n > fin-current) { fin = current + n; } } iter_cursor(Iterator start, Iterator fin) : current(start) , start(start) , fin(std::move(fin)) { } iter_cursor(Iterator start, Iterator fin, Iterator current) : current(std::move(current)) , start(std::move(start)) , fin(std::move(fin)) { } iter_cursor get_cursor() const { return *this; } private: Iterator current; Iterator start, fin; }; template struct cursor_interface { virtual bool empty() const = 0; virtual void inc() = 0; virtual cursor_interface* copy() const = 0; virtual T get() const = 0; virtual ~cursor_interface() {} }; template class dynamic_cursor : collection_tag { template struct instance : cursor_interface { Cur innerCursor; instance(Cur cursor) : innerCursor(std::move(cursor)) { } virtual bool empty() const { return innerCursor.empty(); } virtual void inc() { innerCursor.inc(); } virtual T get() const { return innerCursor.get(); } virtual cursor_interface* copy() const { return new instance(*this); } }; std::unique_ptr> myCur; public: typedef forward_cursor_tag cursor_category; // TODO: not strictly true! typedef typename std::remove_reference::type element_type; typedef T reference_type; dynamic_cursor() {} dynamic_cursor(const dynamic_cursor& other) : myCur(other.myCur ? other.myCur->copy() : nullptr) { } dynamic_cursor(dynamic_cursor&& other) : myCur(other.myCur.release()) { } template dynamic_cursor(Cursor cursor) : myCur(new instance(std::move(cursor))) { } template dynamic_cursor(Iterator start, Iterator end) { *this = iter_cursor(start, end); } bool empty() const { return !myCur || myCur->empty(); } void inc() { myCur->inc(); } T get() const { return myCur->get(); } dynamic_cursor& operator=(dynamic_cursor other) { std::swap(myCur, other.myCur); return *this; } }; template struct container_interface { virtual dynamic_cursor get_cursor() const = 0; }; template class dynamic_collection { std::shared_ptr< container_interface > container; template struct instance : container_interface { Container c; instance(Container c) : c(c) { } dynamic_cursor get_cursor() const { return c.get_cursor(); } }; public: typedef dynamic_cursor cursor; dynamic_collection() {} dynamic_collection(const dynamic_collection& other) : container(other.container) { } // container or query template dynamic_collection(Container c) : container(new instance(c)) { } // container or query template dynamic_collection(Iterator begin, Iterator end) : container(new instance< iter_cursor >(iter_cursor(begin, end))) { } dynamic_cursor get_cursor() const { return container ? container->get_cursor() : dynamic_cursor(); } }; } #endif // !defined(CPPLINQ_LINQ_CURSOR_HPP