#pragma once #include #include #include #include #include "Nullable.h" #include "Exception.h" #include "MetaProgramming.h" // #define LINQ_DEBUG #ifdef LINQ_DEBUG #include "Misc.h" #define LINQ_TYPE_NAME() utl::TypeHelper::name() #define LINQ_CTOR() do { std::cout << "CTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) #define LINQ_COPY_CTOR() do { std::cout << "COPY(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) #define LINQ_MOVE_CTOR() do { std::cout << "MOVE(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) #define LINQ_DTOR() do { std::cout << "DTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) #else #define LINQ_TYPE_NAME() while(0) #define LINQ_CTOR() while(0) #define LINQ_COPY_CTOR() while(0) #define LINQ_MOVE_CTOR() while(0) #define LINQ_DTOR() while(0) #endif namespace linq { namespace __impl { struct tag_range { }; struct tag_builder { }; /* meta programming **********************************************************************/ template struct mp_array_properties; template struct mp_array_properties { using size = std::integral_constant; using value_type = T; using iterator_type = T*; }; template using mp_range_value_type = typename utl::mp_remove_ref::value_type; /* helper types **************************************************************************/ template struct wrapper { using value_type = T; value_type value; inline value_type operator*() const { return value; } inline wrapper& operator=(const wrapper& other) { value = other.value; return *this; } inline wrapper& operator=(wrapper&& other) { value = std::move(other).value; return *this; } template inline wrapper(X&& v) : value(std::forward(v)) { } inline wrapper(const value_type& v) : value(v) { } inline wrapper(const wrapper& other) : value(other.value) { } inline wrapper(wrapper&& other) : value(std::move(other.value)) { } }; template struct wrapper { using value_type = T&; using storage_type = T*; storage_type value; inline value_type operator*() const { return *value; } inline wrapper& operator=(const wrapper& other) { value = other.value; return *this; } inline wrapper& operator=(wrapper&& other) { value = std::move(other.value); return *this; } inline wrapper(value_type v) : value(&v) { } inline wrapper(const wrapper& other) : value(other.value) { } inline wrapper(wrapper&& other) : value(std::move(other.value)) { } }; template struct op_wrapper_less { using predicate_type = TPredicate; using value_type = wrapper; predicate_type predicate; inline bool operator()(const value_type& l, const value_type& r) { return predicate(*l, *r); } inline op_wrapper_less(const predicate_type& lp) : predicate(lp) { } inline op_wrapper_less(const op_wrapper_less& other) : predicate(other.predicate) { } inline op_wrapper_less(op_wrapper_less&& other) : predicate(std::move(other.predicate)) { } }; template struct range_wrapper { using range_type = TRange; using this_type = range_wrapper; using value_type = mp_range_value_type; range_type range; inline value_type& front() { return range.front(); } inline bool next() { return range.next(); } template inline auto operator >> (TBuilder&& builder) & { return builder.build(range); } template inline auto operator >> (TBuilder&& builder) && { return builder.build(std::move(range)); } template range_wrapper(Args&&... args) : range(std::forward(args)...) { } range_wrapper(const this_type& other) : range(other.range) { } range_wrapper(this_type&& other) : range(std::move(other.range)) { } }; template struct lookup { public: using key_type = TKey; using clean_key_type = utl::mp_remove_ref; using value_type = TValue; using this_type = lookup; using wrapped_key_type = wrapper; using wrapped_value_type = wrapper; using keys_value_type = std::pair; using keys_type = std::vector; using values_type = std::vector; using range_indices_type = std::pair; private: struct lookup_range; struct lookup_key_value_range; using lookup_range_wrapper = range_wrapper; using lookup_key_value_range_wrapper = range_wrapper; public: using range_type = lookup_key_value_range_wrapper; private: struct lookup_range : public tag_range { using value_type = lookup::value_type; enum class State { Initialize, Iterating, Finished, }; const values_type& values; size_t current; size_t end; State state; inline value_type& front() { assert(state == State::Iterating); assert(current < end); return *values[current]; } inline bool next() { switch (state) { case State::Iterating: { ++current; } case State::Initialize: { auto hasElements = (current < end); state = (hasElements ? State::Iterating : State::Finished); return hasElements; } default: { return false; } } } inline lookup_range(const values_type& v, size_t c, size_t e) : values (v), current (c), end (e), state (State::Initialize) { LINQ_CTOR(); } inline lookup_range(const lookup_range& other) : values (other.values), current (other.current), end (other.end), state (other.state) { LINQ_COPY_CTOR(); } inline lookup_range(lookup_range&& other) : values (std::move(other.values)), current (std::move(other.current)), end (std::move(other.end)), state (std::move(other.state)) { LINQ_MOVE_CTOR(); } inline ~lookup_range() { LINQ_DTOR(); } }; struct lookup_key_value_range : public tag_range { using value_type = std::pair; using iterator_type = typename keys_type::const_iterator; lookup container; bool initialized; range_indices_type range_indices; iterator_type current; iterator_type end; inline value_type front() { assert(initialized); assert(current != end); return value_type( *current->first, std::move(container.createRange(range_indices))); } inline bool next() { if (!initialized) { initialized = true; current = container._keys.begin(); end = container._keys.end(); if (current == end) return false; } else { if (current == end) return false; ++current; if (current == end) return false; } range_indices = container.findKey(*current->first); return true; } template lookup_key_value_range(C&& c) : container (std::forward(c)), initialized (false) { LINQ_CTOR(); } lookup_key_value_range(const lookup_key_value_range& other) : container (other.container), initialized (other.initialized), range_indices (other.range_indices), current (other.current), end (other.end) { LINQ_COPY_CTOR(); } lookup_key_value_range(lookup_key_value_range&& other) : container (std::move(other).container), initialized (std::move(other).initialized), range_indices (std::move(other).range_indices), current (std::move(other).current), end (std::move(other).end) { LINQ_MOVE_CTOR(); } ~lookup_key_value_range() { LINQ_DTOR(); } }; struct op_compare_keys { bool operator()( const keys_value_type& l, const keys_value_type& r) { return *l.first < *r.first; } }; private: keys_type _keys; values_type _values; inline range_indices_type findKey(const clean_key_type& key) { if (_values.empty()) return std::make_pair(0, 0); auto it = std::lower_bound( _keys.begin(), _keys.end(), typename keys_type::value_type (const_cast(key), 0), op_compare_keys()); if ( it == _keys.end() || *it->first != const_cast(key)) return std::make_pair(0, 0); auto next = it + 1; range_indices_type ret; ret.first = it->second; ret.second = next == _keys.end() ? _values.size() : next->second; return ret; } inline lookup_range_wrapper createRange(const range_indices_type& indices) { return lookup_range_wrapper(_values, indices.first, indices.second); } public: inline lookup_range_wrapper operator[](const clean_key_type& key) { return createRange(findKey(key)); } template inline auto operator >> (TBuilder&& builder) & { return builder.build(std::move(lookup_key_value_range(*this))); } template inline auto operator >> (TBuilder&& builder) && { return builder.build(std::move(lookup_key_value_range(std::move(*this)))); } private: inline lookup(keys_type&& k, values_type&& v) : _keys (k), _values (v) { LINQ_CTOR(); } public: inline lookup() { LINQ_CTOR(); } inline lookup(const lookup& other) : _keys (other._keys), _values (other._values) { LINQ_COPY_CTOR(); } inline lookup(lookup&& other) : _keys (std::move(other)._keys), _values (std::move(other)._values) { LINQ_MOVE_CTOR(); } inline ~lookup() { LINQ_DTOR(); } public: template static inline auto build(TRange& range, TKeyPredicate& kp, TValuePredicate& vp) { keys_type k; values_type v; size_t index = 0; while (range.next()) { k.emplace_back(kp(range.front()), index); v.emplace_back(vp(range.front())); ++index; } if (v.empty()) return lookup(); std::sort(k.begin(), k.end(), op_compare_keys()); keys_type keys; values_type values; keys.reserve (k.size()); values.reserve(k.size()); auto it = k.begin(); auto end = k.end(); index = 0; if (it != end) { keys.emplace_back(it->first, index); values.push_back(v.at(it->second)); } auto prev = it; ++it; ++index; while (it != end) { values.push_back(v.at(it->second)); if (*prev->first < *it->first) keys.push_back(std::make_pair(it->first, index)); prev = it; ++it; ++index; } return lookup(std::move(keys), std::move(values)); } }; /* ranges ********************************************************************************/ template struct iterator_range : public tag_range { using iterator_type = TIterator; using this_type = iterator_range; using value_type = decltype(*std::declval()); bool initialized; iterator_type current; iterator_type end; inline value_type& front() { assert(initialized); assert(current != end); return *current; } inline bool next() { if (!initialized) initialized = true; else if (current != end) ++current; return (current != end); } inline iterator_range(iterator_type beg, iterator_type end) : initialized (false), current (beg), end (end) { LINQ_CTOR(); } inline iterator_range(const this_type& other) : initialized (other.initialized), current (other.current), end (other.end) { LINQ_COPY_CTOR(); } inline iterator_range(this_type&& other) : initialized (std::move(other).initialized), current (std::move(other).current), end (std::move(other).end) { LINQ_MOVE_CTOR(); } inline ~iterator_range() { LINQ_DTOR(); } }; template using iterator_range_wrapper = range_wrapper>; template struct container_range : public tag_range { using container_type = TContainer; using this_type = container_range; using iterator_type = decltype(std::begin(std::declval())); using value_type = decltype(*std::declval()); TContainer container; bool initialized; iterator_type current; inline value_type& front() { assert(initialized); assert(current != std::end(container)); return *current; } inline bool next() { if (!initialized) { initialized = true; current = std::begin(container); } else if (current != std::end(container)) { ++current; } return (current != std::end(container)); } inline container_range(container_type& c) noexcept : container (c), initialized (false) { LINQ_CTOR(); } inline container_range(const this_type& other) noexcept : container (other.container), initialized (other.initialized), current (other.current) { LINQ_COPY_CTOR(); } inline container_range(this_type&& other) noexcept : container (std::move(other).container), initialized (std::move(other).initialized), current (std::move(other).current) { LINQ_MOVE_CTOR(); } inline ~container_range() { LINQ_DTOR(); } }; template using container_range_wrapper = range_wrapper>; template struct generator_range : public tag_range { using predicate_type = TPredicate; using this_type = generator_range; using predicate_value_type = decltype(std::declval()()); using value_type = decltype(*std::declval()); predicate_type predicate; predicate_value_type value; inline value_type& front() { assert(static_cast(value)); return *value; } inline bool next() { value = predicate(); return static_cast(value); } inline generator_range(predicate_type p) : predicate (p) { LINQ_CTOR(); } inline generator_range(const this_type& other) : predicate (other.predicate), value (other.value) { LINQ_COPY_CTOR(); } inline generator_range(this_type&& other) : predicate (std::move(other).predicate), value (std::move(other).value) { LINQ_MOVE_CTOR(); } inline ~generator_range() { LINQ_DTOR(); } }; template using generator_range_wrapper = range_wrapper>; template struct where_range : public tag_range { using range_type = TRange; using predicate_type = TPredicate; using this_type = where_range; using value_type = mp_range_value_type; range_type range; predicate_type predicate; inline value_type& front() { return range.front(); } inline bool next() { while (range.next()) { if (predicate(range.front())) return true; } return false; } template inline where_range(R&& r, P&& p) : range (std::forward(r)), predicate (std::forward

(p)) { LINQ_CTOR(); } inline where_range(const this_type& other) : range (other.range), predicate (other.predicate) { LINQ_COPY_CTOR(); } inline where_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate) { LINQ_MOVE_CTOR(); } inline ~where_range() { LINQ_DTOR(); } }; template using where_range_wrapper = range_wrapper>; template struct select_range : public tag_range { using range_type = TRange; using predicate_type = TPredicate; using this_type = select_range; using range_value_type = mp_range_value_type; using value_type = decltype(std::declval()(std::declval())); using cache_type = utl::Nullable; predicate_type predicate; range_type range; cache_type cache; inline value_type& front() { assert(static_cast(cache)); return *cache; } inline bool next() { if (range.next()) { cache = predicate(range.front()); return true; } cache.reset(); return false; } template inline select_range(R&& r, P&& p) : range (std::forward(r)), predicate (std::forward

(p)) { LINQ_CTOR(); } inline select_range(const this_type& other) : range (other.range), predicate (other.predicate) { LINQ_COPY_CTOR(); } inline select_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate) { LINQ_MOVE_CTOR(); } inline ~select_range() { LINQ_DTOR(); } }; template using select_range_wrapper = range_wrapper>; template struct select_many_range : public tag_range { template struct __impl_make_inner_range { using iterator_type = decltype(std::begin(std::declval())); using type = iterator_range; }; template using mp_make_inner_range = typename __impl_make_inner_range::type; using range_type = TRange; using predicate_type = TPredicate; using this_type = where_range; using range_value_type = mp_range_value_type; using predicate_return_type = decltype(std::declval()(std::declval())); using inner_range_type = utl::mp_eval_if< std::is_base_of, predicate_return_type, mp_make_inner_range, predicate_return_type>; using value_type = mp_range_value_type; using inner_range_cache_type = utl::Nullable; predicate_type predicate; range_type range; inner_range_cache_type inner_range; template inline typename std::enable_if::value>::type build_inner_range(T value) { inner_range = value; } template inline typename std::enable_if::value>::type build_inner_range(T value) { inner_range = inner_range_type(std::begin(value), std::end(value)); } inline value_type& front() { assert(inner_range); return inner_range->front(); } inline bool next() { if (inner_range && inner_range->next()) return true; while (range.next()) { inner_range.reset(); build_inner_range(predicate(range.front())); if (inner_range && inner_range->next()) return true; } inner_range.reset(); return false; } template inline select_many_range(R&& r, P&& p) : range (std::forward(r)), predicate (std::forward

(p)) { LINQ_CTOR(); } inline select_many_range(const this_type& other) : range (other.range), predicate (other.predicate), inner_range (other.inner_range) { LINQ_COPY_CTOR(); } inline select_many_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate), inner_range (std::move(other).inner_range) { LINQ_MOVE_CTOR(); } inline ~select_many_range() { LINQ_DTOR(); } }; template using select_many_range_wrapper = range_wrapper>; template struct order_by_range : public tag_range { using range_type = TRange; using select_predicate_type = TSelectPredicate; using less_predicate_type = TLessPredicate; using this_type = order_by_range; using value_type = mp_range_value_type; using vector_value_type = utl::mp_if< std::is_reference, utl::mp_remove_ref*, value_type>; using vector_type = std::vector; range_type range; select_predicate_type select_predicate; less_predicate_type less_predicate; ssize_t current; vector_type values; template inline utl::mp_enable_if_c::value, X> front_impl() { assert(current >= 0); assert(current < static_cast(values.size())); return *values.at(current); } template inline utl::mp_enable_if_c::value, X> front_impl() { assert(current >= 0); assert(current < static_cast(values.size())); return values.at(current); } template inline utl::mp_enable_if_c::value> storeValue(X x) { values.emplace_back(&x); } template inline utl::mp_enable_if_c::value> storeValue(X x) { values.emplace_back(x); } template inline utl::mp_enable_if_c::value> sortValues() { std::sort( values.begin(), values.end(), [this](vector_value_type& l, vector_value_type& r) { return this->less_predicate( this->select_predicate(*l), this->select_predicate(*r)); }); } template inline utl::mp_enable_if_c::value> sortValues() { std::sort( values.begin(), values.end(), [this](vector_value_type& l, vector_value_type& r) { return this->less_predicate( this->select_predicate(l), this->select_predicate(r)); }); } inline value_type& front() { return front_impl(); } inline bool next() { if (current < 0) { values.clear(); while (range.next()) storeValue(range.front()); if (values.empty()) return false; sortValues(); current = 0; return true; } if (current < static_cast(values.size())) ++current; return (current < static_cast(values.size())); } template inline order_by_range(R&& r, SP&& sp, LP&& lp) : range (std::forward(r)), select_predicate(std::forward(sp)), less_predicate (std::forward(lp)), current (-1) { LINQ_CTOR(); } inline order_by_range(const this_type& other) : range (other.range), select_predicate(other.select_predicate), less_predicate (other.less_predicate), current (other.current) { LINQ_COPY_CTOR(); } inline order_by_range(this_type&& other) : range (std::move(other).range), select_predicate(std::move(other).select_predicate), less_predicate (std::move(other).less_predicate), current (std::move(other).current) { LINQ_MOVE_CTOR(); } inline ~order_by_range() { LINQ_DTOR(); } }; template using order_by_range_wrapper = range_wrapper>; template struct distinct_range : public tag_range { using range_type = TRange; using less_predicate_type = TLessPredicate; using this_type = distinct_range; using value_type = mp_range_value_type; using set_value_type = wrapper; using set_less_type = op_wrapper_less; using set_type = std::set; range_type range; set_type set; inline value_type& front() { return range.front(); } inline bool next() { while(range.next()) { if (set.emplace(range.front()).second) return true; } return false; } template inline distinct_range(R&& r, LP&& lp) : range (std::forward(r)), set (std::forward(lp)) { LINQ_CTOR(); } inline distinct_range(const this_type& other) : range (other.range), set (other.set) { LINQ_COPY_CTOR(); } inline distinct_range(this_type&& other) : range (std::move(other).range), set (std::move(other).set) { LINQ_MOVE_CTOR(); } inline ~distinct_range() { LINQ_DTOR(); } }; template using distinct_range_wrapper = range_wrapper>; /* builder *******************************************************************************/ template class TOuterRange> struct builder : public tag_builder { template using outer_range_type = TOuterRange; using this_type = builder; template inline auto build(TRange&& range) { // CAUTION: we want no reference to a range here, because the passed range may be destroyed before used in outer_range_type using range_type = utl::mp_remove_ref; return outer_range_type(std::forward(range)); } inline builder() { LINQ_CTOR(); } inline builder(this_type&& other) { LINQ_MOVE_CTOR(); } inline builder(const this_type&) = delete; inline ~builder() { LINQ_DTOR(); } }; template class TOuterRange> struct predicate_builder : public tag_builder { template using outer_range_type = TOuterRange; using predicate_type = TPredicate; using this_type = predicate_builder; predicate_type predicate; template inline auto build(TRange&& range) { // CAUTION: we want no reference to a range here, because the passed range may be destroyed before used in outer_range_type using range_type = utl::mp_remove_ref; return outer_range_type(std::forward(range), std::move(predicate)); } inline predicate_builder(const predicate_type& p) : predicate(p) { LINQ_CTOR(); } inline predicate_builder(this_type&& other) : predicate(std::move(other).predicate) { LINQ_MOVE_CTOR(); } inline predicate_builder(const this_type&) = delete; inline ~predicate_builder() { LINQ_DTOR(); } }; template class TOuterRange> struct dual_predicate_builder : public tag_builder { template using outer_range_type = TOuterRange; using predicate_0_type = TPredicate0; using predicate_1_type = TPredicate1; using this_type = dual_predicate_builder; predicate_0_type predicate0; predicate_1_type predicate1; template inline auto build(TRange&& range) { // CAUTION: we want no reference to a range here, because the passed range may be destroyed before used in outer_range_type using range_type = utl::mp_remove_ref; return outer_range_type(std::forward(range), std::move(predicate0), std::move(predicate1)); } inline dual_predicate_builder(const predicate_0_type& p0, const predicate_1_type& p1) : predicate0(p0), predicate1(p1) { LINQ_CTOR(); } inline dual_predicate_builder(this_type&& other) : predicate0(std::move(other).predicate0), predicate1(std::move(other).predicate1) { LINQ_MOVE_CTOR(); } inline dual_predicate_builder(const this_type&) = delete; inline ~dual_predicate_builder() { LINQ_DTOR(); } }; struct count_builder : public tag_builder { template inline auto build(TRange&& range) { size_t ret = 0; while (range.next()) ++ret; return ret; } }; struct sum_builder : public tag_builder { template inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; return_type sum = return_type(); while (range.next()) sum += range.front(); return sum; } }; struct min_builder : public tag_builder { template inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; return_type ret = std::numeric_limits::max(); while (range.next()) { if (ret > range.front()) ret = range.front(); } return ret; } }; struct max_builder : public tag_builder { template inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; return_type ret = std::numeric_limits::min(); while (range.next()) { if (ret < range.front()) ret = range.front(); } return ret; } }; struct any_builder : public tag_builder { template inline auto build(TRange&& range) { return range.next(); } }; template struct contains_builder : public tag_builder { using comperator_type = T; using predicate_type = TPredicate; using this_type = contains_builder; comperator_type comperator; predicate_type predicate; template inline auto build(TRange&& range) { while(range.next()) { if (predicate(comperator, range.front())) return true; } return false; } inline contains_builder(comperator_type&& c, predicate_type&& p) : comperator(c), predicate (p) { } }; struct single_builder : public tag_builder { template inline auto build(TRange&& range) { if (!range.next()) throw utl::Exception("range is empty"); auto ret = std::move(range.front()); if (range.next()) throw utl::Exception("range contains more than one value"); return ret; } }; struct single_or_default_builder : public tag_builder { template inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; if (!range.next()) return value_type(); auto ret = std::move(range.front()); if (range.next()) return value_type(); return ret; } }; struct first_builder : public tag_builder { template inline auto build(TRange&& range) { if (!range.next()) throw utl::Exception("range is empty"); return std::move(range.front()); } }; struct first_or_default_builder : public tag_builder { template inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; if (!range.next()) return value_type(); return std::move(range.front()); } }; struct last_builder_base { template struct cache { std::unique_ptr value; inline cache& operator=(T& t) { value.reset(new T(std::move(t))); return *this; } inline T& operator*() { return *value; } inline operator bool() { return static_cast(value); } inline cache() { } }; template struct cache { T* value; inline cache& operator=(T& t) { value = &t; return *this; } inline T& operator*() { return *value; } inline operator bool() { return static_cast(value); } inline cache() : value(nullptr) { } }; }; struct last_builder : public tag_builder, public last_builder_base { template inline auto build(TRange&& range) { using value_type = mp_range_value_type; using cache_type = cache; cache_type cache; while(range.next()) cache = range.front(); if (!static_cast(cache)) throw utl::Exception("range is empty"); return std::move(*cache); } }; struct last_or_default_builder : public tag_builder, public last_builder_base { template inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; using cache_type = cache; cache_type cache; while(range.next()) cache = range.front(); if (!static_cast(cache)) return value_type(); return std::move(*cache); } }; struct to_vector_builder : public tag_builder { size_t capacity; template inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; using vector_type = std::vector; vector_type ret; ret.reserve(capacity); while (range.next()) ret.emplace_back(std::move(range.front())); return ret; } inline to_vector_builder(size_t cap = 16) : capacity(cap) { } }; struct to_list_builder : public tag_builder { template inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; using list_type = std::list; list_type ret; while (range.next()) ret.emplace_back(std::move(range.front())); return ret; } }; template struct to_map_builder : public tag_builder { using key_predicate_type = TKeyPredicate; using value_predicate_type = TValuePredicate; using this_type = to_map_builder; key_predicate_type key_predicate; value_predicate_type value_predicate; template inline auto build(TRange&& range) { using range_type = TRange; using range_value_type = mp_range_value_type; using key_type = decltype(std::declval()(std::declval())); using value_type = decltype(std::declval()(std::declval())); using map_type = std::map, value_type>; map_type map; while (range.next()) { auto ret = map.emplace( key_predicate(range.front()), value_predicate(range.front())); if (!ret.second) throw utl::Exception("duplicate key in range"); } return map; } inline to_map_builder(const key_predicate_type& kp, const value_predicate_type& vp) : key_predicate (kp), value_predicate (vp) { LINQ_CTOR(); } }; template struct for_each_builder : public tag_builder { using predicate_type = TPredicate; using this_type = for_each_builder; predicate_type predicate; template inline auto build(TRange&& range) { while(range.next()) predicate(range.front()); } inline for_each_builder(const predicate_type& p) : predicate(p) { } }; template struct to_lookup_builder : public tag_builder { using key_predicate_type = TKeyPredicate; using value_predicate_type = TValuePredicate; using this_type = to_lookup_builder; key_predicate_type key_predicate; value_predicate_type value_predicate; template inline auto build(TRange&& range) { using range_type = TRange; using range_value_type = mp_range_value_type; using key_type = decltype(std::declval()(std::declval())); using value_type = decltype(std::declval()(std::declval())); using lookup_type = lookup; return lookup_type::build(range, key_predicate, value_predicate); } inline to_lookup_builder(const key_predicate_type& kp, const value_predicate_type& vp) : key_predicate (kp), value_predicate (vp) { LINQ_CTOR(); } }; } /* default operations ********************************************************************/ struct op_select_default { template inline T operator()(T t) { return t; } }; struct op_less_default { template inline bool operator()(L&& l, R&& r) const { return (l < r); } }; struct op_compare_default { template inline bool operator()(L&& l, R&& r) const { return (l == r); } }; struct op_select_key_default { template inline auto operator()(std::pair& p) { return p.first; } template inline auto operator()(std::tuple& t) { return std::get<0>(t); } template inline auto operator()(T& t) { return t; } }; struct op_select_value_default { template inline auto operator()(std::pair& p) { return p.second; } template inline auto operator()(std::tuple& t) { return std::get<1>(t); } template inline auto operator()(T&& t) { return std::forward(t); } }; /* constructors ******************************************************************************/ template inline auto from_iterator(TIterator&& beg, TIterator&& end) { return __impl::iterator_range_wrapper(std::forward(beg), std::forward(end)); } template inline auto from_container(TContainer&& container) { return __impl::container_range_wrapper(std::forward(container)); } template inline auto from_generator(TPredicate&& predicate) { return __impl::generator_range_wrapper(std::forward(predicate)); } template inline auto from_array(TArray&& array) { using array_type = typename std::remove_reference::type; using array_size = typename __impl::mp_array_properties::size; return from_iterator(&array[0], &array[array_size::value]); } /* filter ************************************************************************************/ template inline auto where(TPredicate&& predicate) { return __impl::predicate_builder(std::forward(predicate)); } template inline auto select(TPredicate&& predicate) { return __impl::predicate_builder(std::forward(predicate)); } template inline auto select_many(TPredicate&& predicate) { return __impl::predicate_builder(std::forward(predicate)); } template inline auto order_by(TSelectPredicate&& sp, TLessPredicate&& lp) { return __impl::dual_predicate_builder(std::forward(sp), std::forward(lp)); } template inline auto order_by(TSelectPredicate&& sp) { return order_by(std::forward(sp), op_less_default()); } inline auto order_by() { return order_by(op_select_default(), op_less_default()); } template inline auto distinct(TLessPredicate&& lp) { return __impl::predicate_builder(std::forward(lp)); } inline auto distinct() { return distinct(op_less_default()); } /* result generators */ inline auto count() { return __impl::count_builder(); } inline auto sum() { return __impl::sum_builder(); } inline auto min() { return __impl::min_builder(); } inline auto max() { return __impl::max_builder(); } inline auto any() { return __impl::any_builder(); } template inline auto contains(T&& t, TPredicate&& p) { return __impl::contains_builder(std::forward(t), std::forward(p)); } template inline auto contains(T&& t) { return contains(std::forward(t), op_compare_default()); } inline auto single() { return __impl::single_builder(); } inline auto single_or_default() { return __impl::single_or_default_builder(); } inline auto first() { return __impl::first_builder(); } inline auto first_or_default() { return __impl::first_or_default_builder(); } inline auto last() { return __impl::last_builder(); } inline auto last_or_default() { return __impl::last_or_default_builder(); } inline auto to_vector(size_t capacity = 16) { return __impl::to_vector_builder(capacity); } inline auto to_list() { return __impl::to_list_builder(); } template inline auto to_map(TKeyPredicate&& kp, TValuePredicate&& vp) { return __impl::to_map_builder(std::forward(kp), std::forward(vp)); } template inline auto to_map(TKeyPredicate&& kp) { return to_map(std::forward(kp), std::move(op_select_value_default())); } inline auto to_map() { return to_map(std::move(op_select_key_default()), std::move(op_select_value_default())); } template inline auto for_each(TPredicate&& p) { return __impl::for_each_builder(std::forward(p)); } template inline auto to_lookup(TKeyPredicate&& kp, TValuePredicate&& vp) { return __impl::to_lookup_builder(std::forward(kp), std::forward(vp)); } template inline auto to_lookup(TKeyPredicate&& kp) { return to_lookup(std::forward(kp), std::move(op_select_value_default())); } inline auto to_lookup() { return to_lookup(std::move(op_select_key_default()), std::move(op_select_value_default())); } }