585 lines
16 KiB
C++
585 lines
16 KiB
C++
|
// 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)
|
||
|
|