diff options
author | Scott Blomquist <sblom@microsoft.com> | 2013-01-17 17:48:09 -0800 |
---|---|---|
committer | Scott Blomquist <sblom@microsoft.com> | 2013-01-17 17:48:09 -0800 |
commit | a0f2bbe9acce9483163de730341cf89ec0331d2b (patch) | |
tree | ad38c78a7f0e916051edce90374586961dcdfee4 /Ix/CPP/src | |
parent | 6aa4590ea5952d3ba5e1f99b6ede76d856f88617 (diff) | |
download | RxCpp-a0f2bbe9acce9483163de730341cf89ec0331d2b.tar.gz |
New directory structure.
Diffstat (limited to 'Ix/CPP/src')
-rw-r--r-- | Ix/CPP/src/cpplinq/linq.hpp | 531 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_cursor.hpp | 340 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_groupby.hpp | 195 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_iterators.hpp | 194 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_last.hpp | 85 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_select.hpp | 52 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_selectmany.hpp | 107 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_skip.hpp | 35 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_take.hpp | 96 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/linq_where.hpp | 69 | ||||
-rw-r--r-- | Ix/CPP/src/cpplinq/util.hpp | 223 |
11 files changed, 1927 insertions, 0 deletions
diff --git a/Ix/CPP/src/cpplinq/linq.hpp b/Ix/CPP/src/cpplinq/linq.hpp new file mode 100644 index 0000000..1070331 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq.hpp @@ -0,0 +1,531 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +/// +/// namespace cpplinq +/// ----------------- +/// +/// Defines a number of range-based composable operators for enumerating and modifying collections +/// +/// The general design philosophy is to +/// (1) emulate the composable query patterns introduced in C# Linq +/// (2) preserve iterator category and writability where possible +/// For instance, like C# Linq we have a select operator to project one sequence into a new one. +/// Unlike Linq, invoking Select on a random access sequence will yield you a _random_ access sequence. +/// +/// The general workflow begins with 'from()' which normally only takes a reference +/// the the collection in question. However, from that point on, all operators function +/// by value, so that the range can store any necessary state, rather than duplicating it +/// onto iterator pairs. +/// +/// In subsequent documentation, "powers" lists which powers that the operator attempts to preserve if +/// available on on the input sequence. Some iterator powers may be skipped - in such a case, round down +/// to the next supported power (e.g. if 'fwd' and 'rnd', an input of 'bidi' will round down to a 'fwd' result). +/// +/// +/// +/// class linq_query +/// ---------------- +/// +/// from(container&) +/// ================ +/// - Result: Query +/// +/// Construct a new query, from an lvalue reference to a collection. Does not copy the collection +/// +/// +/// +/// from(iter, iter) +/// ================ +/// - Result: Query +/// +/// Construct a new query, from an iterator pair. +/// +/// +/// +/// query.select(map) +/// ========================== +/// - Result: Query +/// - Powers: input, forward, bidirectional, random access +/// +/// For each element `x` in the input sequences, computes `map(x)` for the result sequence. +/// +/// +/// +/// query.where(pred) -> query +/// ========================== +/// - Result: Query +/// - Powers: input, forward, bidirectional +/// +/// Each element `x` in the input appears in the output if `pred(x)` is true. +/// +/// The expression `pred(x)` is evaluated only when moving iterators (op++, op--). +/// Dereferencing (op*) does not invoke the predicate. +/// +/// +/// +/// query.groupby(keymap [, keyequal]) +/// ==================================== +/// Result: Query of groups. Each group has a 'key' field, and is a query of elements from the input. +/// Powers: forward +/// +/// +/// +/// query.any([pred]) +/// ================= +/// - Result: bool +/// +/// (No argument) Returns true if sequence is non-empty. Equivalent to `query.begin()!=query.end()` +/// +/// (One argument) Returns true if the sequence contains any elements for which `pred(element)` is true. +/// Equivalent to `query.where(pred).any()`. +/// +/// +/// +/// query.all(pred) +/// =============== +/// - Result: bool +/// +/// Returns true if `pred` holds for all elements in the sequence. Equivalent to `!query.any(std::not1(pred))` +/// +/// +/// +/// query.take(n) +/// ============= +/// - Result: query +/// - Powers: input, forward, random access (not bidirectional) +/// +/// Returns a sequence that contains up to `n` items from the original sequence. +/// +/// +/// +/// query.skip(n) +/// ============= +/// - Result: query +/// - Powers: input, forward, random access (not bidirectional) +/// +/// Returns a sequence that skips the first `n` items from the original sequence, or an empty sequence if +/// fewer than `n` were available on input. +/// +/// Note: begin() takes O(n) time when input iteration power is weaker than random access. +/// +/// +/// +/// query.count([pred]) +/// =================== +/// - Result: size_t +/// +/// _TODO: should use inner container's iterator distance type instead._ +/// +/// (Zero-argument) Returns the number of elements in the range. +/// Equivalent to `std::distance(query.begin(), query.end())` +/// +/// (One-argument) Returns the number of elements for whicht `pred(element)` is true. +/// Equivalent to `query.where(pred).count()` +/// + + + +#if !defined(CPPLINQ_LINQ_HPP) +#define CPPLINQ_LINQ_HPP +#pragma once + +#include <functional> +#include <iterator> +#include <list> +#include <map> +#include <memory> +#include <utility> +#include <type_traits> +#include <vector> + + + +// some configuration macros +#if _MSC_VER > 1600 || __cplusplus > 199711L +#define LINQ_USE_RVALUEREF 1 +#endif + +#if (defined(_MSC_VER) && _CPPRTTI) || !defined(_MSC_VER) +#define LINQ_USE_RTTI 1 +#endif + + +// individual features +#include "util.hpp" +#include "linq_cursor.hpp" +#include "linq_iterators.hpp" +#include "linq_select.hpp" +#include "linq_take.hpp" +#include "linq_skip.hpp" +#include "linq_groupby.hpp" +#include "linq_where.hpp" +#include "linq_last.hpp" +#include "linq_selectmany.hpp" + + + + +namespace cpplinq +{ + +namespace detail +{ + template<class Pred> + struct not1_{ + Pred pred; + not1_(Pred p) : pred(p) + {} + template<class T> + bool operator()(const T& value) + { + return !pred(value); + } + }; + // note: VC2010's std::not1 doesn't support lambda expressions. provide our own. + template<class Pred> + not1_<Pred> not1(Pred p) { return not1_<Pred>(p); } +} + +namespace detail { + template <class U> + struct cast_to { + template <class T> + U operator()(const T& value) const { + return static_cast<U>(value); + } + }; +} + +template <class Collection> +class linq_driver +{ + typedef typename Collection::cursor::element_type + element_type; + typedef typename Collection::cursor::reference_type + reference_type; +public: + typedef cursor_iterator<typename Collection::cursor> + iterator; + + linq_driver(Collection c) : c(c) {} + + + // -------------------- linq core methods -------------------- + + template <class KeyFn> + linq_driver< linq_groupby<Collection, KeyFn> > groupby(KeyFn fn) + { + return linq_groupby<Collection, KeyFn>(c, std::move(fn) ); + } + + // TODO: groupby(keyfn, eq) + + // TODO: join... + + template <class Selector> + linq_driver< linq_select<Collection, Selector> > select(Selector sel) const { + return linq_select<Collection, Selector>(c, std::move(sel) ); + } + + template <class Fn> + linq_driver< linq_select_many<Collection, Fn, detail::default_select_many_selector> > + select_many(Fn fn) const + { + return linq_select_many<Collection, Fn, detail::default_select_many_selector>(c, fn, detail::default_select_many_selector()); + } + + template <class Fn, class Fn2> + linq_driver< linq_select_many<Collection, Fn, Fn2> > select_many(Fn fn, Fn2 fn2) const + { + return linq_select_many<Collection, Fn, Fn2>(c, fn, fn2); + } + + template <class Predicate> + linq_driver< linq_where<Collection, Predicate> > where(Predicate p) const { + return typename linq_where<Collection, Predicate>(c, std::move(p) ); + } + + + // -------------------- linq peripheral methods -------------------- + + template <class Fn> + element_type aggregate(Fn fn) const + { + auto it = begin(); + if (it == end()) { + return element_type(); + } + + reference_type first = *it; + return std::accumulate(++it, end(), first, fn); + } + + template <class T, class Fn> + T aggregate(T initialValue, Fn fn) const + { + return std::accumulate(begin(), end(), initialValue, fn); + } + + bool any() const { return !empty(cur); } + + template <class Predicate> + bool any(Predicate p) const { + auto it = std::find_if(begin(), end(), p); + return it != end(); + } + + template <class Predicate> + bool all(Predicate p) const { + auto it = std::find_if(begin(), end(), detail::not1(p)); + return it == end(); + } + + // TODO: average + + template <class U> + auto cast() + -> decltype(static_cast<linq_driver*>(0)->select(detail::cast_to<U>())) + { + return this->select(detail::cast_to<U>()); + } + + // TODO: concat + + bool contains(const typename Collection::cursor::element_type& value) const { + return std::find(begin(), end(), value) != end(); + } + + typename std::iterator_traits<iterator>::distance_type count() const { + return std::distance(begin(), end()); + } + + template <class Predicate> + typename std::iterator_traits<iterator>::distance_type count(Predicate p) const { + auto filtered = this->where(p); + return std::distance(begin(filtered), end(filtered)); + } + + // TODO: default_if_empty + + // TODO: distinct() + // TODO: distinct(cmp) + + reference_type element_at(size_t ix) const { + auto cur = c.get_cursor(); + while(ix && !cur.empty()) { + cur.inc(); + --ix; + } + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + element_type element_at_or_default(size_t ix) const { + auto cur = c.get_cursor(); + while(ix && !cur.empty()) { + cur.inc(); + -- ix; + } + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + bool empty() const { + return !this->any(); + } + + // TODO: except(second) + // TODO: except(second, eq) + + reference_type first() const { + auto cur = c.get_cursor(); + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + template <class Predicate> + reference_type first(Predicate pred) const { + auto cur = c.get_cursor(); + while (!cur.empty() && !pred(cur.get())) { + cur.inc(); + } + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + element_type first_or_default() const { + auto cur = c.get_cursor(); + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + template <class Predicate> + element_type first_or_default(Predicate pred) const { + auto cur = c.get_cursor(); + while (!cur.empty() && !pred(cur.get())) { + cur.inc(); + } + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + // TODO: intersect(second) + // TODO: intersect(second, eq) + + // note: forward cursors and beyond can provide a clone, so we can refer to the element directly + typename std::conditional< + std::is_convertible< + typename Collection::cursor::cursor_category, + forward_cursor_tag>::value, + reference_type, + element_type>::type + last() const + { + return linq_last_(c.get_cursor(), typename Collection::cursor::cursor_category()); + } + + template <class Predicate> + reference_type last(Predicate pred) const + { + auto cur = c.where(pred).get_cursor(); + return linq_last_(cur, typename decltype(cur)::cursor_category()); + } + + element_type last_or_default() const + { + return linq_last_or_default_(c.get_cursor(), typename Collection::cursor::cursor_category()); + } + + template <class Predicate> + element_type last_or_default(Predicate pred) const + { + auto cur = c.where(pred).get_cursor(); + return linq_last_or_default_(cur, typename decltype(cur)::cursor_category()); + } + + reference_type max() const + { + return max(std::less<element_type>()); + } + + template <class Compare> + reference_type max(Compare less) const + { + auto it = std::max_element(begin(), end(), less); + if (it == end()) + throw std::logic_error("max performed on empty range"); + + return *it; + } + + reference_type min() const + { + return min(std::less<element_type>()); + } + + template <class Compare> + reference_type min(Compare less) const + { + auto it = std::min_element(begin(), end(), less); + if (it == end()) + throw std::logic_error("max performed on empty range"); + + return *it; + } + + // TODO: order_by(sel) + // TODO: order_by(sel, less) + // TODO: order_by_descending(sel) + // TODO: order_by_descending(sel, less) + + // TODO: sequence_equal(second) + // TODO: sequence_equal(second, eq) + + // TODO: single / single_or_default + + linq_driver<linq_skip<Collection>> skip(size_t n) const { + return linq_skip<Collection>(c, n); + } + + // TODO: skip_while(pred) + + // TODO: sum + + linq_driver<linq_take<Collection>> take(size_t n) const { + return linq_take<Collection>(c, n); + } + + // TODO: take_while + + // TODO: then_by / then_by_descending ? + + // TODO: to_... + + // TODO: union(second) + // TODO: union(eq) + + // TODO: zip + + // -------------------- conversion methods -------------------- + + std::vector<typename Collection::cursor::element_type> to_vector() const + { + return std::vector<typename Collection::cursor::element_type>(begin(), end()); + } + + // -------------------- container/range methods -------------------- + + iterator begin() const { auto cur = c.get_cursor(); return !cur.empty() ? iterator(cur) : iterator(); } + iterator end() const { return iterator(); } + linq_driver& operator=(const linq_driver& other) { c = other.c; return *this; } + template <class TC2> + linq_driver& operator=(const linq_driver<TC2>& other) { c = other.c; return *this; } + + typename std::iterator_traits<iterator>::reference + operator[](size_t ix) const { + return *(begin()+=ix); + } + + // -------------------- collection methods (leaky abstraction) -------------------- + + typedef typename Collection::cursor cursor; + cursor get_cursor() { return c.get_cursor(); } + + linq_driver< dynamic_collection<typename Collection::cursor::reference_type> > + late_bind() const + { + return dynamic_collection<typename Collection::cursor::reference_type>(c); + } + +private: + Collection c; +}; + +// TODO: should probably use reference-wrapper instead? +template <class TContainer> +linq_driver<iter_cursor<typename util::container_traits<TContainer>::iterator>> from(TContainer& c) +{ + auto cur = iter_cursor<typename util::container_traits<TContainer>::iterator>(begin(c), end(c)); + return std::move(cur); +} +template <class T> +const linq_driver<T>& from(const linq_driver<T>& c) +{ + return c; +} +template <class Iter> +linq_driver<iter_cursor<Iter>> from(Iter start, Iter finish) +{ + return iter_cursor<Iter>(start, finish); +} + +template <class TContainer> +linq_driver<TContainer> from_value(const TContainer& c) +{ + return linq_driver<TContainer>(c); +} + +} + +#endif // defined(CPPLINQ_LINQ_HPP) + diff --git a/Ix/CPP/src/cpplinq/linq_cursor.hpp b/Ix/CPP/src/cpplinq/linq_cursor.hpp new file mode 100644 index 0000000..827c488 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_cursor.hpp @@ -0,0 +1,340 @@ +// 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 + +/// 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 <size_t n> 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 <size_t> + struct iter_to_cursor_category_; + + template <class Iter> + struct iter_to_cursor_category + { + static const size_t catIx = sizeof(detail::get_category_from_iterator(typename std::iterator_traits<Iter>::iterator_category()) /*.value*/ ); + typedef typename iter_to_cursor_category_<catIx>::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 <class Cat1, class Cat2> + 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 <class Cat1, class Cat2, class Cat3 = void> + struct min_cursor_category : min_cursor_category<typename min_cursor_category<Cat1, Cat2>::type, Cat3> + { + }; + + template <class Cat1, class Cat2> + struct min_cursor_category<Cat1, Cat2> + : std::conditional< + less_or_equal_cursor_category<Cat2, Cat1>::value, + Cat2, + Cat1> + { + }; + + template <class Collection> + struct cursor_type { + typedef decltype(cursor(*static_cast<Collection*>(0))) type; + }; + } + + // simultaniously models a cursor and a cursor-collection + template <class Iterator> + class iter_cursor : collection_tag { + public: + + typedef iter_cursor cursor; + + typedef typename std::remove_reference<typename std::iterator_traits<Iterator>::value_type>::type + element_type; + typedef typename std::iterator_traits<Iterator>::reference + reference_type; + typedef typename util::iter_to_cursor_category<Iterator>::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<Iterator>::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(ptrdiff_t n) { current += n; } + size_t size() { return fin-start; } + void position() { return current-start; } + void truncate(size_t n) { + if (n > fin-current) { + fin = current + n; + } + } + + + iter_cursor(Iterator start, Iterator fin) + : start(start) + , fin(std::move(fin)) + , current(start) + { + } + + iter_cursor(Iterator start, Iterator fin, Iterator current) + : start(std::move(start)) + , fin(std::move(fin)) + , current(std::move(current)) + { + } + + iter_cursor get_cursor() const { return *this; } + + private: + Iterator current; + Iterator start, fin; + }; + + + template <class T> + 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 T> + class dynamic_cursor : collection_tag + { + template <class Cur> + struct instance : cursor_interface<T> + { + 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<T>* copy() const + { + return new instance(*this); + } + }; + + std::unique_ptr<cursor_interface<T>> myCur; + + public: + typedef forward_cursor_tag cursor_category; // TODO: not strictly true! + typedef typename std::remove_reference<T>::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 <class Cursor> + dynamic_cursor(Cursor cursor) + : myCur(new instance<Cursor>(std::move(cursor))) + { + } + + template <class Iterator> + dynamic_cursor(Iterator start, Iterator end) + { + *this = iter_cursor<Iterator>(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 <class T> + struct container_interface + { + virtual dynamic_cursor<T> get_cursor() const = 0; + }; + + template <class T> + class dynamic_collection + { + std::shared_ptr< container_interface<T> > container; + + template <class Container> + struct instance : container_interface<T> + { + Container c; + + instance(Container c) : c(c) + { + } + + dynamic_cursor<T> get_cursor() const + { + return c.get_cursor(); + } + }; + + public: + typedef dynamic_cursor<T> cursor; + + dynamic_collection() {} + + dynamic_collection(const dynamic_collection& other) + : container(other.container) + { + } + + // container or query + template <class Container> + dynamic_collection(Container c) + : container(new instance<Container>(c)) + { + } + + // container or query + template <class Iterator> + dynamic_collection(Iterator begin, Iterator end) + : container(new instance< iter_cursor<Iterator> >(iter_cursor<Iterator>(begin, end))) + { + } + + dynamic_cursor<T> get_cursor() const { + return container ? container->get_cursor() : dynamic_cursor<T>(); + } + }; +} + +#endif // !defined(CPPLINQ_LINQ_CURSOR_HPP diff --git a/Ix/CPP/src/cpplinq/linq_groupby.hpp b/Ix/CPP/src/cpplinq/linq_groupby.hpp new file mode 100644 index 0000000..c521e5e --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_groupby.hpp @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_GROUPBY_HPP) +#define CPPLINQ_LINQ_GROUPBY_HPP +#pragma once + +namespace cpplinq +{ + +template <class Iter, class Key> +struct group +{ + Key key; + Iter start; + Iter fin; + + typedef Iter iterator; + typedef Iter const_iterator; + + group(){} + + group(const Key& key) : key(key) + { + } + + Iter begin() const { return start; } + Iter end() const { return fin; } +}; + +struct default_equality +{ + template <class T> + bool operator()(const T& a, const T& b) const { + return a == b; + } +}; +struct default_less +{ + template<class T> + bool operator()(const T& a, const T& b) const { + return a < b; + } +}; + +// progressively constructs grouping as user iterates over groups and elements +// within each group. Performs this task by building a std::list of element +// iterators with equal elements within each group. +// +// invariants: +// - relative order of groups corresponds to relative order of each group's first +// element, as they appeared in the input sequence. +// - relative order of elements within a group correspond to relative order +// as they appeared in the input sequence. +// +// requires: +// Iter must be a forward iterator. +template <class Collection, class KeyFn, class Compare = default_less> +class linq_groupby +{ + typedef typename Collection::cursor + inner_cursor; + + typedef typename util::result_of<KeyFn(typename inner_cursor::element_type)>::type + key_type; + + typedef std::list<typename inner_cursor::element_type> + element_list_type; + + typedef group<typename element_list_type::iterator, key_type> + group_type; + + typedef std::list<group_type> + group_list_type; + +private: + struct impl_t + { + // TODO: would be faster to use a chunked list, where + // pointers are invalidated but iterators are not. Need + // benchmarks first + + element_list_type elements; + std::list<group_type> groups; + std::map<key_type, group_type*, Compare> groupIndex; + + + + KeyFn keySelector; + Compare comp; + + impl_t(inner_cursor cur, + KeyFn keySelector, + Compare comp = Compare()) + : keySelector(keySelector) + , groupIndex(comp) + { + // TODO: make lazy + insert_all(std::move(cur)); + } + + void insert_all(inner_cursor cur) + { + while(!cur.empty()) { + insert(cur.get()); + cur.inc(); + } + } + void insert(typename inner_cursor::reference_type element) + { + key_type key = keySelector(element); + auto groupPos = groupIndex.find(key); + if(groupPos == groupIndex.end()) { + // new group + bool firstGroup = groups.empty(); + + elements.push_back(element); + if(!firstGroup) { + // pop new element out of previous group + --groups.back().fin; + } + + // build new group + groups.push_back(group_type(key)); + group_type& newGroup = groups.back(); + + groupIndex.insert( std::make_pair(key, &newGroup) ); + + newGroup.fin = elements.end(); + --(newGroup.start = newGroup.fin); + } else { + // add to existing group at end + elements.insert(groupPos->second->end(), element); + } + } + }; + +public: + struct cursor { + typedef group_type + element_type; + + typedef element_type + reference_type; + + typedef forward_cursor_tag + cursor_category; + + cursor(inner_cursor cur, + KeyFn keyFn, + Compare comp = Compare()) + { + impl.reset(new impl_t(cur, keyFn, comp)); + inner = impl->groups.begin(); + fin = impl->groups.end(); + } + + void forget() { } // nop on forward-only cursors + bool empty() const { + return inner == fin; + } + void inc() { + if (inner == fin) { + throw std::logic_error("attempt to iterate past end of range"); + } + ++inner; + } + reference_type get() const { + return *inner; + } + + private: + std::shared_ptr<impl_t> impl; + typename std::list<group_type>::iterator inner; + typename std::list<group_type>::iterator fin; + }; + + linq_groupby(Collection c, + KeyFn keyFn, + Compare comp = Compare()) + : c(c), keyFn(keyFn), comp(comp) + { + } + + cursor get_cursor() const { return cursor(c.get_cursor(), keyFn, comp); } + +private: + Collection c; + KeyFn keyFn; + Compare comp; +}; + +} + +#endif // !defined(CPPLINQ_LINQ_GROUPBY_HPP) + diff --git a/Ix/CPP/src/cpplinq/linq_iterators.hpp b/Ix/CPP/src/cpplinq/linq_iterators.hpp new file mode 100644 index 0000000..ed267ec --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_iterators.hpp @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_ITERATORS_HPP) +#define CPPLINQ_LINQ_ITERATORS_HPP +#pragma once + +namespace cpplinq { + + // if a member, provides the straightforward implementation of various redundant operators. For example, + // providing -> for any iterator providing *, and so forth. + struct use_default_iterator_operators {}; + + #define CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS \ + operator ::cpplinq::use_default_iterator_operators() const { return ::cpplinq::use_default_iterator_operators(); } + + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + Iter + >::type + operator+(const Iter& it, typename std::iterator_traits<Iter>::distance_type n) { + return it += n; + } + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + Iter + >::type + operator-(const Iter& it, typename std::iterator_traits<Iter>::distance_type n) { + return it -= n; + } + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + Iter + >::type + operator-=(const Iter& it, typename std::iterator_traits<Iter>::distance_type n) { + return it += (-n); + } + + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + bool + >::type + operator!=(const Iter& it, const Iter& it2) { + return !(it == it2); + } + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + bool + >::type + operator>(const Iter& it, const Iter& it2) { + return it2 < it; + } + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + bool + >::type + operator<=(const Iter& it, const Iter& it2) { + return !(it2 < it); + } + template <class Iter> + typename std::enable_if< + std::is_convertible<Iter, use_default_iterator_operators>::value, + bool + >::type + operator>=(const Iter& it, const Iter& it2) { + return !(it < it2); + } + + namespace util { + template <class Iter, class T> + typename std::iterator_traits<Iter>::pointer deref_iterator(const Iter& it) { + return detail::deref_iterator(it, std::identity<typename std::iterator_traits<Iter>::reference>()); + } + + template <class Iter, class T> + T* deref_iterator(const Iter& it, std::identity<T&>) { + return &*it; + } + + template <class Iter, class T> + util::value_ptr<T> deref_iterator(const Iter& it, std::identity<T>) { + return util::value_ptr<T>(*it); + } + } + + + template <class Iter> + class iter_range + { + Iter start, finish; + public: + + CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS + + typedef Iter iterator; + typedef typename iterator::value_type value_type; + + explicit iter_range(Iter start, Iter finish) : start(start), finish(finish) {} + iterator begin() const { return start; } + iterator end() const { return finish; } + }; + template <class Iter> + iter_range<Iter> make_range(Iter start, Iter finish) { + return iter_range<Iter>(start, finish); + } + + // decays into a onepass/forward iterator + template <class Cursor> + class cursor_iterator + : public std::iterator<std::forward_iterator_tag, + typename Cursor::element_type, + ptrdiff_t, + typename std::conditional<std::is_reference<typename Cursor::reference_type>::value, + typename std::add_pointer<typename Cursor::element_type>::type, + util::value_ptr<typename Cursor::element_type>>::type, + typename Cursor::reference_type> + { + public: + CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS; + + cursor_iterator(Cursor cur) : cur(cur) { + } + + cursor_iterator() : cur() { + } + + bool operator==(const cursor_iterator& other) const { + return !cur && !other.cur; + } + + typename Cursor::reference_type operator*() const { + return cur->get(); + } + + pointer operator->() const { + auto& v = **this; + return &v; + } + + cursor_iterator& operator++() { + cur->inc(); + + if (cur->empty()) { cur.reset(); } + return *this; + } + + cursor_iterator& operator+=(ptrdiff_t n) { + cur->skip(n); + + if (cur->empty()) { cur.reset(); } + return *this; + } + + + + private: + bool empty() const { + !cur || cur->empty(); + } + + util::maybe<Cursor> cur; + }; + + template <class Container> + class container_range + { + Container c; + + public: + typedef cursor_iterator<typename Container::cursor> iterator; + + container_range(Container c) : c(c) + { + } + + iterator begin() const + { + return iterator(c.get_cursor()); + } + + iterator end() const + { + return iterator(); + } + }; + +} + +#endif diff --git a/Ix/CPP/src/cpplinq/linq_last.hpp b/Ix/CPP/src/cpplinq/linq_last.hpp new file mode 100644 index 0000000..fd08823 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_last.hpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_LAST_HPP) +#define CPPLINQ_LINQ_LAST_HPP +#pragma once + +namespace cpplinq { + + template <class Cursor> + typename Cursor::element_type + linq_last_(Cursor c, onepass_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + typename Cursor::element_type elem = c.get(); + for(;;) { + c.inc(); + if (c.empty()) break; + elem = c.get(); + } + return std::move(elem); + } + + // TODO: bidirectional iterator in constant time + + template <class Cursor> + typename Cursor::reference_type + linq_last_(Cursor c, forward_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + Cursor best = c; + for(;;) { + c.inc(); + if (c.empty()) break; + best = c; + } + return best.get(); + } + + template <class Cursor> + typename Cursor::reference_type + linq_last_(Cursor c, random_access_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + c.skip(c.size()-1); + return c.get(); + } + + template <class Cursor> + typename Cursor::element_type + linq_last_or_default_(Cursor c, onepass_cursor_tag) + { + typename Cursor::element_type elem; + while(!c.empty()) { + elem = c.get(); + c.inc(); + } + return std::move(elem); + } + + template <class Cursor> + typename Cursor::element_type + linq_last_or_default_(Cursor c, forward_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + Cursor best = c; + for(;;) { + c.inc(); + if (c.empty()) break; + best = c; + } + return best.get(); + } + + template <class Cursor> + typename Cursor::element_type + linq_last_or_default_(Cursor c, random_access_cursor_tag) + { + if (c.empty()) { return typename Cursor::element_type(); } + c.skip(c.size()-1); + return c.get(); + } + +} + +#endif // CPPLINQ_LINQ_LAST_HPP diff --git a/Ix/CPP/src/cpplinq/linq_select.hpp b/Ix/CPP/src/cpplinq/linq_select.hpp new file mode 100644 index 0000000..650a1dc --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_select.hpp @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_SELECT_HPP) +#define CPPLINQ_LINQ_SELECT_HPP +#pragma once + +namespace cpplinq +{ + template <class Collection, class Selector> + class linq_select + { + typedef typename Collection::cursor + inner_cursor; + public: + struct cursor { + typedef typename util::result_of<Selector(typename inner_cursor::element_type)>::type + reference_type; + typedef typename std::remove_reference<reference_type>::type + element_type; + typedef typename inner_cursor::cursor_category + cursor_category; + + cursor(const inner_cursor& cur, Selector sel) : cur(cur), sel(std::move(sel)) {} + + void forget() { cur.forget(); } + bool empty() const { return cur.empty(); } + void inc() { cur.inc(); } + reference_type get() const { return sel(cur.get()); } + + bool atbegin() const { return cur.atbegin(); } + void dec() { cur.dec(); } + + void skip(size_t n) { cur.skip(n); } + size_t position() const { return cur.position(); } + size_t size() const { return cur.size(); } + private: + inner_cursor cur; + Selector sel; + }; + + linq_select(const Collection& c, Selector sel) : c(c), sel(sel) {} + + cursor get_cursor() const { return cursor(c.get_cursor(), sel); } + + private: + Collection c; + Selector sel; + }; + +} + +#endif // defined(CPPLINQ_LINQ_SELECT_HPP) diff --git a/Ix/CPP/src/cpplinq/linq_selectmany.hpp b/Ix/CPP/src/cpplinq/linq_selectmany.hpp new file mode 100644 index 0000000..45f0574 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_selectmany.hpp @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#include "util.hpp" +#include "linq_cursor.hpp" + +namespace cpplinq +{ + namespace detail + { + struct default_select_many_selector + { + template <class T1, class T2> + auto operator()(T1&& t1, T2&& t2) const + -> decltype(std::forward<T2>(t2)) + { + return std::forward<T2>(t2); + } + }; + } + + // cur<T> -> (T -> cur<element_type>) -> cur<element_type> + template <class Container1, class Fn, class Fn2> + class linq_select_many + { + template <class T> static T instance(); // for type inference + + Container1 c1; + Fn fn; + Fn2 fn2; + + typedef typename Container1::cursor Cur1; + typedef decltype(from(instance<Fn>()(instance<Cur1>().get()))) Container2; + typedef typename Container2::cursor Cur2; + + public: + class cursor + { + public: + typedef typename util::min_cursor_category<typename Cur1::cursor_category, + typename Cur2::cursor_category, + forward_cursor_tag>::type + cursor_category; + typedef typename Cur2::reference_type reference_type; + typedef typename Cur2::element_type element_type; + + private: + // TODO: we need to lazy eval somehow, but this feels wrong. + Cur1 cur1; + dynamic_cursor<reference_type> cur2; + Fn fn; + Fn2 fn2; + + public: + cursor(Cur1 cur1, const Fn& fn, const Fn2& fn2) + : cur1(std::move(cur1)), fn(fn), fn2(fn2) + { + auto container2 = fn(cur1.get()); + cur2 = from(container2).get_cursor(); + } + + bool empty() const + { + return cur2.empty(); + } + + void inc() + { + cur2.inc(); + thunk(); + } + + reference_type get() const + { + return fn2(cur1.get(), cur2.get()); + } + + private: + void thunk() + { + // refill cur2 + while (cur2.empty() && !cur1.empty()) { + cur1.inc(); + if (cur1.empty()) + break; + + auto container2 = fn(cur1.get()); + cur2 = from(container2).get_cursor(); + } + } + }; + + linq_select_many(Container1 c1, Fn fn, Fn2 fn2) + : c1(std::move(c1)), fn(std::move(fn)), fn2(std::move(fn2)) + { + } + + cursor get_cursor() const + { + return cursor(c1.get_cursor(), fn, fn2); + } + }; +} + + + diff --git a/Ix/CPP/src/cpplinq/linq_skip.hpp b/Ix/CPP/src/cpplinq/linq_skip.hpp new file mode 100644 index 0000000..422592a --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_skip.hpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_SKIP_HPP) +#define CPPLINQ_LINQ_SKIP_HPP +#pragma once + +namespace cpplinq +{ + template <class Collection> + struct linq_skip + { + public: + typedef typename Collection::cursor cursor; + + linq_skip(const Collection& c, size_t n) : c(c), n(n) {} + + cursor get_cursor() const { + size_t rem = n; + + auto cur = c.get_cursor(); + while(rem-- && !cur.empty()) { + cur.inc(); + } + cur.forget(); + return std::move(cur); + } + + private: + Collection c; + size_t n; + }; +} +#endif // !defined(CPPLINQ_LINQ_SKIP_HPP) + + diff --git a/Ix/CPP/src/cpplinq/linq_take.hpp b/Ix/CPP/src/cpplinq/linq_take.hpp new file mode 100644 index 0000000..63f7449 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_take.hpp @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_TAKE_HPP) +#define CPPLINQ_LINQ_TAKE_HPP +#pragma once + +namespace cpplinq +{ + template <class InnerCursor> + struct linq_take_cursor + { + typedef typename InnerCursor::element_type element_type; + typedef typename InnerCursor::reference_type reference_type; + typedef typename InnerCursor::cursor_category cursor_category; + + linq_take_cursor(const InnerCursor& cur, size_t rem) : cur(cur), rem(rem) {} + + void forget() { cur.forget(); } + bool empty() const { return cur.empty() || rem == 0; } + void inc() { cur.inc(); --rem; } + reference_type get() const { return cur.get(); } + + bool atbegin() const { return cur.atbegin(); } + void dec() { cur.dec(); --rem; } + + void skip(size_t n) { cur.skip(n); rem -= n; } + size_t position() const { return cur.position(); } + size_t size() const { return cur.size(); } + + private: + InnerCursor cur; + size_t rem; + }; + + namespace detail { + template <class Collection> + linq_take_cursor<typename Collection::cursor> + take_get_cursor_( + const Collection& c, + size_t n, + onepass_cursor_tag + ) + { + return linq_take_cursor<typename Collection::cursor>(c.get_cursor(), n); + } + + template <class Collection> + typename Collection::cursor + take_get_cursor_( + const Collection& c, + size_t n, + random_access_cursor_tag + ) + { + auto cur = c.get_cursor(); + if (cur.size() > n) { + cur.truncate(n); + } + return std::move(cur); + } + } + + template <class Collection> + struct linq_take + { + typedef typename std::conditional< + util::less_or_equal_cursor_category< + random_access_cursor_tag, + typename Collection::cursor::cursor_category>::value, + typename Collection::cursor, + linq_take_cursor<typename Collection::cursor>>::type + cursor; + + linq_take(const Collection& c, size_t n) : c(c), n(n) {} + + cursor get_cursor() const { + return detail::take_get_cursor_(c, n, typename Collection::cursor::cursor_category()); + } + + Collection c; + size_t n; + }; + + template <class Collection> + auto get_cursor( + const linq_take<Collection>& take + ) + -> decltype(get_cursor_(take, typename Collection::cursor::cursor_category())) + { + return get_cursor_(take, typename Collection::cursor::cursor_category()); + } + + +} +#endif // !defined(CPPLINQ_LINQ_TAKE_HPP) + diff --git a/Ix/CPP/src/cpplinq/linq_where.hpp b/Ix/CPP/src/cpplinq/linq_where.hpp new file mode 100644 index 0000000..4f66ba7 --- /dev/null +++ b/Ix/CPP/src/cpplinq/linq_where.hpp @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_WHERE_HPP) +#define CPPLINQ_LINQ_WHERE_HPP +#pragma once + +namespace cpplinq +{ + template <class Collection, class Predicate> + class linq_where + { + typedef typename Collection::cursor + inner_cursor; + public: + struct cursor { + typedef typename util::min_iterator_category< + bidirectional_cursor_tag, + typename inner_cursor::cursor_category>::type + cursor_category; + typedef typename inner_cursor::element_type + element_type; + typedef typename inner_cursor::reference_type + reference_type; + + cursor(const inner_cursor& cur, const Predicate& p) : cur(cur), pred(p) + { + if (!pred(cur.get())) { + this->inc(); + } + } + + void forget() { cur.forget(); } + bool empty() const { return cur.empty(); } + void inc() { + for (;;) { + cur.inc(); + if (cur.empty() || pred(cur.get())) break; + } + } + reference_type get() const { + return cur.get(); + } + + bool atbegin() const { return atbegin(cur); } + void dec() { + for (;;) { + cur.dec(); + if (pred(cur.get())) break; + } + } + private: + inner_cursor cur; + Predicate pred; + }; + + linq_where(const Collection& c, Predicate pred) : c(c), pred(pred) {} + + cursor get_cursor() const { + return cursor(c.get_cursor(), pred); + } + + private: + Collection c; + Predicate pred; + }; +} + +#endif // !defined(CPPLINQ_LINQ_WHERE_HPP) + diff --git a/Ix/CPP/src/cpplinq/util.hpp b/Ix/CPP/src/cpplinq/util.hpp new file mode 100644 index 0000000..6fb62ff --- /dev/null +++ b/Ix/CPP/src/cpplinq/util.hpp @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_UTIL_HPP) +#define CPPLINQ_LINQ_UTIL_HPP +#pragma once + +namespace cpplinq { namespace util { + + template <class Container> + struct container_traits { + typedef typename Container::iterator iterator; + typedef typename std::iterator_traits<iterator>::value_type value_type; + typedef typename std::iterator_traits<iterator>::iterator_category iterator_category; + + // TODO: conservative definition for now. + enum { is_writable_iterator = + std::is_reference<typename std::iterator_traits<iterator>::reference>::value + && std::is_same<typename std::remove_cv<value_type>::type, + typename std::remove_cv<typename std::remove_reference<typename std::iterator_traits<iterator>::reference>::type>::type>::value + }; + }; + + template <> + struct container_traits<int>; + + template <class Container> + struct container_traits<Container&> + : container_traits<Container> + {}; + template <class Container> + struct container_traits<const Container> + : container_traits<Container> + { + typedef typename Container::const_iterator iterator; + }; + + // Note: returns false if no partial order exists between two + // particular iterator categories, such as with some of the boost categories + template <class Cat1, class Cat2> + struct less_or_equal_iterator_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 <class Cat1, class Cat2> + struct min_iterator_category + : std::conditional< + less_or_equal_iterator_category<Cat2, Cat1>::value, + Cat2, + Cat1> + { + }; + +#if 0 +#define CppLinq_GET_ITERATOR_TYPE(TContainer) \ + decltype(begin(static_cast<TContainer*>(0))) +#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \ + decltype(begin(static_cast<const TContainer*>(0))) +#else +#define CppLinq_GET_ITERATOR_TYPE(TContainer) \ + typename ::cpplinq::util::container_traits<TContainer>::iterator +#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \ + typename ::cpplinq::util::container_traits<TContainer>::const_iterator +#endif + + // VC10's std::tr1::result_of is busted with lambdas. use decltype instead on vc10 and later +#if defined(_MSC_VER) && _MSC_VER >= 1600 + namespace detail { + template <class T> T instance(); + }; + template <class Fn> struct result_of; + template <class Fn> + struct result_of<Fn()> { + typedef decltype(detail::instance<Fn>()()) type; + }; + template <class Fn, class A0> + struct result_of<Fn(A0)> { + typedef decltype(detail::instance<Fn>()(detail::instance<A0>())) type; + }; + template <class Fn, class A0, class A1> + struct result_of<Fn(A0,A1)> { + typedef decltype(detail::instance<Fn>()(detail::instance<A0>(), + detail::instance<A1>())) type; + }; + template <class Fn, class A0, class A1, class A2> + struct result_of<Fn(A0,A1,A2)> { + typedef decltype(detail::instance<Fn>()(detail::instance<A0>(), + detail::instance<A1>(), + detail::instance<A2>())) type; + }; + template <class Fn, class A0, class A1, class A2, class A3> + struct result_of<Fn(A0,A1,A2,A3)> { + typedef decltype(detail::instance<Fn>()(detail::instance<A0>(), + detail::instance<A1>(), + detail::instance<A2>(), + detail::instance<A3>())) type; + }; +#else + template <class T> + struct result_of<T> : std::tr1::result_of<T> {}; +#endif + + // faux pointer proxy for iterators that dereference to a value rather than reference, such as selectors + template <class T> + struct value_ptr + { + T value; + value_ptr(const T& pvalue) : value(value) + {} + value_ptr(const T* pvalue) : value(*pvalue) + {} + const T* operator->() + { + return &value; + } + }; + + + template <class T> + class maybe + { + bool is_set; + typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type + storage; + public: + maybe() + : is_set(false) + { + } + + maybe(T value) + : is_set(false) + { + new (reinterpret_cast<T*>(&storage)) T(value); + is_set = true; + } + + maybe(const maybe& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast<T*>(&storage)) T(*other.get()); + is_set = true; + } + } + maybe(maybe&& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast<T*>(&storage)) T(std::move(*other.get())); + is_set = true; + other.reset(); + } + } + + ~maybe() + { + reset(); + } + + void reset() + { + if (is_set) { + is_set = false; + reinterpret_cast<T*>(&storage)->~T(); + } + } + + T* get() { + return is_set ? reinterpret_cast<T*>(&storage) : 0; + } + + const T* get() const { + return is_set ? reinterpret_cast<const T*>(&storage) : 0; + } + + void set(const T& value) { + if (is_set) { + *reinterpret_cast<T*>(&storage) = value; + } else { + new (reinterpret_cast<T*>(&storage)) T(value); + is_set = true; + } + } + + T& operator*() { return *get(); } + const T& operator*() const { return *get(); } + T* operator->() { return get(); } + const T* operator->() const { return get(); } + + maybe& operator=(const T& other) { + set(other); + } + maybe& operator=(const maybe& other) { + if (const T* pother = other.get()) { + set(*pother); + } else { + reset(); + } + return *this; + } + + // boolean-like operators + operator T*() { return get(); } + operator const T*() const { return get(); } + + private: + + }; +}} + + +#endif //CPPLINQ_UTIL_HPP + |