feat add rxcpp

This commit is contained in:
tqcq
2024-03-14 20:50:17 +08:00
parent 15bdc54bef
commit 90da26f0a4
124 changed files with 27992 additions and 511 deletions

584
3party/cpplinq/linq.hpp Normal file
View File

@ -0,0 +1,584 @@
// 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: std::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
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#include <functional>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <list>
#include <map>
#include <set>
#include <memory>
#include <utility>
#include <type_traits>
#include <vector>
#include <cstddef>
// 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
#if defined(__clang__)
#if __has_feature(cxx_rvalue_references)
#define LINQ_USE_RVALUEREF 1
#endif
#if __has_feature(cxx_rtti)
#define LINQ_USE_RTTI 1
#endif
#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 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 { auto cur = c.get_cursor(); return !cur.empty(); }
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
#if !defined(__clang__)
// Clang complains that linq_driver is not complete until the closing brace
// so (linq_driver*)->select() cannot be resolved.
template <class U>
auto cast()
-> decltype(static_cast<linq_driver*>(0)->select(detail::cast_to<U>()))
{
return this->select(detail::cast_to<U>());
}
#endif
// TODO: concat
bool contains(const typename Collection::cursor::element_type& value) const {
return std::find(begin(), end(), value) != end();
}
typename std::iterator_traits<iterator>::difference_type count() const {
return std::distance(begin(), end());
}
template <class Predicate>
typename std::iterator_traits<iterator>::difference_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(std::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(std::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(std::size_t n) const {
return linq_skip<Collection>(c, n);
}
// TODO: skip_while(pred)
template<typename ITEM = element_type>
typename std::enable_if<std::is_default_constructible<ITEM>::value, ITEM>::type sum() const {
ITEM seed{};
return sum(seed);
}
element_type sum(element_type seed) const {
return std::accumulate(begin(), end(), seed);
}
template <typename Selector, typename Result = typename std::result_of<Selector(element_type)>::type>
typename std::enable_if<std::is_default_constructible<Result>::value, Result>::type sum(Selector sel) const {
return from(begin(), end()).select(sel).sum();
}
template <typename Selector, typename Result = typename std::result_of<Selector(element_type)>::type>
Result sum(Selector sel, Result seed) const {
return from(begin(), end()).select(sel).sum(seed);
}
linq_driver<linq_take<Collection>> take(std::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());
}
std::list<typename Collection::cursor::element_type> to_list() const
{
return std::list<typename Collection::cursor::element_type>(begin(), end());
}
std::set<typename Collection::cursor::element_type> to_set() const
{
return std::set<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[](std::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>(std::begin(c), std::end(c));
return 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);
}
}
#pragma pop_macro("min")
#pragma pop_macro("max")
#endif // defined(CPPLINQ_LINQ_HPP)

View File

@ -0,0 +1,342 @@
// 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 <cstddef>
/// 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 <std::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 <std::size_t>
struct iter_to_cursor_category_;
template <class Iter>
struct iter_to_cursor_category
{
static const std::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(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 <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

View File

@ -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)

View File

@ -0,0 +1,196 @@
// 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
#include <cstddef>
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 deref_iterator(it, util::identity<typename std::iterator_traits<Iter>::reference>());
}
template <class Iter, class T>
T* deref_iterator(const Iter& it, util::identity<T&>) {
return &*it;
}
template <class Iter, class T>
util::value_ptr<T> deref_iterator(const Iter& it, util::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,
std::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();
}
typename cursor_iterator::pointer operator->() const {
auto& v = **this;
return &v;
}
cursor_iterator& operator++() {
cur->inc();
if (cur->empty()) { cur.reset(); }
return *this;
}
cursor_iterator& operator+=(std::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

View File

@ -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 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 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

View File

@ -0,0 +1,54 @@
// 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
#include <cstddef>
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(std::size_t n) { cur.skip(n); }
std::size_t position() const { return cur.position(); }
std::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)

View File

@ -0,0 +1,164 @@
// 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"
#include <type_traits>
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);
}
};
}
namespace detail
{
template <typename Fn, typename Arg>
struct resolve_select_many_fn_return_type
{
typedef decltype(std::declval<Fn>()(std::declval<Arg>())) value;
};
template <typename TCol>
struct value_collection_adapter
{
value_collection_adapter(const TCol& col)
: _collection(col){}
value_collection_adapter(const value_collection_adapter& src)
: _collection(src._collection) {}
value_collection_adapter(value_collection_adapter && src)
: _collection(std::move(src._collection)) {}
const TCol& get() const
{
return _collection;
}
TCol& get()
{
return _collection;
}
private:
TCol _collection;
};
template<typename TCol>
struct collection_store_type
{
typedef typename std::remove_reference<TCol>::type collection_type;
typedef std::reference_wrapper<collection_type> reference_store_type;
typedef value_collection_adapter<collection_type> value_store_type;
typedef typename std::conditional<std::is_reference<TCol>::value, reference_store_type, value_store_type>::type store;
};
}
// 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;
typedef typename detail::resolve_select_many_fn_return_type<Fn, typename Cur1::element_type>::value inner_collection;
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;
typedef detail::collection_store_type<inner_collection> collection_store_type;
typedef typename collection_store_type::store collection_store;
typedef std::shared_ptr<collection_store> collection_store_ptr;
private:
// TODO: we need to lazy eval somehow, but this feels wrong.
Cur1 cur1;
dynamic_cursor<reference_type> cur2;
Fn fn;
Fn2 fn2;
collection_store_ptr store;
public:
cursor(Cur1 cur1, const Fn& fn, const Fn2& fn2)
: cur1(std::move(cur1)), fn(fn), fn2(fn2)
{
if (!cur1.empty())
{
store = std::make_shared<collection_store>(fn(cur1.get()));
cur2 = from(store->get()).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;
store = std::make_shared<collection_store>(fn(cur1.get()));
cur2 = from(store->get()).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);
}
};
}

View File

@ -0,0 +1,37 @@
// 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
#include <cstddef>
namespace cpplinq
{
template <class Collection>
struct linq_skip
{
public:
typedef typename Collection::cursor cursor;
linq_skip(const Collection& c, std::size_t n) : c(c), n(n) {}
cursor get_cursor() const {
std::size_t rem = n;
auto cur = c.get_cursor();
while(rem-- && !cur.empty()) {
cur.inc();
}
cur.forget();
return cur;
}
private:
Collection c;
std::size_t n;
};
}
#endif // !defined(CPPLINQ_LINQ_SKIP_HPP)

View File

@ -0,0 +1,98 @@
// 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
#include <cstddef>
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, std::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(std::size_t n) { cur.skip(n); rem -= n; }
std::size_t position() const { return cur.position(); }
std::size_t size() const { return cur.size(); }
private:
InnerCursor cur;
std::size_t rem;
};
namespace detail {
template <class Collection>
linq_take_cursor<typename Collection::cursor>
take_get_cursor_(
const Collection& c,
std::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,
std::size_t n,
random_access_cursor_tag
)
{
auto cur = c.get_cursor();
if (cur.size() > n) {
cur.truncate(n);
}
return 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, std::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;
std::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)

View File

@ -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 (!cur.empty() && !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)

232
3party/cpplinq/util.hpp Normal file
View File

@ -0,0 +1,232 @@
// 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;
};
#elif defined(_MSC_VER)
template <class T>
struct result_of<T> : std::tr1::result_of<T> {};
#else
using std::result_of;
#endif
template<class Type>
struct identity
{
typedef Type type;
Type operator()(const Type& left) const {return left;}
};
// 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& value) : 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(T value) {
if (is_set) {
*reinterpret_cast<T*>(&storage) = std::move(value);
} else {
new (reinterpret_cast<T*>(&storage)) T(std::move(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