diff --git a/3rdParty/coveo_linq/based_on b/3rdParty/coveo_linq/based_on new file mode 100644 index 0000000..badfc6f --- /dev/null +++ b/3rdParty/coveo_linq/based_on @@ -0,0 +1 @@ +https://github.com/clechasseur/linq/commit/6a12099b7c88afa49852cbe4da4c757c5a8765eb \ No newline at end of file diff --git a/3rdParty/coveo_linq/lib/coveo/enumerable.h b/3rdParty/coveo_linq/lib/coveo/enumerable.h new file mode 100644 index 0000000..3996504 --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/enumerable.h @@ -0,0 +1,14 @@ +/** + * @file + * @brief Forward header for coveo/seq/enumerable.h. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_ENUMERABLE_FWD_H +#define COVEO_ENUMERABLE_FWD_H + +#include + +#endif // COVEO_ENUMERABLE_FWD_H diff --git a/3rdParty/coveo_linq/lib/coveo/linq.h b/3rdParty/coveo_linq/lib/coveo/linq.h new file mode 100644 index 0000000..a2f854f --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/linq.h @@ -0,0 +1,14 @@ +/** + * @file + * @brief Forward header for coveo/linq/linq.h. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_LINQ_FWD_H +#define COVEO_LINQ_FWD_H + +#include + +#endif // COVEO_LINQ_FWD_H diff --git a/3rdParty/coveo_linq/lib/coveo/linq/detail/linq_detail.h b/3rdParty/coveo_linq/lib/coveo/linq/detail/linq_detail.h new file mode 100644 index 0000000..563b9ce --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/linq/detail/linq_detail.h @@ -0,0 +1,3970 @@ +/** + * @file + * @brief Implementation details of LINQ operators. + * + * This file contains implementation details for built-in LINQ operators. + * It should not be necessary to use this file directly when using the library. + * Code in the coveo::linq::detail namespace is subject to change + * in-between versions. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_LINQ_DETAIL_H +#define COVEO_LINQ_DETAIL_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace coveo { +namespace linq { +namespace detail { + +/** + * @internal + * @brief Proxy comparator. + * @headerfile linq_detail.h + * + * Comparator that keeps a pointer to another comparator and uses it + * to implement operator()(). Allows instances of lambdas to be + * used as predicates for sets, for instance. + * + * @tparam Pred Predicate to proxy. + */ +template +class proxy_cmp +{ +private: + const typename std::decay::type* ppred_; + +public: + explicit proxy_cmp(const Pred& pred) + : ppred_(std::addressof(pred)) { } + + template + auto operator()(T&& left, U&& right) const -> decltype((*ppred_)(std::forward(left), std::forward(right))) { + return (*ppred_)(std::forward(left), std::forward(right)); + } +}; + +/** + * @internal + * @brief Dereferencing comparator. + * @headerfile linq_detail.h + * + * Comparator that receives pointers to elements to compare, + * dereferences them and calls another predicate. + * + * @tparam Pred Predicate to call with dereferenced pointers. + */ +template +class deref_cmp +{ +private: + Pred pred_; + +public: + explicit deref_cmp(Pred&& pred) + : pred_(std::forward(pred)) { } + + template + auto operator()(T* const pleft, U* const pright) const -> decltype(pred_(*pleft, *pright)) { + return pred_(*pleft, *pright); + } +}; + +/** + * @internal + * @brief Indexless proxy selector. + * @headerfile linq_detail.h + * + * Selector that can be used with operators that pass an element + * and its index, when the index is not needed. Will simply drop + * the index and call another selector with the element. + * + * @tparam Selector Selector to call with element only. + */ +template +class indexless_selector_proxy +{ +private: + Selector sel_; // Selector that doesn't care about index + +public: + explicit indexless_selector_proxy(Selector&& sel) + : sel_(std::forward(sel)) { } + + template + auto operator()(T&& element, std::size_t) -> decltype(sel_(std::forward(element))) { + return sel_(std::forward(element)); + } +}; + +/** + * @internal + * @brief Dereferencing next delegate implementation. + * @headerfile linq_detail.h + * + * Implementation of a coveo::enumerable::next_delegate + * that takes a sequence of pointers and returns references by + * dereferencing said pointers. Meant to be used to construct + * coveo::enumerables. + * + * @tparam Seq Sequence containing pointers to wrap. + * @see coveo::linq::detail::make_deref_next_impl() + */ +template +class deref_next_impl +{ +public: + using iterator_type = typename seq_traits::iterator_type; + +private: + // Struct storing sequence and ending. Shared among delegates. + struct deref_info { + Seq seq_; // Sequence of pointers. + iterator_type iend_; // Iterator pointing at end of seq_. + + explicit deref_info(Seq&& seq) + : seq_(std::forward(seq)), + iend_(std::end(seq_)) { } + + // Cannot copy/move, stored in a shared_ptr + deref_info(const deref_info&) = delete; + deref_info& operator=(const deref_info&) = delete; + }; + using deref_info_sp = std::shared_ptr; + + deref_info_sp spinfo_; // Shared sequence info. + iterator_type icur_; // Iterator pointing at current element. + +public: + explicit deref_next_impl(Seq&& seq) + : spinfo_(std::make_shared(std::forward(seq))), + icur_(std::begin(spinfo_->seq_)) { } + + auto operator()() -> typename seq_traits::value_type { + typename seq_traits::value_type pobj = nullptr; + if (icur_ != spinfo_->iend_) { + pobj = *icur_; + ++icur_; + } + return pobj; + } +}; + +/// @cond + +/** + * @internal + * @brief Helper to create dereferencing next delegates. + * @headerfile linq_detail.h + * + * Helper function to create instances of coveo::linq::detail::deref_next_impl. + * Allows easier creation by deducing the sequence type. + * + * @tparam Seq Sequence containing pointers to wrap. + * @see coveo::linq::detail::deref_next_impl + */ +template +auto make_deref_next_impl(Seq&& seq) -> deref_next_impl { + return deref_next_impl(std::forward(seq)); +} + +/// @endcond + +/** + * @internal + * @brief Identity unary predicate. + * @headerfile linq_detail.h + * + * Transparent unary predictate that returns values unmodified. + */ +template +struct identity { + template + auto operator()(T&& obj) const -> decltype(std::forward(obj)) { + return std::forward(obj); + } +}; + +/** + * @internal + * @brief Pair-creating binary predicate. + * @headerfile linq_detail.h + * + * Transparent binary predictate that returns pairs of its two arguments. + */ +template +struct pair_of { + template + auto operator()(T&& obj1, U&& obj2) const -> std::pair { + return std::pair(std::forward(obj1), std::forward(obj2)); + } +}; + +/** + * @internal + * @brief Less-than predicate. + * @headerfile linq_detail.h + * + * Transparent less-than binary predicate, like + * std::less from C++14. + */ +template +struct less { + template + auto operator()(T&& left, U&& right) const -> decltype(std::forward(left) < std::forward(right)) { + return std::forward(left) < std::forward(right); + } +}; + +/** + * @internal + * @brief Greater-than predicate. + * @headerfile linq_detail.h + * + * Transparent greater-than binary predicate, like + * std::greater from C++14. + */ +template +struct greater { + template + auto operator()(T&& left, U&& right) const -> decltype(std::forward(left) > std::forward(right)) { + return std::forward(left) > std::forward(right); + } +}; + +/// @cond + +/** + * @internal + * @brief Helper to create std::unique_ptrs. + * @headerfile linq_detail.h + * + * Creates an object and stores it in a std::unique_ptr. + * Like std::make_unique() from C++14. + * + * @tparam T Object to store in the @c unique_ptr. + * @tparam Args Arguments to forward to the constructor of @c T. + */ +template +auto make_unique(Args&&... args) -> std::unique_ptr { + return std::unique_ptr(new T(std::forward(args)...)); +} + +/// @endcond + +/** + * @internal + * @brief coveo::linq::aggregate() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::aggregate() LINQ operator. + * Version with a single argument (aggregate function). + * + * @tparam F Function to aggregate the values. + * @see coveo::linq::aggregate() + */ +template +class aggregate_impl_1 +{ +private: + const F& agg_f_; + +public: + explicit aggregate_impl_1(const F& agg_f) + : agg_f_(agg_f) { } + + template + auto operator()(Seq&& seq) -> typename std::decay::type { + auto it = std::begin(seq); + auto end = std::end(seq); + if (it == end) { + throw_linq_empty_sequence(); + } + auto aggregate(*it); + for (++it; it != end; ++it) { + aggregate = agg_f_(aggregate, *it); + } + return aggregate; + } +}; + +/** + * @internal + * @brief coveo::linq::aggregate() implementation (2). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::aggregate() LINQ operator. + * Version with two arguments (initial value and aggregate function). + * + * @tparam Acc Type of aggregate value. + * @tparam F Function to aggregate the values. + * @see coveo::linq::aggregate() + */ +template +class aggregate_impl_2 +{ +private: + const Acc& seed_; + const F& agg_f_; + +public: + aggregate_impl_2(const Acc& seed, const F& agg_f) + : seed_(seed), agg_f_(agg_f) { } + + template + auto operator()(Seq&& seq) -> Acc { + Acc aggregate(seed_); + for (auto&& element : seq) { + aggregate = agg_f_(aggregate, element); + } + return aggregate; + } +}; + +/** + * @internal + * @brief coveo::linq::aggregate() implementation (3). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::aggregate() LINQ operator. + * Version with three arguments (initial value, aggregate function and + * result function). + * + * @tparam Acc Type of aggregate value. + * @tparam F Function to aggregate the values. + * @tparam RF Function called to produce final result from aggregate. + * @see coveo::linq::aggregate() + */ +template +class aggregate_impl_3 : public aggregate_impl_2 +{ +private: + const RF& result_f_; + +public: + aggregate_impl_3(const Acc& seed, const F& agg_f, const RF& result_f) + : aggregate_impl_2(seed, agg_f), result_f_(result_f) { } + + template + auto operator()(Seq&& seq) -> decltype(result_f_(std::declval())) { + return result_f_(aggregate_impl_2::operator()(seq)); + } +}; + +/** + * @internal + * @brief coveo::linq::all() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::all() LINQ operator. + * + * @tparam Pred Predicate used on sequence elements. + * @see coveo::linq::all() + */ +template +class all_impl +{ +private: + const Pred& pred_; + +public: + explicit all_impl(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> bool { + return std::all_of(std::begin(seq), std::end(seq), pred_); + } +}; + +/** + * @internal + * @brief coveo::linq::any() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::any() LINQ operator. + * Version without argument. + * + * @see coveo::linq::any() + */ +template +class any_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> bool { + return std::begin(seq) != std::end(seq); + } +}; + +/** + * @internal + * @brief coveo::linq::any() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::any() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate used on sequence elements. + * @see coveo::linq::any() + */ +template +class any_impl_1 +{ +private: + const Pred& pred_; + +public: + explicit any_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> bool { + return std::any_of(std::begin(seq), std::end(seq), pred_); + } +}; + +/** + * @internal + * @brief coveo::linq::average() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::average() LINQ operator. + * + * @tparam F Function to get numeric value for each element. + * @see coveo::linq::average() + */ +template +class average_impl +{ +private: + const F& num_f_; + +public: + explicit average_impl(const F& num_f) + : num_f_(num_f) { } + + template + auto operator()(Seq&& seq) + -> typename std::decay::type + { + auto it = std::begin(seq); + auto end = std::end(seq); + if (it == end) { + throw_linq_empty_sequence(); + } + auto total = num_f_(*it); + decltype(total) count = 1; + for (++it; it != end; ++it) { + total += num_f_(*it); + ++count; + } + return total / count; + } +}; + +/** + * @internal + * @brief Selector for coveo::linq::cast(). + * @headerfile linq_detail.h + * + * Selector used to implement the coveo::linq::cast() + * LINQ operator through coveo::linq::select(). + * + * @tparam U Type to cast the elements to. + * @see coveo::linq::cast() + */ +template +class cast_selector +{ +public: + template + auto operator()(T&& obj) const -> U { + return static_cast(obj); + } +}; + +/** + * @internal + * @brief coveo::linq::concat() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::concat() LINQ operator. + * + * @tparam Seq2 Second sequence to concatenate (the first one was passed + * via the call to coveo::linq::from()). + * @see coveo::linq::concat() + */ +template +class concat_impl +{ +private: + // Implementation of next delegate that concatenates two sequences + template + class next_impl + { + public: + // Type of element returned by this next delegate. The elements will be const + // if at least one sequence is const. + using enum_type = typename std::conditional::value_type>::value || + std::is_const::value_type>::value, + typename seq_traits::const_value_type, + typename seq_traits::value_type>::type; + using enum_pointer = typename seq_element_traits::pointer; + using enum_reference = typename seq_element_traits::reference; + + private: + // Type of iterators for both sequences + using first_iterator_type = typename seq_traits::iterator_type; + using second_iterator_type = typename seq_traits::iterator_type; + + // Information used to concatenate sequences. Shared among delegates. + class concat_info + { + private: + Seq1 seq1_; // First sequence to concatenate + first_iterator_type iend1_; // End of first sequence + Seq2 seq2_; // Second sequence to concatenate + second_iterator_type iend2_; // End of second sequence + + public: + concat_info(Seq1&& seq1, Seq2&& seq2) + : seq1_(std::forward(seq1)), + iend1_(std::end(seq1_)), + seq2_(std::forward(seq2)), + iend2_(std::end(seq2_)) { } + + // Cannot move/copy, stored in a shared_ptr + concat_info(const concat_info&) = delete; + concat_info& operator=(const concat_info&) = delete; + + first_iterator_type first_begin() { + return std::begin(seq1_); + } + second_iterator_type second_begin() { + return std::begin(seq2_); + } + + // Returns next element from one of the sequences or nullptr when done + auto get_next(first_iterator_type& icur1, second_iterator_type& icur2) -> enum_pointer { + // First return all elements from first sequence, then from second sequence. + enum_pointer pobj = nullptr; + if (icur1 != iend1_) { + enum_reference robj = *icur1; + pobj = std::addressof(robj); + ++icur1; + } else if (icur2 != iend2_) { + enum_reference robj = *icur2; + pobj = std::addressof(robj); + ++icur2; + } + return pobj; + } + }; + using concat_info_sp = std::shared_ptr; + + concat_info_sp spinfo_; // Shared concat info + first_iterator_type icur1_; // Current position in first sequence + second_iterator_type icur2_; // Current position in second sequence + + public: + next_impl(Seq1&& seq1, Seq2&& seq2) + : spinfo_(std::make_shared(std::forward(seq1), std::forward(seq2))), + icur1_(spinfo_->first_begin()), icur2_(spinfo_->second_begin()) { } + + auto operator()() -> decltype(spinfo_->get_next(icur1_, icur2_)) { + return spinfo_->get_next(icur1_, icur2_); + } + }; + +private: + Seq2 seq2_; // Second sequence (possibly a ref) + +public: + explicit concat_impl(Seq2&& seq2) + : seq2_(std::forward(seq2)) { } + + // Movable but not copyable + concat_impl(const concat_impl&) = delete; + concat_impl(concat_impl&&) = default; + concat_impl& operator=(const concat_impl&) = delete; + concat_impl& operator=(concat_impl&&) = default; + + template + auto operator()(Seq1&& seq1) + -> enumerable::enum_type> + { + auto siz1 = try_get_size_delegate(seq1); + auto siz2 = try_get_size_delegate(seq2_); + typename enumerable::enum_type>::size_delegate siz; + if (siz1 != nullptr && siz2 != nullptr) { + std::size_t size = siz1() + siz2(); + siz = [size]() -> std::size_t { return size; }; + } + return { next_impl(std::forward(seq1), std::forward(seq2_)), + siz }; + } +}; + +/** + * @internal + * @brief coveo::linq::contains() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::contains() LINQ operator. + * Version with one argument (object to look for). + * + * @tparam T Type of object to look for. + * @see coveo::linq::contains() + */ +template +class contains_impl_1 +{ +private: + const T& obj_; // Object to look for. + +public: + explicit contains_impl_1(const T& obj) + : obj_(obj) { } + + template + auto operator()(Seq&& seq) -> bool { + bool found = false; + for (auto&& element : seq) { + if (element == obj_) { + found = true; + break; + } + } + return found; + } +}; + +/** + * @internal + * @brief coveo::linq::contains() implementation (2). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::contains() LINQ operator. + * Version with two arguments (object to look for and predicate). + * + * @tparam T Type of object to look for. + * @tparam Pred Predicate used to compare elements. + * @see coveo::linq::contains() + */ +template +class contains_impl_2 +{ +private: + const T& obj_; // Object to look for. + const Pred& pred_; // Predicate used to compare objects. + +public: + contains_impl_2(const T& obj, const Pred& pred) + : obj_(obj), pred_(pred) { } + + template + auto operator()(Seq&& seq) -> bool { + bool found = false; + for (auto&& element : seq) { + if (pred_(element, obj_)) { + found = true; + break; + } + } + return found; + } +}; + +/** + * @internal + * @brief coveo::linq::count() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::count() LINQ operator. + * Version without argument. + * + * @see coveo::linq::count() + */ +template +class count_impl_0 +{ +private: + // Used if sequence has size() method + template::type>::value, void>::type> + auto impl(Seq&& seq) -> std::size_t { + return seq.size(); + } + + // Used otherwise (no choice but to use distance) + template::type>::value, void*>::type> + auto impl(Seq&& seq, _V = nullptr) -> std::size_t { + return static_cast(std::distance(std::begin(seq), std::end(seq))); + } + +public: + template + auto operator()(Seq&& seq) -> std::size_t { + return impl(std::forward(seq)); + } +}; + +/** + * @internal + * @brief coveo::linq::count() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::count() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate used on elements. + * @see coveo::linq::count() + */ +template +class count_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit count_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> std::size_t { + return static_cast(std::count_if(std::begin(seq), std::end(seq), pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::default_if_empty() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::default_if_empty() LINQ operator. + * Version without argument. + * + * @see coveo::linq::default_if_empty() + */ +template +class default_if_empty_impl_0 +{ +public: + template + auto operator()(Seq&& seq) + -> enumerable::const_value_type> + { + enumerable::const_value_type> e; + if (any_impl_0<>()(std::forward(seq))) { + e = enumerate_container(std::forward(seq)); + } else { + e = enumerate_one(typename seq_traits::raw_value_type()); + } + return e; + } +}; + +/** + * @internal + * @brief coveo::linq::default_if_empty() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::default_if_empty() LINQ operator. + * Version with one argument (default value). + * + * @tparam T Type of default value. + * @see coveo::linq::default_if_empty() + */ +template +class default_if_empty_impl_1 +{ +private: + const T& obj_; // Object to use to create default value if empty. + +public: + explicit default_if_empty_impl_1(const T& obj) + : obj_(obj) { } + + template + auto operator()(Seq&& seq) + -> enumerable::const_value_type> + { + enumerable::const_value_type> e; + if (any_impl_0<>()(std::forward(seq))) { + e = enumerate_container(std::forward(seq)); + } else { + e = enumerate_one(typename seq_traits::raw_value_type(obj_)); + } + return e; + } +}; + +/** + * @internal + * @brief coveo::linq::distinct() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::distinct() LINQ operator. + * + * @tparam Pred Predicate used to compare elements. + * @see coveo::linq::distinct() + */ +template +class distinct_impl +{ +private: + // Implementation of next delegate that filters duplicate elements + template + class next_impl + { + private: + // Type of iterator for the sequence + using iterator_type = typename seq_traits::iterator_type; + + // Set storing pointers to seen elements + using seen_elements_set = std::set::const_pointer, deref_cmp>>; + + // Info used to produce distinct elements. Shared among delegates. + class distinct_info + { + private: + Seq seq_; // Sequence being iterated + iterator_type iend_; // Iterator pointing at end of sequence + Pred pred_; // Predicate ordering the elements + + public: + distinct_info(Seq&& seq, Pred&& pred) + : seq_(std::forward(seq)), + iend_(std::end(seq_)), + pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a shared_ptr + distinct_info(const distinct_info&) = delete; + distinct_info& operator=(const distinct_info&) = delete; + + iterator_type seq_begin() { + return std::begin(seq_); + } + seen_elements_set init_seen_elements() { + return seen_elements_set(deref_cmp>(proxy_cmp(pred_))); + } + + // Returns next distinct element or nullptr when done + auto get_next(iterator_type& icur, seen_elements_set& seen) + -> typename seq_traits::pointer + { + typename seq_traits::pointer pobj = nullptr; + for (; pobj == nullptr && icur != iend_; ++icur) { + typename seq_traits::reference robjtmp = *icur; + auto pobjtmp = std::addressof(robjtmp); + if (seen.emplace(pobjtmp).second) { + // Not seen yet, return this element. + pobj = pobjtmp; + } + } + return pobj; + } + }; + using distinct_info_sp = std::shared_ptr; + + distinct_info_sp spinfo_; // Shared info + iterator_type icur_; // Iterator pointing at current element + seen_elements_set seen_; // Set of seen elements + + public: + next_impl(Seq&& seq, Pred&& pred) + : spinfo_(std::make_shared(std::forward(seq), std::forward(pred))), + icur_(spinfo_->seq_begin()), seen_(spinfo_->init_seen_elements()) { } + + auto operator()() -> decltype(spinfo_->get_next(icur_, seen_)) { + return spinfo_->get_next(icur_, seen_); + } + }; + +private: + Pred pred_; // Predicate used to compare elements + +public: + explicit distinct_impl(Pred&& pred) + : pred_(std::forward(pred)) { } + + // Movable but not copyable + distinct_impl(const distinct_impl&) = delete; + distinct_impl(distinct_impl&&) = default; + distinct_impl& operator=(const distinct_impl&) = delete; + distinct_impl& operator=(distinct_impl&&) = default; + + template + auto operator()(Seq&& seq) + -> enumerable::value_type> + { + return next_impl(std::forward(seq), std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::element_at() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::element_at() LINQ operator. + * + * @see coveo::linq::element_at() + */ +template +class element_at_impl +{ +private: + std::size_t n_; // Index of element to fetch. + +private: + // If we have random-access iterators, we can perform fast computations + template + auto impl(Seq&& seq, std::random_access_iterator_tag) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (static_cast(iend - icur) <= n_) { + throw_linq_out_of_range(); + } + icur += n_; + return *icur; + } + + // Otherwise, we can only move by hand + template + auto impl(Seq&& seq, std::input_iterator_tag) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + auto iend = std::end(seq); + for (std::size_t i = 0; i < n_ && icur != iend; ++i, ++icur) { + } + if (icur == iend) { + throw_linq_out_of_range(); + } + return *icur; + } + +public: + explicit element_at_impl(std::size_t n) + : n_(n) { } + + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::element_at_or_default() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::element_at_or_default() LINQ operator. + * + * @see coveo::linq::element_at_or_default() + */ +template +class element_at_or_default_impl +{ +private: + std::size_t n_; // Index of element to fetch. + +private: + // If we have random-access iterators, we can perform fast computations + template + auto impl(Seq&& seq, std::random_access_iterator_tag) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + return static_cast(iend - icur) > n_ ? *(icur + n_) + : typename seq_traits::raw_value_type(); + } + + // Otherwise, we can only move by hand + template + auto impl(Seq&& seq, std::input_iterator_tag) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + for (std::size_t i = 0; i < n_ && icur != iend; ++i, ++icur) { + } + return icur != iend ? *icur + : typename seq_traits::raw_value_type(); + } + +public: + element_at_or_default_impl(std::size_t n) + : n_(n) { } + + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::except() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::except() LINQ operator. + * + * @tparam Seq2 Sequence to except (e.g. substract) from the first. + * @tparam Pred Predicate used to compare elements. + * @see coveo::linq::except() + */ +template +class except_impl +{ +private: + // Implementation of next delegate that filters out elements in the second sequence + template + class next_impl + { + private: + // Vector of pointers to elements in the second sequence. Used to filter them out. + using elements_to_filter_v = std::vector::const_pointer>; + + // Type of iterator for the first sequence. + using first_iterator_type = typename seq_traits::iterator_type; + + // Bean storing info about elements to filter out. Shared among delegate instances. + class filter_info + { + private: + Seq1 seq1_; // Sequence of elements to scan and return. + first_iterator_type iend1_; // End of seq1_. + Seq2 seq2_; // Sequence of elements to filter out. + deref_cmp pred_; // Predicate used to sort and search through v_to_filter_. + elements_to_filter_v v_to_filter_; // Elements to filter out. Late-initialized. + bool init_called_ = false; // Init flag for v_to_filter_. + + void init() { + try_reserve(v_to_filter_, seq2_); + auto icur2 = std::begin(seq2_); + auto iend2 = std::end(seq2_); + for (; icur2 != iend2; ++icur2) { + typename seq_traits::const_reference robjtmp = *icur2; + v_to_filter_.emplace_back(std::addressof(robjtmp)); + } + std::sort(v_to_filter_.begin(), v_to_filter_.end(), pred_); + init_called_ = true; + } + + public: + filter_info(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : seq1_(std::forward(seq1)), iend1_(std::end(seq1_)), + seq2_(std::forward(seq2)), pred_(std::forward(pred)) { } + + // No move or copy, it's stored in a shared_ptr + filter_info(const filter_info&) = delete; + filter_info& operator=(const filter_info&) = delete; + + first_iterator_type first_begin() { + return std::begin(seq1_); + } + + bool filtered(const typename seq_traits::pointer pobj) { + if (!init_called_) { + // Init elements to filter on first call + init(); + } + return std::binary_search(v_to_filter_.cbegin(), v_to_filter_.cend(), pobj, pred_); + } + + // Returns next non-filtered element or nullptr when done + auto get_next(first_iterator_type& icur1) -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + for (; pobj == nullptr && icur1 != iend1_; ++icur1) { + typename seq_traits::reference robjtmp = *icur1; + auto pobjtmp = std::addressof(robjtmp); + if (!filtered(pobjtmp)) { + pobj = pobjtmp; + } + } + return pobj; + } + }; + using filter_info_sp = std::shared_ptr; + + filter_info_sp spfilter_; // Bean containing filter info + first_iterator_type icur_; // Current position in first sequence + + public: + next_impl(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : spfilter_(std::make_shared(std::forward(seq1), + std::forward(seq2), + std::forward(pred))), + icur_(spfilter_->first_begin()) { } + + auto operator()() -> decltype(spfilter_->get_next(icur_)) { + return spfilter_->get_next(icur_); + } + }; + +private: + Seq2 seq2_; // Sequence of elements to filter out + Pred pred_; // Predicate used to compare elements + +public: + except_impl(Seq2&& seq2, Pred&& pred) + : seq2_(std::forward(seq2)), pred_(std::forward(pred)) { } + + // Movable but not copyable + except_impl(const except_impl&) = delete; + except_impl(except_impl&&) = default; + except_impl& operator=(const except_impl&) = delete; + except_impl& operator=(except_impl&&) = default; + + template + auto operator()(Seq1&& seq1) + -> enumerable::value_type> + { + return next_impl(std::forward(seq1), + std::forward(seq2_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::first() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::first() LINQ operator. + * Version without argument. + * + * @see coveo::linq::first() + */ +template +class first_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + if (icur == std::end(seq)) { + throw_linq_empty_sequence(); + } + return *icur; + } +}; + +/** + * @internal + * @brief coveo::linq::first() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::first() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate used to locate element. + * @see coveo::linq::contains() + */ +template +class first_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit first_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur == iend) { + throw_linq_empty_sequence(); + } + auto ifound = std::find_if(icur, iend, pred_); + if (ifound == iend) { + throw_linq_out_of_range(); + } + return *ifound; + } +}; + +/** + * @internal + * @brief coveo::linq::first_or_default() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::first_or_default() LINQ operator. + * Version without argument. + * + * @see coveo::linq::first_or_default() + */ +template +class first_or_default_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + return icur != std::end(seq) ? *icur + : typename seq_traits::raw_value_type(); + } +}; + +/** + * @internal + * @brief coveo::linq::first_or_default() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::first_or_default() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate used to locate element. + * @see coveo::linq::first_or_default() + */ +template +class first_or_default_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit first_or_default_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + auto iend = std::end(seq); + auto ifound = std::find_if(std::begin(seq), iend, pred_); + return ifound != iend ? *ifound + : typename seq_traits::raw_value_type(); + } +}; + +/** + * @internal + * @brief coveo::linq::group_by() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::group_by(), + * coveo::linq::group_values_by(), coveo::linq::group_by_and_fold() + * and coveo::linq::group_values_by_and_fold LINQ operators. + * + * @tparam KeySelector Selector used to obtain keys for each element. + * @tparam ValueSelector Selector used to obtain values for each element. + * @tparam ResultSelector Selector used to produce the final results from + * elements' keys and corresponding values. + * @tparam Pred Predicate used to compare keys. + * @see coveo::linq::group_by() + * @see coveo::linq::group_values_by() + * @see coveo::linq::group_by_and_fold() + * @see coveo::linq::group_values_by_and_fold() + */ +template +class group_by_impl +{ +private: + // Implementation of next delegate that returns group information + template + class next_impl + { + public: + // Key and value types returned by selectors. + using key = decltype(std::declval()(std::declval::reference>())); + using value = decltype(std::declval()(std::declval::reference>())); + + // Vector of values sharing a common key. + using value_v = std::vector::type>; + using values = decltype(enumerate_container(std::declval())); + + // Map that stores keys and their corresponding values. + using values_by_key_m = std::map::type, value_v, proxy_cmp>; + + // Result returned by result selector. + using result = decltype(std::declval()(std::declval(), std::declval())); + + // Vector of results returned by this next delegate. + using result_v = std::vector::type>; + + private: + // Bean storing group information. Shared among delegates in a shared_ptr. + class groups_info + { + private: + Seq seq_; // Sequence containing the elements + KeySelector key_sel_; // Returns keys for elements + ValueSelector value_sel_; // Returns values for elements + ResultSelector result_sel_; // Converts groups into end results + Pred pred_; // Compares keys + result_v results_; // Vector of end results + bool init_called_ = false; // Whether results_ has been initialized + + void init() { + // First build map of groups + values_by_key_m groups{proxy_cmp(pred_)}; + for (auto&& obj : seq_) { + groups[key_sel_(obj)].emplace_back(value_sel_(obj)); + } + + // Now build vector of results + // Note that since we no longer need the map afterwards, we can actually move + // the vectors stored as map values into the results vector. + results_.reserve(groups.size()); + for (auto&& group_pair : groups) { + results_.emplace_back(result_sel_(group_pair.first, + enumerate_container(std::move(group_pair.second)))); + } + + init_called_ = true; + } + + public: + groups_info(Seq&& seq, KeySelector&& key_sel, ValueSelector&& value_sel, + ResultSelector&& result_sel, Pred&& pred) + : seq_(std::forward(seq)), + key_sel_(std::forward(key_sel)), + value_sel_(std::forward(value_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Not copyable/movable, stored in a shared_ptr + groups_info(const groups_info&) = delete; + groups_info& operator=(const groups_info&) = delete; + + const result_v& get_results() { + if (!init_called_) { + init(); + } + return results_; + } + }; + using groups_info_sp = std::shared_ptr; + + groups_info_sp spgroups_; // Information about groups + typename result_v::const_iterator icurr_{}; // Iterator pointing at current result. + typename result_v::const_iterator iendr_{}; // Iterator pointing at end of result vector. + bool init_called_ = false; // Whether icurr_ and iendr_ have been initialized + + void init() { + const auto& results = spgroups_->get_results(); + icurr_ = std::begin(results); + iendr_ = std::end(results); + init_called_ = true; + } + + public: + next_impl(Seq&& seq, KeySelector&& key_sel, ValueSelector&& value_sel, + ResultSelector&& result_sel, Pred&& pred) + : spgroups_(std::make_shared(std::forward(seq), + std::forward(key_sel), + std::forward(value_sel), + std::forward(result_sel), + std::forward(pred))) { } + + auto operator()() -> typename seq_traits::const_pointer { + // Init iterators on first call + if (!init_called_) { + init(); + } + typename seq_traits::const_pointer pobj = nullptr; + if (icurr_ != iendr_) { + typename seq_traits::const_reference robj = *icurr_; + pobj = std::addressof(robj); + ++icurr_; + } + return pobj; + } + }; + +private: + KeySelector key_sel_; // Selector that provides keys for each element + ValueSelector value_sel_; // Selector that provides values for each element + ResultSelector result_sel_; // Selector that converts each group into a final result + Pred pred_; // Predicate used to compare keys + +public: + group_by_impl(KeySelector&& key_sel, ValueSelector&& value_sel, + ResultSelector&& result_sel, Pred&& pred) + : key_sel_(std::forward(key_sel)), + value_sel_(std::forward(value_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Movable but not copyable + group_by_impl(const group_by_impl&) = delete; + group_by_impl(group_by_impl&&) = default; + group_by_impl& operator=(const group_by_impl&) = delete; + group_by_impl& operator=(group_by_impl&&) = default; + + template + auto operator()(Seq&& seq) + -> enumerable::result_v>::const_value_type> + { + return next_impl(std::forward(seq), + std::forward(key_sel_), + std::forward(value_sel_), + std::forward(result_sel_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::group_join() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::group_join() LINQ operator. + * + * @tparam InnerSeq Inner sequence to join (the outer sequence will have + * been provided via the call to coveo::linq::from()). + * @tparam OuterKeySelector Selector used to fetch keys from elements + * in the outer sequence. + * @tparam InnerKeySelector Selector used to fetch keys from elements + * in the inner sequence. + * @tparam ResultSelector Selector used to produce final results from + * elements in outer sequence and corresponding + * group of elements in inner sequence. + * @tparam Pred Predicate used to compare keys. + * @see coveo::linq::group_join() + */ +template +class group_join_impl +{ +private: + // Implementation of next delegate that returns final results + template + class next_impl + { + public: + // Key returned by key selectors. + using key = decltype(std::declval()(std::declval::reference>())); + + // Group of pointers to elements from the inner sequence that share a common key. + using inner_element_v = std::vector::pointer>; + using inner_elements = enumerable::const_value_type>; + + // Result returned by result selector. + using result = decltype(std::declval()(std::declval::reference>(), + std::declval())); + + // Vector of results returned by this next delegate. + using result_v = std::vector::type>; + + private: + // Bean storing groups information. Shared among delegates in a shared_ptr. + class groups_info + { + private: + OuterSeq outer_seq_; // Outer sequence. + InnerSeq inner_seq_; // Inner sequence from which to create groups. + OuterKeySelector outer_key_sel_; // Key selector for outer sequence. + InnerKeySelector inner_key_sel_; // Key selector for inner sequence. + ResultSelector result_sel_; // Selector converting groups into final results. + Pred pred_; // Predicate used to compare keys. + result_v results_; // Vector of final results. + bool init_called_ = false; // Whether results_ has been initialized. + + void init() { + // Build map of groups of elements from inner sequence. + using groups_m = std::map::type, inner_element_v, proxy_cmp>; + groups_m keyed_inner_elems{proxy_cmp(pred_)}; + for (auto&& inner_elem : inner_seq_) { + typename seq_traits::reference robj = inner_elem; + keyed_inner_elems[inner_key_sel_(inner_elem)].emplace_back(std::addressof(robj)); + } + + // Iterate outer sequence and build final results by matching the elements with + // the groups we built earlier. + try_reserve(results_, outer_seq_); + const auto iendki = keyed_inner_elems.end(); + for (auto&& outer_elem : outer_seq_) { + const key outer_key = outer_key_sel_(outer_elem); + const auto icurki = keyed_inner_elems.find(outer_key); + inner_elements inner_elems; + if (icurki != iendki) { + const std::size_t inner_size = icurki->second.size(); + inner_elems = inner_elements(make_deref_next_impl(icurki->second), + [inner_size]() -> std::size_t { return inner_size; }); + } + results_.emplace_back(result_sel_(outer_elem, inner_elems)); + } + + init_called_ = true; + } + + public: + groups_info(OuterSeq&& outer_seq, InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, Pred&& pred) + : outer_seq_(std::forward(outer_seq)), + inner_seq_(std::forward(inner_seq)), + outer_key_sel_(std::forward(outer_key_sel)), + inner_key_sel_(std::forward(inner_key_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Not copyable/movable, stored in a shared_ptr + groups_info(const groups_info&) = delete; + groups_info& operator=(const groups_info&) = delete; + + const result_v& get_results() { + if (!init_called_) { + init(); + } + return results_; + } + }; + using groups_info_sp = std::shared_ptr; + + groups_info_sp spgroups_; // Information about groups and results + typename result_v::const_iterator icurr_{}; // Iterator pointing at current result. + typename result_v::const_iterator iendr_{}; // Iterator pointing at end of result vector. + bool init_called_ = false; // Whether icurr_/iendr_ have been initialized. + + void init() { + const auto& results = spgroups_->get_results(); + icurr_ = std::begin(results); + iendr_ = std::end(results); + init_called_ = true; + } + + public: + next_impl(OuterSeq&& outer_seq, InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, Pred&& pred) + : spgroups_(std::make_shared(std::forward(outer_seq), + std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + std::forward(pred))) { } + + auto operator()() -> typename seq_traits::const_pointer { + // Init iterators on first call + if (!init_called_) { + init(); + } + typename seq_traits::const_pointer pobj = nullptr; + if (icurr_ != iendr_) { + typename seq_traits::const_reference robj = *icurr_; + pobj = std::addressof(robj); + ++icurr_; + } + return pobj; + } + }; + +private: + InnerSeq inner_seq_; // Inner sequence whose elements to group with outer elements with matching keys + OuterKeySelector outer_key_sel_; // Selector to get keys from elements in the outer sequence + InnerKeySelector inner_key_sel_; // Selector to get keys from elements in the inner sequence + ResultSelector result_sel_; // Selector to convert groups into final results + Pred pred_; // Predicate used to compare keys + +public: + group_join_impl(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, + Pred&& pred) + : inner_seq_(std::forward(inner_seq)), + outer_key_sel_(std::forward(outer_key_sel)), + inner_key_sel_(std::forward(inner_key_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Movable but not copyable + group_join_impl(const group_join_impl&) = delete; + group_join_impl(group_join_impl&&) = default; + group_join_impl& operator=(const group_join_impl&) = delete; + group_join_impl& operator=(group_join_impl&&) = default; + + template + auto operator()(OuterSeq&& outer_seq) + -> enumerable::result_v>::const_value_type> + { + return next_impl(std::forward(outer_seq), + std::forward(inner_seq_), + std::forward(outer_key_sel_), + std::forward(inner_key_sel_), + std::forward(result_sel_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::intersect() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::intersect() LINQ operator. + * + * @tparam Seq2 Second sequence to intersect (the first one will have been + * provided via the call to coveo::linq::from()). + * @tparam Pred Predicate used to compare elements. + * @see coveo::linq::intersect() + */ +template +class intersect_impl +{ +public: + // Implementation of next delegate that performs intersection + template + class next_impl + { + private: + // Vector storing pointers to the elements of second sequence. + using seq2_element_v = std::vector::pointer>; + + // Type of iterators for the sequences. + using first_iterator_type = typename seq_traits::iterator_type; + + // Information about intersection. Shared among delegates through smart_ptr. + class intersect_info + { + private: + Seq1 seq1_; // First sequence to intersect. + first_iterator_type iend1_; // Iterator pointing at end of first sequence. + Seq2 seq2_; // Second sequence to intersect. + deref_cmp pred_; // Predicate used to sort and search through v_in_seq2_. + seq2_element_v v_in_seq2_; // Vector of elements from second sequence. + bool init_called_ = false; // Whether v_in_seq2_ has been initialized. + + void init() { + // Add all elements from second sequence to a vector and sort it. + try_reserve(v_in_seq2_, seq2_); + auto icur2 = std::begin(seq2_); + auto iend2 = std::end(seq2_); + for (; icur2 != iend2; ++icur2) { + typename seq_traits::reference robjtmp = *icur2; + v_in_seq2_.emplace_back(std::addressof(robjtmp)); + } + std::sort(v_in_seq2_.begin(), v_in_seq2_.end(), pred_); + init_called_ = true; + } + + public: + intersect_info(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : seq1_(std::forward(seq1)), + iend1_(std::end(seq1_)), + seq2_(std::forward(seq2)), + pred_(std::forward(pred)) { } + + // Not copyable/movable, stored in a shared_ptr + intersect_info(const intersect_info&) = delete; + intersect_info& operator=(const intersect_info&) = delete; + + first_iterator_type first_begin() { + return std::begin(seq1_); + } + + bool is_in_seq2(const typename seq_traits::pointer pobj) { + if (!init_called_) { + init(); + } + return std::binary_search(v_in_seq2_.cbegin(), v_in_seq2_.cend(), pobj, pred_); + } + + // Returns next element that is both in first and second sequence or nullptr when done + auto get_next(first_iterator_type& icur1) -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + for (; pobj == nullptr && icur1 != iend1_; ++icur1) { + typename seq_traits::reference robjtmp = *icur1; + auto pobjtmp = std::addressof(robjtmp); + if (is_in_seq2(pobjtmp)) { + pobj = pobjtmp; + } + } + return pobj; + } + }; + using intersect_info_sp = std::shared_ptr; + + intersect_info_sp spint_info_; // Intersection information. + first_iterator_type icur_; // Current position in first sequence. + + public: + next_impl(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : spint_info_(std::make_shared(std::forward(seq1), + std::forward(seq2), + std::forward(pred))), + icur_(spint_info_->first_begin()) { } + + auto operator()() -> decltype(spint_info_->get_next(icur_)) { + return spint_info_->get_next(icur_); + } + }; + +private: + Seq2 seq2_; // Second sequence to intersect. + Pred pred_; // Predicate used to compare elements. + +public: + intersect_impl(Seq2&& seq2, Pred&& pred) + : seq2_(std::forward(seq2)), + pred_(std::forward(pred)) { } + + // Movable but not copyable + intersect_impl(const intersect_impl&) = delete; + intersect_impl(intersect_impl&&) = default; + intersect_impl& operator=(const intersect_impl&) = delete; + intersect_impl& operator=(intersect_impl&&) = default; + + template + auto operator()(Seq1&& seq1) + -> enumerable::value_type> + { + return next_impl(std::forward(seq1), + std::forward(seq2_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::join() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::join() LINQ operator. + * + * @tparam InnerSeq Inner sequence to join (the outer sequence will have been + * provided by the call to coveo::linq::from()). + * @tparam OuterKeySelector Selector used to fetch keys from elements + * in the outer sequence. + * @tparam InnerKeySelector Selector used to fetch keys from elements + * in the inner sequence. + * @tparam ResultSelector Selector used to produce final results from + * elements from outer and inner sequence with + * matching keys. + * @tparam Pred Predicate used to compare keys. + * @see coveo::linq::join() + */ +template +class join_impl +{ +private: + // Implementation of next delegate that performs join + template + class next_impl + { + public: + // Key returned by key selectors. + using key = decltype(std::declval()(std::declval::reference>())); + + // Group of pointers to elements from the inner sequence that share a common key. + using inner_element_v = std::vector::pointer>; + + // Result returned by result selector. + using result = decltype(std::declval()(std::declval::reference>(), + std::declval::reference>())); + + // Vector of results returned by this next delegate. + using result_v = std::vector::type>; + + private: + // Bean storing join information. Shared among delegates in a shared_ptr. + class join_info + { + private: + OuterSeq outer_seq_; // Outer sequence. + InnerSeq inner_seq_; // Inner sequence to join with outer sequence. + OuterKeySelector outer_key_sel_; // Key selector for outer sequence. + InnerKeySelector inner_key_sel_; // Key selector for inner sequence. + ResultSelector result_sel_; // Selector converting joined elements into final results. + Pred pred_; // Predicate used to compare keys. + result_v results_; // Vector of final results. + bool init_called_ = false; // Whether results_ has been initialized. + + void init() { + // Build map of groups of elements from inner sequence. + using groups_m = std::map::type, inner_element_v, proxy_cmp>; + groups_m keyed_inner_elems{proxy_cmp(pred_)}; + for (auto&& inner_elem : inner_seq_) { + typename seq_traits::reference robj = inner_elem; + keyed_inner_elems[inner_key_sel_(inner_elem)].emplace_back(std::addressof(robj)); + } + + // Iterate outer sequence and build final results by joining the elements with + // those in the groups we built earlier. + try_reserve(results_, inner_seq_); + const auto iendki = keyed_inner_elems.end(); + for (auto&& outer_elem : outer_seq_) { + const key outer_key = outer_key_sel_(outer_elem); + const auto icurki = keyed_inner_elems.find(outer_key); + if (icurki != iendki) { + for (auto* pinner_elem : icurki->second) { + results_.emplace_back(result_sel_(outer_elem, *pinner_elem)); + } + } + } + + init_called_ = true; + } + + public: + join_info(OuterSeq&& outer_seq, InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, Pred&& pred) + : outer_seq_(std::forward(outer_seq)), + inner_seq_(std::forward(inner_seq)), + outer_key_sel_(std::forward(outer_key_sel)), + inner_key_sel_(std::forward(inner_key_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Not copyable/movable, stored in a shared_ptr + join_info(const join_info&) = delete; + join_info& operator=(const join_info&) = delete; + + const result_v& get_results() { + if (!init_called_) { + init(); + } + return results_; + } + }; + using join_info_sp = std::shared_ptr; + + join_info_sp spjoin_info_; // Information about joined elements and results + typename result_v::const_iterator icurr_{}; // Iterator pointing at current result. + typename result_v::const_iterator iendr_{}; // Iterator pointing at end of result vector. + bool init_called_ = false; // Whether icurr_/iendr_ have been initialized. + + void init() { + const auto& results = spjoin_info_->get_results(); + icurr_ = std::begin(results); + iendr_ = std::end(results); + init_called_ = true; + } + + public: + next_impl(OuterSeq&& outer_seq, InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, Pred&& pred) + : spjoin_info_(std::make_shared(std::forward(outer_seq), + std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + std::forward(pred))) { } + + auto operator()() -> typename seq_traits::const_pointer { + // Init iterators on first call + if (!init_called_) { + init(); + } + typename seq_traits::const_pointer pobj = nullptr; + if (icurr_ != iendr_) { + typename seq_traits::const_reference robj = *icurr_; + pobj = std::addressof(robj); + ++icurr_; + } + return pobj; + } + }; + +private: + InnerSeq inner_seq_; // Inner sequence to join. + OuterKeySelector outer_key_sel_; // Fetches keys for elements of outer sequence. + InnerKeySelector inner_key_sel_; // Fetches keys for elements of inner sequence. + ResultSelector result_sel_; // Creates results from joined elements. + Pred pred_; // Predicate to compare keys. + +public: + join_impl(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, + Pred&& pred) + : inner_seq_(std::forward(inner_seq)), + outer_key_sel_(std::forward(outer_key_sel)), + inner_key_sel_(std::forward(inner_key_sel)), + result_sel_(std::forward(result_sel)), + pred_(std::forward(pred)) { } + + // Movable but not copyable + join_impl(const join_impl&) = delete; + join_impl(join_impl&&) = default; + join_impl& operator=(const join_impl&) = delete; + join_impl& operator=(join_impl&&) = default; + + template + auto operator()(OuterSeq&& outer_seq) + -> enumerable::result_v>::const_value_type> + { + return next_impl(std::forward(outer_seq), + std::forward(inner_seq_), + std::forward(outer_key_sel_), + std::forward(inner_key_sel_), + std::forward(result_sel_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::last() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::last() LINQ operator. + * Version without argument. + * + * @see coveo::linq::last() + */ +template +class last_impl_0 +{ +private: + // If we have bidi iterators, we can simply use rbegin + template + auto impl(Seq&& seq, std::bidirectional_iterator_tag) -> decltype(*std::begin(seq)) { + auto ricur = seq.rbegin(); + if (ricur == seq.rend()) { + throw_linq_empty_sequence(); + } + return *ricur; + } + + // Otherwise we'll have to be creative + template + auto impl(Seq&& seq, std::input_iterator_tag) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur == iend) { + throw_linq_empty_sequence(); + } + decltype(icur) iprev; + while (icur != iend) { + iprev = icur; + ++icur; + } + return *iprev; + } + +public: + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::last() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::last() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate to satisfy. + * @see coveo::linq::last() + */ +template +class last_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy + +private: + // If we have bidi iterators, we can simply use rbegin + template + auto impl(Seq&& seq, std::bidirectional_iterator_tag) -> decltype(*std::begin(seq)) { + auto ricur = seq.rbegin(); + auto riend = seq.rend(); + if (ricur == riend) { + throw_linq_empty_sequence(); + } + auto rifound = std::find_if(ricur, riend, pred_); + if (rifound == riend) { + throw_linq_out_of_range(); + } + return *rifound; + } + + // Otherwise we'll have to be creative + template + auto impl(Seq&& seq, std::input_iterator_tag) -> decltype(*std::begin(seq)) { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur == iend) { + throw_linq_empty_sequence(); + } + auto ifound = iend; + while (icur != iend) { + if (pred_(*icur)) { + ifound = icur; + } + ++icur; + } + if (ifound == iend) { + throw_linq_out_of_range(); + } + return *ifound; + } + +public: + explicit last_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::last_or_default() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::last_or_default() LINQ operator. + * Version without argument. + * + * @see coveo::linq::last_or_default() + */ +template +class last_or_default_impl_0 +{ +private: + // If we have bidi iterators, we can simply use rbegin + template + auto impl(Seq&& seq, std::bidirectional_iterator_tag) -> typename seq_traits::raw_value_type { + auto ricur = seq.rbegin(); + return ricur != seq.rend() ? *ricur + : typename seq_traits::raw_value_type(); + } + + // Otherwise we'll have to be creative + template + auto impl(Seq&& seq, std::input_iterator_tag) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + auto iprev = iend; + while (icur != iend) { + iprev = icur; + ++icur; + } + return iprev != iend ? *iprev + : typename seq_traits::raw_value_type(); + } + +public: + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::last_or_default() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::last_or_default() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate to satisfy. + * @see coveo::linq::last_or_default() + */ +template +class last_or_default_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy + +private: + // If we have bidi iterators, we can simply use rbegin + template + auto impl(Seq&& seq, std::bidirectional_iterator_tag) -> typename seq_traits::raw_value_type { + auto riend = seq.rend(); + auto rifound = std::find_if(seq.rbegin(), riend, pred_); + return rifound != riend ? *rifound + : typename seq_traits::raw_value_type(); + } + + // Otherwise we'll have to be creative + template + auto impl(Seq&& seq, std::input_iterator_tag) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + auto ifound = iend; + while (icur != iend) { + if (pred_(*icur)) { + ifound = icur; + } + ++icur; + } + return ifound != iend ? *ifound + : typename seq_traits::raw_value_type(); + } + +public: + explicit last_or_default_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::max() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::max() LINQ operator. + * Version without argument. + * + * @see coveo::linq::max() + */ +template +class max_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto iend = std::end(seq); + auto imax = std::max_element(std::begin(seq), iend); + if (imax == iend) { + throw_linq_empty_sequence(); + } + return *imax; + } +}; + +/** + * @internal + * @brief coveo::linq::max() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::max() LINQ operator. + * Version with value selector. + * + * @tparam Selector Selector used to fetch values from sequence elements. + * @see coveo::linq::max() + */ +template +class max_impl_1 +{ +private: + const Selector& sel_; + +public: + explicit max_impl_1(const Selector& sel) + : sel_(sel) { } + + template + auto operator()(Seq&& seq) -> typename std::decay::type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur == iend) { + throw_linq_empty_sequence(); + } + auto max_val = sel_(*icur); + while (++icur != iend) { + max_val = std::max(max_val, sel_(*icur)); + } + return max_val; + } +}; + +/** + * @internal + * @brief coveo::linq::min() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::min() LINQ operator. + * Version without argument. + * + * @see coveo::linq::min() + */ +template +class min_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto iend = std::end(seq); + auto imin = std::min_element(std::begin(seq), iend); + if (imin == iend) { + throw_linq_empty_sequence(); + } + return *imin; + } +}; + +/** + * @internal + * @brief coveo::linq::min() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::min() LINQ operator. + * Version with value selector. + * + * @tparam Selector Selector used to fetch values from sequence elements. + * @see coveo::linq::min() + */ +template +class min_impl_1 +{ +private: + const Selector& sel_; // Selector used to fetch values from sequence elements. + +public: + explicit min_impl_1(const Selector& sel) + : sel_(sel) { } + + template + auto operator()(Seq&& seq) -> typename std::decay::type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur == iend) { + throw_linq_empty_sequence(); + } + auto min_val = sel_(*icur); + while (++icur != iend) { + min_val = std::min(min_val, sel_(*icur)); + } + return min_val; + } +}; + +/** + * @internal + * @brief coveo::linq::none() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::none() LINQ operator. + * + * @see coveo::linq::none() + */ +template +class none_impl +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit none_impl(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> bool { + return std::none_of(std::begin(seq), std::end(seq), pred_); + } +}; + +/** + * @internal + * @brief coveo::linq::order_by() internal comparator. + * @headerfile linq_detail.h + * + * Comparator of sequence elements used by coveo::linq::order_by() et al. + * Fetches keys from sequence elements then compares them with a predicate. + * Its operator()() returns an @c int that represents the relationship + * between the elements (negative values mean left is before right, etc.) + * + * @tparam KeySelector Selector used to fetch keys from sequence elements. + * @tparam Pred Predicate used to compare the keys. + * @tparam Descending Whether we should order ascending or descending. + * @tparam _LessValue Value to use to compute ordering. Set automatically + * from the value of @c Descending. + * @see coveo::linq::order_by() + * @see coveo::linq::order_by_descending() + * @see coveo::linq::then_by() + * @see coveo::linq::then_by_descending() + * @see coveo::linq::detail::dual_order_by_comparator + * @see coveo::linq::detail::order_by_impl + * @see coveo::linq::detail::order_by_impl_with_seq + */ +template +class order_by_comparator +{ +private: + KeySelector key_sel_; // Key selector used to fetch keys from elements. + Pred pred_; // Predicate used to compare keys. + +public: + order_by_comparator(KeySelector&& key_sel, Pred&& pred) + : key_sel_(std::forward(key_sel)), pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a unique_ptr + order_by_comparator(const order_by_comparator&) = delete; + order_by_comparator& operator=(const order_by_comparator&) = delete; + + // Compares two values, returning relative position of the two in an ordered sequence. + template + int operator()(T1&& left, T2&& right) const { + decltype(key_sel_(left)) leftk = key_sel_(left); + decltype(key_sel_(right)) rightk = key_sel_(right); + int cmp; + if (pred_(leftk, rightk)) { + cmp = _LessValue; + } else if (pred_(rightk, leftk)) { + cmp = -_LessValue; + } else { + // Keys are equal. + cmp = 0; + } + return cmp; + } +}; + +/** + * @internal + * @brief coveo::linq::order_by() internal dual comparator. + * @headerfile linq_detail.h + * + * Dual comparator used by coveo::linq::order_by() et al. Chains + * two comparators together: when operator()() is called, the + * first comparator is used; if it returns 0, the second one is used. + * + * @tparam Cmp1 First comparator to chain. + * @tparam Cmp2 Second comparator to chain. + * @see coveo::linq::order_by() + * @see coveo::linq::order_by_descending() + * @see coveo::linq::then_by() + * @see coveo::linq::then_by_descending() + * @see coveo::linq::detail::order_by_comparator + * @see coveo::linq::detail::order_by_impl + * @see coveo::linq::detail::order_by_impl_with_seq + */ +template +class dual_order_by_comparator +{ +private: + std::unique_ptr upcmp1_; + std::unique_ptr upcmp2_; + +public: + dual_order_by_comparator(std::unique_ptr&& upcmp1, std::unique_ptr&& upcmp2) + : upcmp1_(std::move(upcmp1)), upcmp2_(std::move(upcmp2)) { } + + // Cannot copy/move, stored in a unique_ptr + dual_order_by_comparator(const dual_order_by_comparator&) = delete; + dual_order_by_comparator& operator=(const dual_order_by_comparator&) = delete; + + // Compares two values by calling first then second comparator. + template + int operator()(T1&& left, T2&& right) const { + int cmp = (*upcmp1_)(left, right); + if (cmp == 0) { + cmp = (*upcmp2_)(left, right); + } + return cmp; + } +}; + +/// @cond + +// Forward declaration to declare friendship +template class order_by_impl; + +/// @endcond + +/** + * @internal + * @brief coveo::linq::order_by() et al implementation. + * @headerfile linq_detail.h + * + * "Second" implementation of the coveo::linq::order_by(), + * coveo::linq::order_by_descending(), coveo::linq::then_by() + * and coveo::linq::then_by_descending() LINQ operators. + * This is the implementation that has both a comparator and a sequence. + * It is the final form when the operator is fully applied. + * + * Contrarily to all other LINQ operation implementations, this + * implementation cannot return a coveo::enumerable, because + * we need to keep our identity to allow chaining. Thus, we implement + * an @c enumerable "interface" manually by fronting the same methods. + * + * @tparam Seq Sequence to order. + * @tparam Cmp Comparator used to order sequence elements. + * @see coveo::linq::order_by() + * @see coveo::linq::order_by_descending() + * @see coveo::linq::then_by() + * @see coveo::linq::then_by_descending() + * @see coveo::linq::detail::order_by_impl + */ +template +class order_by_impl_with_seq +{ + // We need this friendship so that it can "steal" our internals. + template friend class order_by_impl; + +private: + Seq seq_; // Sequence we're ordering. + std::unique_ptr upcmp_; // Comparator used to order a sequence. + enumerable::value_type> enum_; // Enumerator of ordered elements. + bool init_called_ = false; // Whether enum_ has been initialized. + + // Called to initialize enum_ before using it. + void init() { + std::vector::pointer> ordered; + try_reserve(ordered, seq_); + for (auto&& elem : seq_) { + typename seq_traits::reference robj = elem; + ordered.push_back(std::addressof(robj)); + } + std::stable_sort(ordered.begin(), + ordered.end(), + [this](typename seq_traits::pointer pleft, + typename seq_traits::pointer pright) { + return (*upcmp_)(*pleft, *pright) < 0; + }); + const std::size_t num_ordered = ordered.size(); + enum_ = { make_deref_next_impl(std::move(ordered)), + [num_ordered]() -> std::size_t { return num_ordered; } }; + init_called_ = true; + } + +public: + // Type of iterators used for the ordered sequence. + using iterator = typename enumerable::value_type>::iterator; + using const_iterator = typename enumerable::value_type>::const_iterator; + + // Constructor called by the impl without sequence. + order_by_impl_with_seq(Seq&& seq, std::unique_ptr&& upcmp) + : seq_(std::forward(seq)), upcmp_(std::move(upcmp)) { } + + // Movable but not copyable + order_by_impl_with_seq(const order_by_impl_with_seq&) = delete; + order_by_impl_with_seq(order_by_impl_with_seq&&) = default; + order_by_impl_with_seq& operator=(const order_by_impl_with_seq&) = delete; + order_by_impl_with_seq& operator=(order_by_impl_with_seq&&) = default; + + // Support for ordered sequence. + iterator begin() const { + if (!init_called_) { + const_cast*>(this)->init(); + } + return enum_.begin(); + } + iterator end() const { + if (!init_called_) { + const_cast*>(this)->init(); + } + return enum_.end(); + } + iterator cbegin() const { + return begin(); + } + iterator cend() const { + return end(); + } + + // Support for sequence size (a bit like the enumerable API) + bool has_fast_size() const { + if (!init_called_) { + const_cast*>(this)->init(); + } + return enum_.has_fast_size(); + } + std::size_t size() const { + if (!init_called_) { + const_cast*>(this)->init(); + } + return enum_.size(); + } +}; + +/** + * @internal + * @brief coveo::linq::order_by() et al implementation. + * @headerfile linq_detail.h + * + * "First" implementation of the coveo::linq::order_by(), + * coveo::linq::order_by_descending(), coveo::linq::then_by() + * and coveo::linq::then_by_descending() LINQ operators. + * This is the implementation with a comparator only; it is returned by + * the operator helper methods. It supports two operations: either being + * applied to a sequence, in which case it produces the "second" implementation + * (see coveo::linq::detail::order_by_impl_with_seq), or being + * applied to an @c order_by_impl_with_seq, in which case we chain the + * comparators. + * + * @tparam Cmp Comparator used to order sequence elements. + * @see coveo::linq::order_by() + * @see coveo::linq::order_by_descending() + * @see coveo::linq::then_by() + * @see coveo::linq::then_by_descending() + * @see coveo::linq::detail::order_by_impl_with_seq + * @see coveo::linq::detail::order_by_comparator + * @see coveo::linq::detail::dual_order_by_comparator + */ +template +class order_by_impl +{ +private: + std::unique_ptr upcmp_; // Comparator used to order a sequence. + +public: + explicit order_by_impl(std::unique_ptr&& upcmp) + : upcmp_(std::move(upcmp)) { } + + // Movable by not copyable + order_by_impl(const order_by_impl&) = delete; + order_by_impl(order_by_impl&&) = default; + order_by_impl& operator=(const order_by_impl&) = delete; + order_by_impl& operator=(order_by_impl&&) = default; + + // When applied to a sequence, produces a different object. + template + auto operator()(Seq&& seq) -> order_by_impl_with_seq { + return order_by_impl_with_seq(std::forward(seq), std::move(upcmp_)); + } + + // When applied to an impl with sequence, merges the two and chains the comparators. + template + auto operator()(order_by_impl_with_seq&& impl) + -> order_by_impl_with_seq> + { + using dual_comparator = dual_order_by_comparator; + auto new_upcmp = detail::make_unique(std::move(impl.upcmp_), std::move(upcmp_)); + return order_by_impl_with_seq(std::forward(impl.seq_), std::move(new_upcmp)); + } +}; + +/** + * @internal + * @brief coveo::linq::reverse() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::reverse() LINQ operator. + * + * @see coveo::linq::reverse() + */ +template +class reverse_impl +{ +private: + // next impl when we can use reverse iterators + template + class next_impl_fast + { + private: + // Iterator used to go backward in sequence. + using reverse_iterator = typename std::decay().rbegin())>::type; + + // Bean containing info shared among delegates + struct reverse_info { + Seq seq_; // Sequence we're iterating. + reverse_iterator irend_; // End of reversed seq_. + + explicit reverse_info(Seq&& seq) + : seq_(std::forward(seq)), + irend_(seq_.rend()) { } + + // Cannot copy/move, stored in a shared_ptr. + reverse_info(const reverse_info&) = delete; + reverse_info& operator=(const reverse_info&) = delete; + }; + using reverse_info_sp = std::shared_ptr; + + reverse_info_sp spinfo_; // Shared info with sequence. + reverse_iterator ircur_; // Current point in reverse sequence. + + public: + explicit next_impl_fast(Seq&& seq) + : spinfo_(std::make_shared(std::forward(seq))), + ircur_(spinfo_->seq_.rbegin()) { } + + auto operator()() -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + if (ircur_ != spinfo_->irend_) { + typename seq_traits::reference robj = *ircur_; + pobj = std::addressof(robj); + ++ircur_; + } + return pobj; + } + }; + + // next impl when we don't have reverse iterators natively + template + class next_impl_slow + { + private: + // Vector storing pointers of elements from sequence. + using pelems_v = std::vector::pointer>; + using pelems_v_reverse_iterator = typename pelems_v::reverse_iterator; + + // Bean containing info shared among delegates + struct reverse_info { + Seq seq_; // Sequence containing the elements. + pelems_v vpelems_; // Vector of pointers to the elements. + pelems_v_reverse_iterator virend_; // Iterator pointing at end of vpelems_. + + explicit reverse_info(Seq&& seq) + : seq_(std::forward(seq)) + { + // Build vector of pointers to sequence elements. + try_reserve(vpelems_, seq_); + for (auto&& elem : seq_) { + typename seq_traits::reference robj = elem; + vpelems_.push_back(std::addressof(robj)); + } + + // Init end iterator. + virend_ = vpelems_.rend(); + } + + // Cannot copy/move, stored in a shared_ptr. + reverse_info(const reverse_info&) = delete; + reverse_info& operator=(const reverse_info&) = delete; + }; + using reverse_info_sp = std::shared_ptr; + + reverse_info_sp spinfo_; // Bean storing sequence and vector of pointers. + pelems_v_reverse_iterator vircur_; // Iterator pointing at current pointer to element. + + public: + explicit next_impl_slow(Seq&& seq) + : spinfo_(std::make_shared(std::forward(seq))), + vircur_(spinfo_->vpelems_.rbegin()) { } + + auto operator()() -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + if (vircur_ != spinfo_->virend_) { + pobj = *vircur_; + ++vircur_; + } + return pobj; + } + + // To be able to build a proper size delegate + std::size_t size() const { + return spinfo_->vpelems_.size(); + } + }; + + // If we have bidi iterators, we can simply use rbegin + template + auto impl(Seq&& seq, std::bidirectional_iterator_tag) + -> enumerable::value_type> + { + auto siz = try_get_size_delegate(seq); + return enumerable::value_type>(next_impl_fast(std::forward(seq)), + siz); + } + + // Otherwise we'll have to be creative + template + auto impl(Seq&& seq, std::input_iterator_tag) + -> enumerable::value_type> + { + next_impl_slow next_impl(std::forward(seq)); + const std::size_t size = next_impl.size(); + auto siz = [size]() -> std::size_t { return size; }; + return enumerable::value_type>(std::move(next_impl), + siz); + } + +public: + template + auto operator()(Seq&& seq) + -> enumerable::value_type> + { + return impl(std::forward(seq), + typename std::iterator_traits::iterator_type>::iterator_category()); + } +}; + +/** + * @internal + * @brief coveo::linq::select() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::select() and + * coveo::linq::select_with_index() LINQ operators. + * + * @tparam Selector Selector to transform sequence elements. + * @see coveo::linq::select() + * @see coveo::linq::select_with_index() + */ +template +class select_impl +{ +public: + // Next delegate implementation for select operator + template + class next_impl + { + private: + // Iterator used by the sequence. + using iterator_type = typename seq_traits::iterator_type; + + // Containers storing transformed elements. + using transformed_v = std::vector; + using transformed_l = std::forward_list; + using transformed_l_iterator = typename transformed_l::iterator; + + // Bean storing info about elements. Shared among delegates. + struct select_info { + Seq seq_; // Sequence being transformed. + iterator_type icur_; // Iterator pointing at current element in seq_. + iterator_type iend_; // Iterator pointing at end of seq_. + Selector sel_; // Selector transforming the elements. + transformed_v vtransformed_; // Vector of transformed elements. + transformed_l ltransformed_; // List of transformed elements. + transformed_l_iterator llast_; // Iterator pointing to last element in ltransformed_ (before end()). + std::size_t lsize_; // Number of elements in ltransformed_. + bool use_vector_; // Whether we use ltransformed_ (false) or vtransformed_ (true). + + select_info(Seq&& seq, Selector&& sel) + : seq_(std::forward(seq)), + icur_(std::begin(seq_)), + iend_(std::end(seq_)), + sel_(std::forward(sel)), + vtransformed_(), + ltransformed_(), + llast_(ltransformed_.before_begin()), + lsize_(0), + use_vector_(try_reserve(vtransformed_, seq_)) { } + + // Cannot copy/move, stored in a shared_ptr + select_info(const select_info&) = delete; + select_info& operator=(const select_info&) = delete; + + auto get_next_in_vector(std::size_t& vncur) -> CU* { + while (vtransformed_.size() <= vncur && icur_ != iend_) { + vtransformed_.emplace_back(sel_(*icur_, vtransformed_.size())); + ++icur_; + } + return vtransformed_.size() > vncur ? std::addressof(vtransformed_[vncur++]) + : nullptr; + } + + auto get_next_in_list(transformed_l_iterator& licur) -> CU* { + while (licur == llast_ && icur_ != iend_) { + llast_ = ltransformed_.emplace_after(llast_, sel_(*icur_, lsize_++)); + ++icur_; + } + return licur != llast_ ? std::addressof(*++licur) + : nullptr; + } + + auto get_next(std::size_t& vncur, transformed_l_iterator& licur) -> CU* { + return use_vector_ ? get_next_in_vector(vncur) + : get_next_in_list(licur); + } + }; + using select_info_sp = std::shared_ptr; + + select_info_sp spinfo_; // Shared information about elements. + std::size_t vncur_; // Index of current element (if in vector). + transformed_l_iterator licur_; // Iterator pointing at current element (if in list). + + public: + next_impl(Seq&& seq, Selector&& sel) + : spinfo_(std::make_shared(std::forward(seq), + std::forward(sel))), + vncur_(0), + licur_(spinfo_->ltransformed_.before_begin()) { } + + auto operator()() -> CU* { + return spinfo_->get_next(vncur_, licur_); + } + }; + +private: + Selector sel_; // Selector used to transform elements. + +public: + explicit select_impl(Selector&& sel) + : sel_(std::forward(sel)) { } + + template()(std::declval::reference>(), std::declval())), + typename _CU = typename seq_element_traits<_SelectorRes>::const_value_type, + typename _RU = typename seq_element_traits<_SelectorRes>::raw_value_type> + auto operator()(Seq&& seq) -> enumerable<_CU> { + auto siz = try_get_size_delegate(seq); + return enumerable<_CU>(next_impl(std::forward(seq), std::forward(sel_)), + siz); + } +}; + +/** + * @internal + * @brief coveo::linq::select_many() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::select_many() and + * coveo::linq::select_many_with_index LINQ operators. + * + * @tparam Selector Selector used to transform sequence elements. + * @see coveo::linq::select_many() + * @see coveo::linq::select_many_with_index() + */ +template +class select_many_impl +{ +public: + // Next delegate implementation for select_many operator + template + class next_impl + { + private: + // Iterator used by the sequence. + using iterator_type = typename seq_traits::iterator_type; + + // List storing transformed elements. + using transformed_l = std::forward_list; + using transformed_l_iterator = typename transformed_l::iterator; + + // Bean storing info about elements. Shared among delegates. + struct select_info { + Seq seq_; // Sequence being transformed. + iterator_type icur_; // Iterator pointing at current element in seq_. + std::size_t idx_; // Index of current element in seq_. + iterator_type iend_; // Iterator pointing at end of seq_. + Selector sel_; // Selector transforming the elements. + transformed_l ltransformed_; // List of transformed elements. + transformed_l_iterator llast_; // Iterator pointing to last element in ltransformed_ (before end()). + + select_info(Seq&& seq, Selector&& sel) + : seq_(std::forward(seq)), + icur_(std::begin(seq_)), + idx_(0), + iend_(std::end(seq_)), + sel_(std::forward(sel)), + ltransformed_(), + llast_(ltransformed_.before_begin()) { } + + // Cannot copy/move, stored in a shared_ptr + select_info(const select_info&) = delete; + select_info& operator=(const select_info&) = delete; + + auto get_next(transformed_l_iterator& licur) -> CU* { + while (licur == llast_ && icur_ != iend_) { + auto new_elements = sel_(*icur_, idx_++); + llast_ = ltransformed_.insert_after(llast_, std::begin(new_elements), std::end(new_elements)); + ++icur_; + } + return licur != llast_ ? std::addressof(*++licur) + : nullptr; + } + }; + using select_info_sp = std::shared_ptr; + + select_info_sp spinfo_; // Shared information about elements. + transformed_l_iterator licur_; // Iterator pointing at current element. + + public: + next_impl(Seq&& seq, Selector&& sel) + : spinfo_(std::make_shared(std::forward(seq), + std::forward(sel))), + licur_(spinfo_->ltransformed_.before_begin()) { } + + auto operator()() -> CU* { + return spinfo_->get_next(licur_); + } + }; + +private: + Selector sel_; // Selector used to transform elements. + +public: + explicit select_many_impl(Selector&& sel) + : sel_(std::forward(sel)) { } + + template()(std::declval::const_reference>(), std::declval())), + typename _CU = typename seq_traits<_SelectorSeqRes>::const_value_type, + typename _RU = typename seq_traits<_SelectorSeqRes>::raw_value_type> + auto operator()(Seq&& seq) -> enumerable<_CU> { + return next_impl(std::forward(seq), std::forward(sel_)); + } +}; + +/** + * @internal + * @brief coveo::linq::sequence_equal() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::sequence_equal() LINQ operator. + * Version with one argument (second sequence). + * + * @tparam Seq2 Second sequence to compare. The first one will have been + * provided in the call to coveo::linq::from(). + * @see coveo::linq::sequence_equal() + */ +template +class sequence_equal_impl_1 +{ +private: + const Seq2& seq2_; // Second sequence to compare. + +public: + explicit sequence_equal_impl_1(const Seq2& seq2) + : seq2_(seq2) { } + + template + auto operator()(Seq1&& seq1) -> bool { + auto icur1 = std::begin(seq1); + auto iend1 = std::end(seq1); + auto icur2 = std::begin(seq2_); + auto iend2 = std::end(seq2_); + bool is_equal = true; + for (; is_equal && icur1 != iend1 && icur2 != iend2; ++icur1, ++icur2) { + is_equal = *icur1 == *icur2; + } + return is_equal && icur1 == iend1 && icur2 == iend2; + } +}; + +/** + * @internal + * @brief coveo::linq::sequence_equal() implementation (2). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::sequence_equal() LINQ operator. + * Version with two arguments (second sequence and predicate). + * + * @tparam Seq2 Second sequence to compare. The first one will have been + * provided in the call to coveo::linq::from(). + * @tparam Pred Predicate used to compare sequence elements. + * @see coveo::linq::sequence_equal() + */ +template +class sequence_equal_impl_2 +{ +private: + const Seq2& seq2_; // Second sequence to compare. + const Pred& pred_; // Equality predicate. + +public: + sequence_equal_impl_2(const Seq2& seq2, const Pred& pred) + : seq2_(seq2), pred_(pred) { } + + template + auto operator()(Seq1&& seq1) -> bool { + auto icur1 = std::begin(seq1); + auto iend1 = std::end(seq1); + auto icur2 = std::begin(seq2_); + auto iend2 = std::end(seq2_); + bool is_equal = true; + for (; is_equal && icur1 != iend1 && icur2 != iend2; ++icur1, ++icur2) { + is_equal = pred_(*icur1, *icur2); + } + return is_equal && icur1 == iend1 && icur2 == iend2; + } +}; + +/** + * @internal + * @brief coveo::linq::single() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::single() LINQ operator. + * Version without argument. + * + * @see coveo::linq::single() + */ +template +class single_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto ifirst = std::begin(seq); + auto iend = std::end(seq); + if (ifirst == iend) { + throw_linq_empty_sequence(); + } + auto inext = ifirst; + ++inext; + if (inext != iend) { + throw_linq_out_of_range(); + } + return *ifirst; + } +}; + +/** + * @internal + * @brief coveo::linq::single() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::single() LINQ operator. + * Version with predicate. + * + * @tparam Predicate to satisfy. + * @see coveo::linq::single() + */ +template +class single_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit single_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> decltype(*std::begin(seq)) { + auto ibeg = std::begin(seq); + auto iend = std::end(seq); + if (ibeg == iend) { + throw_linq_empty_sequence(); + } + auto ifound = std::find_if(ibeg, iend, pred_); + if (ifound == iend) { + throw_linq_out_of_range(); + } + auto inext = ifound; + ++inext; + auto ifoundagain = std::find_if(inext, iend, pred_); + if (ifoundagain != iend) { + throw_linq_out_of_range(); + } + return *ifound; + } +}; + +/** + * @internal + * @brief coveo::linq::single_or_default() implementation (0). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::single_or_default() LINQ operator. + * Version without argument. + * + * @see coveo::linq::single_or_default() + */ +template +class single_or_default_impl_0 +{ +public: + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + auto icur = std::begin(seq); + auto iend = std::end(seq); + if (icur != iend) { + auto inext = icur; + ++inext; + if (inext != iend) { + icur = iend; + } + } + return icur != iend ? *icur + : typename seq_traits::raw_value_type(); + } +}; + +/** + * @internal + * @brief coveo::linq::single_or_default() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::single_or_default() LINQ operator. + * Version with predicate. + * + * @tparam Pred Predicate to satisfy. + * @see coveo::linq::single_or_default() + */ +template +class single_or_default_impl_1 +{ +private: + const Pred& pred_; // Predicate to satisfy. + +public: + explicit single_or_default_impl_1(const Pred& pred) + : pred_(pred) { } + + template + auto operator()(Seq&& seq) -> typename seq_traits::raw_value_type { + auto iend = std::end(seq); + auto ifound = std::find_if(std::begin(seq), iend, pred_); + if (ifound != iend) { + auto inext = ifound; + ++inext; + auto ifoundagain = std::find_if(inext, iend, pred_); + if (ifoundagain != iend) { + ifound = iend; + } + } + return ifound != iend ? *ifound + : typename seq_traits::raw_value_type(); + } +}; + +/** + * @internal + * @brief coveo::linq::skip() internal predicate. + * @headerfile linq_detail.h + * + * Predicate used by the implementation of the coveo::linq::skip() + * LINQ operator. Simply skips N elements. + * + * @see coveo::linq::skip() + * @see coveo::linq::detail::skip_impl + */ +template +class skip_n_pred +{ +private: + std::size_t n_; // Number of elements to skip. + +public: + explicit skip_n_pred(std::size_t n) + : n_(n) { } + + template + auto operator()(T&&, std::size_t idx) -> bool { + return idx < n_; + } +}; + +/** + * @internal + * @brief coveo::linq::skip() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::skip(), + * coveo::linq::skip_while() and + * coveo::linq::skip_while_with_index() LINQ operators. + * + * @tparam Pred Predicate used to skip elements. + * @see coveo::linq::skip() + * @see coveo::linq::skip_while() + * @see coveo::linq::skip_while_with_index() + */ +template +class skip_impl +{ +public: + // Next delegate implementation for skip operators + template + class next_impl + { + private: + // Iterator for the sequence type. + using iterator_type = typename seq_traits::iterator_type; + + // Bean containing info to share among delegates + struct skip_info { + Seq seq_; // Sequence to skip elements from + iterator_type iend_; // Iterator pointing at end of sequence + Pred pred_; // Predicate to satisfy to skip elements + + skip_info(Seq&& seq, Pred&& pred) + : seq_(std::forward(seq)), + iend_(std::end(seq_)), + pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a shared_ptr + skip_info(const skip_info&) = delete; + skip_info& operator=(const skip_info&) = delete; + }; + using skip_info_sp = std::shared_ptr; + + skip_info_sp spinfo_; // Pointer to shared info + iterator_type icur_; // Iterator pointing at current element + bool init_called_ = false; // Whether icur_ has been initialized + + void init() { + icur_ = std::begin(spinfo_->seq_); + std::size_t n = 0; + while (icur_ != spinfo_->iend_ && spinfo_->pred_(*icur_, n++)) { + ++icur_; + } + init_called_ = true; + } + + public: + next_impl(Seq&& seq, Pred&& pred) + : spinfo_(std::make_shared(std::forward(seq), + std::forward(pred))) { } + + auto operator()() -> typename seq_traits::pointer { + // Init starting point on first call + if (!init_called_) { + init(); + } + typename seq_traits::pointer pobj = nullptr; + if (icur_ != spinfo_->iend_) { + typename seq_traits::reference robj = *icur_; + pobj = std::addressof(robj); + ++icur_; + } + return pobj; + } + }; + +private: + Pred pred_; // Predicate to satisfy to skip. + std::size_t n_; // How many items to skip, if known (otherwise -1). + +public: + skip_impl(Pred&& pred, std::size_t n) + : pred_(std::forward(pred)), + n_(n) { } + + template + auto operator()(Seq&& seq) + -> enumerable::value_type> + { + typename enumerable::value_type>::size_delegate siz; + if (n_ != static_cast(-1)) { + auto seq_siz = try_get_size_delegate(seq); + if (seq_siz != nullptr) { + const std::size_t seq_size = seq_siz(); + const std::size_t size = seq_size > n_ ? seq_size - n_ : 0; + siz = [size]() -> std::size_t { return size; }; + } + } + return enumerable::value_type>(next_impl(std::forward(seq), + std::forward(pred_)), + siz); + } +}; + +/** + * @internal + * @brief coveo::linq::sum() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::sum() LINQ operator. + * + * @tparam F Function to fetch values from sequence elements. + * @see coveo::linq::sum() + */ +template +class sum_impl +{ +private: + const F& num_f_; + +public: + explicit sum_impl(const F& num_f) + : num_f_(num_f) { } + + template + auto operator()(Seq&& seq) -> typename std::decay::type { + auto it = std::begin(seq); + auto end = std::end(seq); + if (it == end) { + throw_linq_empty_sequence(); + } + auto total = num_f_(*it); + for (++it; it != end; ++it) { + total += num_f_(*it); + } + return total; + } +}; + +/** + * @internal + * @brief coveo::linq::take() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::take(), + * coveo::linq::take_while() and + * coveo::linq::take_while_with_index() LINQ operators. + * + * @tparam Pred Predicate used to take elements. + * @see coveo::linq::take() + * @see coveo::linq::take_while() + * @see coveo::linq::take_while_with_index() + */ +template +class take_impl +{ +public: + // Next delegate implementation for take operators + template + class next_impl + { + private: + // Iterator for the sequence type. + using iterator_type = typename seq_traits::iterator_type; + + // Bean containing info to share among delegates + struct take_info { + Seq seq_; // Sequence to take elements from + iterator_type iend_; // Iterator pointing at end of sequence + Pred pred_; // Predicate to satisfy to take elements + + take_info(Seq&& seq, Pred&& pred) + : seq_(std::forward(seq)), + iend_(std::end(seq_)), + pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a shared_ptr + take_info(const take_info&) = delete; + take_info& operator=(const take_info&) = delete; + }; + using take_info_sp = std::shared_ptr; + + take_info_sp spinfo_; // Pointer to shared info + iterator_type icur_; // Iterator pointing at current element + std::size_t n_ = 0; // Index of current element + bool done_ = false; // Whether we're done taking elements + + public: + next_impl(Seq&& seq, Pred&& pred) + : spinfo_(std::make_shared(std::forward(seq), + std::forward(pred))), + icur_(std::begin(spinfo_->seq_)) { } + + auto operator()() -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + if (!done_) { + if (icur_ != spinfo_->iend_ && spinfo_->pred_(*icur_, n_++)) { + typename seq_traits::reference robj = *icur_; + pobj = std::addressof(robj); + ++icur_; + } else { + done_ = true; + } + } + return pobj; + } + }; + +private: + Pred pred_; // Predicate to satisfy to skip. + std::size_t n_; // How many items to take, if known (otherwise -1). + +public: + take_impl(Pred&& pred, std::size_t n) + : pred_(std::forward(pred)), + n_(n) { } + + template + auto operator()(Seq&& seq) + -> enumerable::value_type> + { + typename enumerable::value_type>::size_delegate siz; + if (n_ != static_cast(-1)) { + auto seq_siz = try_get_size_delegate(seq); + if (seq_siz != nullptr) { + const std::size_t size = std::min(n_, seq_siz()); + siz = [size]() -> std::size_t { return size; }; + } + } + return enumerable::value_type>(next_impl(std::forward(seq), + std::forward(pred_)), + siz); + } +}; + +/** + * @internal + * @brief coveo::linq::to() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to() LINQ operator. + * + * @tparam Container Type of container to return. + * @see coveo::linq::to() + */ +template +class to_impl +{ +public: + template + auto operator()(Seq&& seq) -> Container { + return Container(std::begin(std::forward(seq)), std::end(std::forward(seq))); + } +}; + +/** + * @internal + * @brief coveo::linq::to_vector() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to_vector() LINQ operator. + * + * @see coveo::linq::to_vector() + */ +template +class to_vector_impl +{ +public: + template + auto operator()(Seq&& seq) -> std::vector::raw_value_type> { + std::vector::raw_value_type> v; + try_reserve(v, seq); + v.insert(v.end(), std::begin(std::forward(seq)), std::end(std::forward(seq))); + return v; + } +}; + +/** + * @internal + * @brief coveo::linq::to_associative() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to_associative() LINQ operator. + * Version with one argument (key selector). + * + * @tparam Container Type of container to return. + * @tparam KeySelector Selector used to fetch keys from sequence elements. + * @see coveo::linq::to_associative() + */ +template +class to_associative_impl_1 +{ +private: + const KeySelector& key_sel_; // Selector to fetch keys for sequence elements. + +public: + explicit to_associative_impl_1(const KeySelector& key_sel) + : key_sel_(key_sel) { } + + template + auto operator()(Seq&& seq) -> Container { + Container c; + for (auto&& elem : seq) { + auto key = key_sel_(elem); + auto emplace_res = c.emplace(key, elem); + if (!emplace_res.second) { + emplace_res.first->second = elem; + } + } + return c; + } +}; + +/** + * @internal + * @brief coveo::linq::to_associative() implementation (2). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to_associative() LINQ operator. + * Version with two arguments (key and element selectors). + * + * @tparam Container Type of container to return. + * @tparam KeySelector Selector used to fetch keys from sequence elements. + * @tparam ElementSelector Selector used to fetch values from sequence elements. + * @see coveo::linq::to_associative() + */ +template +class to_associative_impl_2 +{ +private: + const KeySelector& key_sel_; // Selector to fetch keys for sequence elements. + const ElementSelector& elem_sel_; // Selector to fetch values for sequence elements. + +public: + to_associative_impl_2(const KeySelector& key_sel, const ElementSelector& elem_sel) + : key_sel_(key_sel), elem_sel_(elem_sel) { } + + template + auto operator()(Seq&& seq) -> Container { + Container c; + for (auto&& elem : seq) { + auto key = key_sel_(elem); + auto mapped = elem_sel_(elem); + auto emplace_res = c.emplace(key, mapped); + if (!emplace_res.second) { + emplace_res.first->second = mapped; + } + } + return c; + } +}; + +/** + * @internal + * @brief coveo::linq::to_map() implementation (1). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to_map() LINQ operator. + * Version with one argument (key selector). + * + * @tparam KeySelector Selector used to fetch keys from sequence elements. + * @see coveo::linq::to_map() + */ +template +class to_map_impl_1 +{ +private: + const KeySelector& key_sel_; // Selector to fetch keys for sequence elements. + +public: + explicit to_map_impl_1(const KeySelector& key_sel) + : key_sel_(key_sel) { } + + template + auto operator()(Seq&& seq) + -> std::map::type, + typename seq_traits::raw_value_type> + { + std::map::type, + typename seq_traits::raw_value_type> m; + for (auto&& elem : seq) { + auto key = key_sel_(elem); + auto emplace_res = m.emplace(key, elem); + if (!emplace_res.second) { + emplace_res.first->second = elem; + } + } + return m; + } +}; + +/** + * @internal + * @brief coveo::linq::to_map() implementation (2). + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::to_map() LINQ operator. + * Version with two arguments (key and element selectors). + * + * @tparam KeySelector Selector used to fetch keys from sequence elements. + * @tparam ElementSelector Selector used to fetch values from sequence elements. + * @see coveo::linq::to_map() + */ +template +class to_map_impl_2 +{ +private: + const KeySelector& key_sel_; // Selector to fetch keys for sequence elements. + const ElementSelector& elem_sel_; // Selector to fetch values for sequence elements. + +public: + to_map_impl_2(const KeySelector& key_sel, const ElementSelector& elem_sel) + : key_sel_(key_sel), elem_sel_(elem_sel) { } + + template + auto operator()(Seq&& seq) + -> std::map::type, + typename std::decay::type> + { + std::map::type, + typename std::decay::type> m; + for (auto&& elem : seq) { + auto key = key_sel_(elem); + auto mapped = elem_sel_(elem); + auto emplace_res = m.emplace(key, mapped); + if (!emplace_res.second) { + emplace_res.first->second = mapped; + } + } + return m; + } +}; + +/** + * @internal + * @brief coveo::linq::union_with() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::union_with() LINQ operator. + * + * @tparam Seq2 Second sequence to union. The first sequence will have been + * provided by the call to coveo::linq::from(). + * @see coveo::linq::union_with() + */ +template +class union_impl +{ +public: + // Implementation of next delegate that filters duplicate elements + template + class next_impl + { + public: + // Type of element returned by this next delegate. The elements will be const + // if at least one sequence is const. + using enum_type = typename std::conditional::value_type>::value || + std::is_const::value_type>::value, + typename seq_traits::const_value_type, + typename seq_traits::value_type>::type; + using enum_ptr_type = typename seq_element_traits::pointer; + using enum_ref_type = typename seq_element_traits::reference; + + private: + // Type of iterator for the sequences + using first_iterator_type = typename seq_traits::iterator_type; + using second_iterator_type = typename seq_traits::iterator_type; + + // Set storing pointers to seen elements + using seen_elements_set = std::set>>; + + // Info used to produce distinct elements. Shared among delegates. + class union_info + { + private: + Seq1 seq1_; // First sequence being iterated + first_iterator_type iend1_; // Iterator pointing at end of first sequence + Seq2 seq2_; // Second sequence being iterated + second_iterator_type iend2_; // Iterator pointing at end of second sequence + Pred pred_; // Predicate ordering the elements + + template + auto get_next_in_seq(It& icur, const It& iend, seen_elements_set& seen) -> enum_ptr_type { + enum_ptr_type pobj = nullptr; + for (; pobj == nullptr && icur != iend; ++icur) { + enum_ref_type robjtmp = *icur; + auto pobjtmp = std::addressof(robjtmp); + if (seen.emplace(pobjtmp).second) { + // Not seen yet, return this element. + pobj = pobjtmp; + } + } + return pobj; + } + + public: + union_info(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : seq1_(std::forward(seq1)), + iend1_(std::end(seq1_)), + seq2_(std::forward(seq2)), + iend2_(std::end(seq2_)), + pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a shared_ptr + union_info(const union_info&) = delete; + union_info& operator=(const union_info&) = delete; + + first_iterator_type first_begin() { + return std::begin(seq1_); + } + second_iterator_type second_begin() { + return std::begin(seq2_); + } + seen_elements_set init_seen_elements() { + return seen_elements_set(deref_cmp>(proxy_cmp(pred_))); + } + + // Returns next distinct element in either sequence or nullptr when done + auto get_next(first_iterator_type& icur1, second_iterator_type& icur2, seen_elements_set& seen) -> enum_ptr_type { + // First look for an element in first sequence + enum_ptr_type pobj = get_next_in_seq(icur1, iend1_, seen); + + // If we did not find an element in first sequence, try in second sequence + if (pobj == nullptr) { + pobj = get_next_in_seq(icur2, iend2_, seen); + } + + return pobj; + } + }; + using union_info_sp = std::shared_ptr; + + union_info_sp spinfo_; // Shared info + first_iterator_type icur1_; // Iterator pointing at current element in first sequence + second_iterator_type icur2_; // Iterator pointing at current element in second sequence + seen_elements_set seen_; // Set of seen elements + + public: + next_impl(Seq1&& seq1, Seq2&& seq2, Pred&& pred) + : spinfo_(std::make_shared(std::forward(seq1), + std::forward(seq2), + std::forward(pred))), + icur1_(spinfo_->first_begin()), + icur2_(spinfo_->second_begin()), + seen_(spinfo_->init_seen_elements()) { } + + auto operator()() -> decltype(spinfo_->get_next(icur1_, icur2_, seen_)) { + return spinfo_->get_next(icur1_, icur2_, seen_); + } + }; + +private: + Seq2 seq2_; // Second sequence to union. + Pred pred_; // Predicate used to compare elements + +public: + explicit union_impl(Seq2&& seq2, Pred&& pred) + : seq2_(std::forward(seq2)), + pred_(std::forward(pred)) { } + + // Movable but not copyable + union_impl(const union_impl&) = delete; + union_impl(union_impl&&) = default; + union_impl& operator=(const union_impl&) = delete; + union_impl& operator=(union_impl&&) = default; + + template + auto operator()(Seq1&& seq1) + -> enumerable::enum_type> + { + return next_impl(std::forward(seq1), + std::forward(seq2_), + std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::where() et al implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::where() and + * coveo::linq::where_with_index() LINQ operators. + * + * @tparam Pred Predicate used to filter elements. + * @see coveo::linq::where() + * @see coveo::linq::where_with_index() + */ +template +class where_impl +{ +public: + // Implementation of next delegate for where/where_with_index operators. + template + class next_impl + { + private: + // Iterator for the sequence. + using iterator_type = typename seq_traits::iterator_type; + + // Bean storing info shared among delegates. + struct where_info { + Seq seq_; // Sequence being iterated. + iterator_type iend_; // Iterator pointing at end of seq_ + Pred pred_; // Predicate to satisfy + + where_info(Seq&& seq, Pred&& pred) + : seq_(std::forward(seq)), + iend_(std::end(seq_)), + pred_(std::forward(pred)) { } + + // Cannot copy/move, stored in a shared_ptr + where_info(const where_info&) = delete; + where_info& operator=(const where_info&) = delete; + }; + using where_info_sp = std::shared_ptr; + + where_info_sp spinfo_; // Shared info containing sequence and predicate. + iterator_type icur_; // Pointer at current element. + std::size_t idx_; // Index of current element. + + public: + next_impl(Seq&& seq, Pred&& pred) + : spinfo_(std::make_shared(std::forward(seq), + std::forward(pred))), + icur_(std::begin(spinfo_->seq_)), idx_(0) { } + + auto operator()() -> typename seq_traits::pointer { + typename seq_traits::pointer pobj = nullptr; + for (; pobj == nullptr && icur_ != spinfo_->iend_; ++icur_, ++idx_) { + typename seq_traits::reference robjtmp = *icur_; + auto pobjtmp = std::addressof(robjtmp); + if (spinfo_->pred_(*pobjtmp, idx_)) { + // This element satistifies the predicate, return it. + pobj = pobjtmp; + } + } + return pobj; + } + }; + +private: + Pred pred_; // Predicate to satisfy. + +public: + explicit where_impl(Pred&& pred) + : pred_(std::forward(pred)) { } + + // Movable but not copyable + where_impl(const where_impl&) = delete; + where_impl(where_impl&&) = default; + where_impl& operator=(const where_impl&) = delete; + where_impl& operator=(where_impl&&) = default; + + template + auto operator()(Seq&& seq) + -> enumerable::value_type> + { + return next_impl(std::forward(seq), std::forward(pred_)); + } +}; + +/** + * @internal + * @brief coveo::linq::zip() implementation. + * @headerfile linq_detail.h + * + * Implementation of the coveo::linq::zip() LINQ operator. + * + * @tparam Seq2 Second sequence to zip together. The first sequence will + * have been provided by the call to coveo::linq::from(). + * @tparam ResultSelector Selector used to "zip" two sequence elements. + * @see coveo::linq::zip() + */ +template +class zip_impl +{ +private: + // Implementation of next delegate for this operator. + template + class next_impl + { + private: + // Iterator types for the sequences. + using first_iterator_type = typename seq_traits::iterator_type; + using second_iterator_type = typename seq_traits::iterator_type; + + // Containers storing zipped elements. + using zipped_v = std::vector; + using zipped_l = std::forward_list; + using zipped_l_iterator = typename zipped_l::iterator; + + // Bean storing info shared among delegates. + struct zip_info { + Seq1 seq1_; // First sequence to zip. + first_iterator_type icur1_; // Iterator pointing at current element of seq1_. + first_iterator_type iend1_; // Iterator pointing at end of seq1_. + Seq2 seq2_; // Second sequence to zip. + second_iterator_type icur2_; // Iterator pointing at current element of seq2_. + second_iterator_type iend2_; // Iterator pointing at end of seq2_. + ResultSelector result_sel_; // Selector producing the results. + zipped_v vzipped_; // Vector of zipped elements. + zipped_l lzipped_; // List of zipped elements. + zipped_l_iterator llast_; // Iterator pointing to last element in lzipped_ (before end()). + bool use_vector_; // Whether we use lzipped_ (false) or vzipped_ (true). + + zip_info(Seq1&& seq1, Seq2&& seq2, ResultSelector&& result_sel, + const typename enumerable::size_delegate& siz) + : seq1_(std::forward(seq1)), + icur1_(std::begin(seq1_)), + iend1_(std::end(seq1_)), + seq2_(std::forward(seq2)), + icur2_(std::begin(seq2_)), + iend2_(std::end(seq2_)), + result_sel_(std::forward(result_sel)), + vzipped_(), + lzipped_(), + llast_(lzipped_.before_begin()), + use_vector_(siz != nullptr) + { + if (siz != nullptr) { + vzipped_.reserve(siz()); + } + } + + // Cannot copy/move, stored in a shared_ptr + zip_info(const zip_info&) = delete; + zip_info& operator=(const zip_info&) = delete; + + auto get_next_in_vector(std::size_t& vncur) -> CU* { + while (vzipped_.size() <= vncur && icur1_ != iend1_ && icur2_ != iend2_) { + vzipped_.emplace_back(result_sel_(*icur1_, *icur2_)); + ++icur1_; + ++icur2_; + } + return vzipped_.size() > vncur ? std::addressof(vzipped_[vncur++]) + : nullptr; + } + + auto get_next_in_list(zipped_l_iterator& licur) -> CU* { + while (licur == llast_ && icur1_ != iend1_ && icur2_ != iend2_) { + llast_ = lzipped_.emplace_after(llast_, result_sel_(*icur1_, *icur2_)); + ++icur1_; + ++icur2_; + } + return licur != llast_ ? std::addressof(*++licur) + : nullptr; + } + + auto get_next(std::size_t& vncur, zipped_l_iterator& licur) -> CU* { + return use_vector_ ? get_next_in_vector(vncur) + : get_next_in_list(licur); + } + }; + using zip_info_sp = std::shared_ptr; + + zip_info_sp spinfo_; // Bean containing shared info. + std::size_t vncur_; // Index of current element (if in vector). + zipped_l_iterator licur_; // Iterator pointing at current element (if in list). + + public: + next_impl(Seq1&& seq1, Seq2&& seq2, ResultSelector&& result_sel, + const typename enumerable::size_delegate& siz) + : spinfo_(std::make_shared(std::forward(seq1), + std::forward(seq2), + std::forward(result_sel), + siz)), + vncur_(0), + licur_(spinfo_->lzipped_.before_begin()) { } + + auto operator()() -> CU* { + return spinfo_->get_next(vncur_, licur_); + } + }; + +private: + Seq2 seq2_; // Second sequence to zip. + ResultSelector result_sel_; // Selector producing the results. + +public: + zip_impl(Seq2&& seq2, ResultSelector&& result_sel) + : seq2_(std::forward(seq2)), + result_sel_(std::forward(result_sel)) { } + + // Movable but not copyable + zip_impl(const zip_impl&) = delete; + zip_impl(zip_impl&&) = default; + zip_impl& operator=(const zip_impl&) = delete; + zip_impl& operator=(zip_impl&&) = default; + + template()(std::declval::reference>(), + std::declval::reference>())), + typename _CU = typename seq_element_traits<_SelectorRes>::const_value_type, + typename _RU = typename seq_element_traits<_SelectorRes>::raw_value_type> + auto operator()(Seq1&& seq1) -> enumerable<_CU> { + auto siz1 = try_get_size_delegate(seq1); + auto siz2 = try_get_size_delegate(seq2_); + typename enumerable<_CU>::size_delegate siz; + if (siz1 != nullptr && siz2 != nullptr) { + const std::size_t min_size = std::min(siz1(), siz2()); + siz = [min_size]() -> std::size_t { return min_size; }; + } + return enumerable<_CU>(next_impl(std::forward(seq1), + std::forward(seq2_), + std::forward(result_sel_), + siz), + siz); + } +}; + +} // detail +} // linq +} // coveo + +#endif // COVEO_LINQ_DETAIL_H diff --git a/3rdParty/coveo_linq/lib/coveo/linq/exception.h b/3rdParty/coveo_linq/lib/coveo/linq/exception.h new file mode 100644 index 0000000..c4cd12d --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/linq/exception.h @@ -0,0 +1,82 @@ +/** + * @file + * @brief Exception classes used by LINQ operators. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_LINQ_EXCEPTION_H +#define COVEO_LINQ_EXCEPTION_H + +#include + +namespace coveo { +namespace linq { + +/** + * @brief Empty sequence exception. + * @headerfile exception.h + * + * Subclass of std::logic_error used by LINQ operators + * that cannot be applied to an empty sequence. + * + * @see coveo::linq::throw_linq_empty_sequence() + */ +class empty_sequence : public std::logic_error +{ +public: + empty_sequence() = delete; + using std::logic_error::logic_error; +}; + +/** + * @brief Out-of-range exception. + * @headerfile exception.h + * + * Subclass of std::out_of_range used by LINQ operators + * when going out of a sequence's range. + * + * @see coveo::linq::throw_linq_out_of_range() + */ +class out_of_range : public std::out_of_range +{ +public: + out_of_range() = delete; + using std::out_of_range::out_of_range; +}; + +/** + * @brief Helper function to throw a coveo::linq::empty_sequence exception. + * @headerfile exception.h + * + * Throws an instance of coveo::linq::empty_sequence to indicate + * that a LINQ operator does not work on the provided empty sequence. + * Does not return. + * + * @see coveo::linq::empty_sequence + */ +template +[[noreturn]] void throw_linq_empty_sequence() { + throw empty_sequence("empty_sequence"); +} + +/** + * @brief Helper function to throw a coveo::linq::out_of_range exception. + * @headerfile exception.h + * + * Throws an instance of coveo::linq::out_of_range to indicate + * that a LINQ operator has gone outside the range of the provided sequence. + * Does not return. + * + * @see coveo::linq::out_of_range + */ +template +[[noreturn]] void throw_linq_out_of_range() { + throw out_of_range("out_of_range"); +} + +} // linq +} // coveo + +#endif // COVEO_LINQ_EXCEPTION_H diff --git a/3rdParty/coveo_linq/lib/coveo/linq/linq.h b/3rdParty/coveo_linq/lib/coveo/linq/linq.h new file mode 100644 index 0000000..9826cce --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/linq/linq.h @@ -0,0 +1,4556 @@ +/** + * @file + * @brief C++ LINQ operators. + * + * This file contains the definition of all LINQ operators, as well + * as the entry points (e.g., coveo::linq::from(), etc.) and + * the chaining operator (e.g. coveo::linq::operator|()). + * This is the only file that needs to be included to use the library. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +/** + * @mainpage notitle + * @tableofcontents + * @section intro Introduction + * + * Welcome to the documentation of the coveo::linq library. This library implements + * .NET-like LINQ operators in C++. + * These can be chained to build powerful expressions to query, filter and transform data in any + * type of sequence, like arrays, containers, etc. (anything that supports std::begin() + * and std::end() should work). + * + * If this is your first time with the library, start at @ref linq "LINQ expressions". + * + * @section example Example + * + * Here is an example that chains many operators to produce a filtered, ordered sequence: + * + * @code + * #include + * #include + * + * int main() + * { + * const int FIRST[] = { 42, 23, 66, 13, 11, 7, 24, 10 }; + * const int SECOND[] = { 67, 22, 13, 23, 41, 66, 6, 7, 10 }; + * + * using namespace coveo::linq; + * + * auto is_even = [](int i) { return i % 2 == 0; }; + * + * auto seq = from(FIRST) + * | intersect(SECOND) // Intersect both arrays + * | where([](int i) { return i != 13; }) // I'm supersticious, remove 13 + * | order_by_descending(is_even) // Place even numbers first + * | then_by([](int i) { return i; }); // Then sort numbers ascending + * + * std::cout << std::endl; + * for (auto&& elem : seq) { + * std::cout << elem << " "; + * } + * std::cout << std::endl; + * // Prints "10 66 7 23" + * + * return 0; + * } + * @endcode + * + * coveo::linq::operator|() is used to @ref linq_chaining "chain together" the various + * @ref linq_operators "LINQ operators", and coveo::linq::from() is used as the + * @ref linq_entry_points "entry point" of the expression, providing the initial sequence on + * which operators will be applied. The sequence is then transformed by each operator, and + * the result is passed to the next operator. + * + * @section install Installing / requirements + * + * The coveo::linq library is header-only. Therefore, it is not + * necessary to "build" it to use. Simply copy the @c lib directory with all + * its files to a suitable place and add it to your project's include path. + * + * The library requires C++11 support. Several compilers meet + * that requirements, including the following (as tested by + * Continuous + * Integration): + * + * - GCC 5.4.1 + * - Clang 3.4 + * - Visual Studio 2015 Update 3 + * + * @section help Help / bugs / etc. + * + * Need help with the library? Found a bug? Please visit the coveo::linq + * GitHub project page at: + * + *     https://github.com/coveo/linq + * + * There, you can file issues with questions or problems. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +/** + * @defgroup linq LINQ expressions + * + * LINQ expressions are comprised of three distinct parts: an @ref linq_entry_points "entry point", + * one or more @ref linq_operators "operators" and a glue operator to @ref linq_chaining "chain" + * those elements together. The entry point provides the initial sequence on which to operate and + * each LINQ operator in turn applies a change to that sequence - be it sorting, filtering, etc. + * + * Here is an example with all elements. Here, we filter a sequence of numbers to only keep those + * above 20, then we order them by numerical value. + * + * @code + * const int NUMBERS[] = { 42, 23, 11, 66, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMBERS) + * | where([](int i) { return i >= 20; }) + * | order_by([](int i) { return i; }); + * // seq contains 23, 42, 66 + * @endcode + * + * coveo::linq::from() is the usual entry point for a LINQ expression, but there are a + * few others. coveo::linq::operator|() is used to chain the LINQ operators together. + * As for LINQ operators, there are too many of them to list them here. For more information, see: + * + * - @ref linq_entry_points + * - @ref linq_chaining + * - @ref linq_operators + */ + +/** + * @ingroup linq + * @defgroup linq_entry_points LINQ expression entry points + * @brief Functions to start a LINQ expression. + * + * An "entry point" is a function that provides the initial sequence on which to apply LINQ operators. + * It also provides a nice look to the expression, a bit like a database query. + * + * The usual entry point for a LINQ expression is coveo::linq::from(), which simply returns + * the sequence passed in. Other entry points generate a sequence for use in the expression. + */ + +/** + * @ingroup linq + * @defgroup linq_chaining LINQ operator chaining + * @brief How to chain LINQ operators into an expression. + * + * In order to create powerful LINQ expressions, operators need to be chained together so as to be + * applied in a specific order. To do this, coveo::linq::operator|() has been overloaded. + * Every @ref linq_operators "LINQ operator" returns a function object that is applied on the + * current sequence by coveo::linq::operator|(). By aligning the instances of @c | in the + * code, LINQ expressions are easy to read: + * + * @code + * using namespace coveo::linq; + * auto seq = from(some_sequence) + * | linq_op_1(...) + * | linq_op_2(...) + * | ...; + * @endcode + */ + +/** + * @ingroup linq + * @defgroup linq_operators LINQ operators + * @brief Information and reference of LINQ operators. + * + * A LINQ operator is designed to accept a sequence as input, perform some specific task with it + * and return a result, which is usually (but not necessarily) another sequence. For example, a LINQ operator + * might filter the elements of a sequence to remove unwanted ones, or it could concatenate the sequence with + * another. Some LINQ operators are inspired by database operations, like coveo::linq::join(), but + * not all of them have an equivalent in the database world. + * + * Some LINQ operators are identified as @b terminal. This means that they return a result that is not a sequence, + * but a single value. Because of this, they can only appear at the end of a LINQ expression, since no other + * operator can be chained after them. + * + * In the coveo::linq library, LINQ operators are implemented as functions that return a + * function object implementing the operator logic. The function object is then @e applied + * to the sequence by coveo::linq::operator|(). For more information on this and on how to + * develop custom LINQ operators, see @ref linq_custom_operators. + * + * The coveo::linq library includes many different operators for various needs. Most of them + * are inspired by a corresponding operator in the .NET library, although usage is often different. + * For a list of operators, see @ref linq_operators_list. + */ + +/** + * @ingroup linq_operators + * @defgroup linq_operators_list List of built-in LINQ operators + * @brief Reference for all built-in LINQ operators. + * + * This page lists all LINQ operators implemented in the coveo::linq library. + * They are listed alphabetically and grouped by purpose. + */ + +/** + * @ingroup linq_operators + * @defgroup linq_custom_operators Implementing custom LINQ operators + * @brief How to design and implement your own LINQ operators. + * + * One of the features of the coveo::linq library is that LINQ operators are + * implemented as function objects to be applied to a sequence, instead of + * member functions (of coveo::enumerable for instance.) This makes it easy + * to extend the library by implementing new operators. + * + * To make it easy to apply LINQ operators, the library defines, for each operator, a + * corresponding function that simply returns the function object. Then, + * coveo::linq::operator|() is used to @e apply the LINQ operator to a sequence. + * What this means internally is that operator|() calls the operator's function + * object's operator()() member function, passing it the sequence to apply it to. + * The operator's function object must then perform its work and return either a new + * sequence or perhaps a single value (for a terminal operator). + * + * Here is a real-world example of this. Let's say we wanted to implement a new LINQ + * operator named @c step that steps through a sequence by specific increments. First, + * we declare the shell of our function object that implements the operator, along with + * an operator()() so that the operator can be applied to a sequence: + * + * @code + * template + * class step_impl + * { + * private: + * std::size_t n_; + * + * public: + * explicit step_impl(std::size_t n) : n_(n) { } + * + * template + * auto operator()(Seq&& seq) { + * // TODO + * } + * }; + * @endcode + * + * Note that the sequence's type is not specified in the class template itself - when the + * operator's function object will be created, we won't know the type of sequence yet. + * Instead, it is specified as a template argument to operator()() itself. Also + * note that the function object's constructor must receive all parameters it needs to + * work (except for the sequence of course). In our case, we receive the number of steps + * to use for each invocation. + * + * Because our operator must step through elements of the sequence it is applied to, it needs + * to return a new sequence. Furthermore, that sequence must wrap the source sequence to be + * able to iterate its elements. One way to do this without implementing complex sequence + * logic is by using coveo::enumerable. This sequence wrapper's only requirement is + * that we implement a next delegate: a function that, when called, returns a pointer + * to the next element in the sequence, or @c nullptr when done. coveo::enumerable + * is used extensively in the coveo::linq library to implement LINQ operators. + * + * Let's add a skeleton for a next delegate to our example: + * + * @code + * template + * class step_impl + * { + * private: + * template + * class next_impl + * { + * public: + * explicit next_impl(Seq&& seq, std::site_t n) { + * // TODO + * } + * + * auto operator()() { + * // TODO + * } + * }; + * + * private: + * std::size_t n_; + * + * public: + * explicit step_impl(std::size_t n) : n_(n) { } + * + * template + * auto operator()(Seq&& seq) + * -> coveo::enumerable::value_type> + * { + * return next_impl(std::forward(seq), n_); + * } + * }; + * @endcode + * + * Note the use of coveo::seq_traits to produce a sequence of the same type + * of elements as the source sequence. Along with coveo::seq_element_traits, + * these can be used to simplify detection of sequence types in LINQ operator implementations. + * + * coveo::enumerable's implementation will @e copy the next delegate every time it + * is needed - in its copy/move constructors / assignment operators, but also when an iterator + * is fetched. This is done so that the state of the enumeration can be kept in the next delegate + * and copied as well. Because of this however, objects to be shared among delegates must be + * kept in shared memory as well - using std::shared_ptr for instance. + * + * Let's add storage for our sequence in our example's next delegate: + * + * @code + * template + * class step_impl + * { + * private: + * template + * class next_impl + * { + * private: + * using iterator_type = typename coveo::seq_traits::iterator_type; + * + * struct internals { + * Seq seq_; + * iterator_type end_; + * std::size_t n_; + * + * internals(Seq&& seq, std::size_t n) + * : seq_(std::forward(seq)), + * end_(std::end(seq_)), + * n_(n) { } + * internals(const internals&) = delete; + * internals& operator=(const internals&) = delete; + * }; + * using internals_sp = std::shared_ptr; + * + * internals_sp spint_; + * iterator_type it_; + * public: + * explicit next_impl(Seq&& seq, std::size_t n) + * : spint_(std::make_shared(std::forward(seq), n)), + * it_(std::begin(spint_->seq_)) { } + * + * auto operator()() { + * // TODO + * } + * }; + * + * private: + * std::size_t n_; + * + * public: + * explicit step_impl(std::size_t n) : n_(n) { } + * + * template + * auto operator()(Seq&& seq) + * -> coveo::enumerable::value_type> + * { + * return next_impl(std::forward(seq), n_); + * } + * }; + * @endcode + * + * The @c internals class is used to keep all data that is to be shared among next delegates, including + * the sequence we're wrapping. This allows us to avoid copying the sequence for every delegate copy. + * The @c internals are kept in a std::shared_ptr. + * + * Also note that we keep an @c end iterator in the @c internals object, but we keep another iterator + * in the next delegate directly. This is the @b state of the enumeration: the iterator points to the + * next element we'll be returning. This iterator is initialized at @c begin initially. + * + * Now, all that is left is to implement the operator logic itself: iterating over the sequence, stepping + * through elements by a specific increment. + * + * @code + * template + * class step_impl + * { + * private: + * template + * class next_impl + * { + * private: + * using iterator_type = typename coveo::seq_traits::iterator_type; + * + * struct internals { + * Seq seq_; + * iterator_type end_; + * std::size_t n_; + * + * internals(Seq&& seq, std::size_t n) + * : seq_(std::forward(seq)), + * end_(std::end(seq_)), + * n_(n) { } + * internals(const internals&) = delete; + * internals& operator=(const internals&) = delete; + * }; + * using internals_sp = std::shared_ptr; + * + * internals_sp spint_; + * iterator_type it_; + * public: + * explicit next_impl(Seq&& seq, std::size_t n) + * : spint_(std::make_shared(std::forward(seq), n)), + * it_(std::begin(spint_->seq_)) { } + * + * auto operator()() -> typename coveo::seq_traits::pointer { + * typename coveo::seq_traits::pointer pobj = nullptr; + * if (it_ != spint_->end_) { + * // Return this element. + * typename coveo::seq_traits::reference robj = *it_; + * pobj = std::addressof(robj); + * + * // Move to next element by stepping as appropriate. + * for (std::size_t i = 0; it_ != spint_->end_ && i < spint_->n_; ++i, ++it_) { + * } + * } + * return pobj; + * } + * }; + * + * private: + * std::size_t n_; + * + * public: + * explicit step_impl(std::size_t n) : n_(n) { } + * + * template + * auto operator()(Seq&& seq) + * -> coveo::enumerable::value_type> + * { + * return next_impl(std::forward(seq), n_); + * } + * }; + * + * template + * auto step(std::size_t n) -> step_impl<> { + * return step_impl<>(n); + * } + * @endcode + * + * Note that we also added a "helper function" that returns our operator's function object's implementation. + * This function will be the one used to invoke our LINQ operator: + * + * @code + * const int NUMS[] = { 42, 23, 66, 11, 7, 67 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | step(2); + * // seq == { 42, 66, 7 }; + * @endcode + * + * Implementing a terminal operator is even easier, because there's no need to provide a sequence implementation. + * Let's say we want to implement a @c prod operator that calculates the product of all sequence elements. + * Like the coveo::linq::sum() operator, it could be done by using a numerical function to + * get a numerical value for each sequence element. It could be done like this: + * + * @code + * template + * class prod_impl + * { + * private: + * const F& num_f_; + * + * public: + * explicit prod_impl(const F& num_f) : num_f_(num_f) { } + * + * template + * auto operator()(Seq&& seq) -> typename std::decay::type { + * auto it = std::begin(seq); + * auto end = std::end(seq); + * if (it == end) { + * coveo::linq::throw_linq_empty_sequence(); + * } + * auto product = num_f_(*it); + * for (++it; it != end; ++it) { + * product *= num_f_(*it); + * } + * return product; + * } + * }; + * + * template + * auto prod(const F& num_f) -> prod_impl { + * return prod_impl(num_f); + * } + * @endcode + * + * Now, using the operator is as easy as using coveo::linq::sum(): + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto product = from(NUMS) + * | prod([](int i) { return i; }); + * // product == 63756 + * @endcode + * + * For more examples on how to implement LINQ operators, the easiest way is probably to have + * a look at the implementation of the operators included in the coveo::linq library + * itself. While the helper functions are documented @ref linq_operators_list "here", it is + * possible to look at the internal implementations in the file @ref coveo/linq/detail/linq_detail.h : + * + * @include coveo/linq/detail/linq_detail.h + */ + +#ifndef COVEO_LINQ_H +#define COVEO_LINQ_H + +#include "coveo/linq/exception.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace coveo { +namespace linq { + +/** + * @ingroup linq_entry_points + * @brief Standard LINQ expression entry point. + * @headerfile linq.h + * + * Standard entry point for a LINQ expression. Specifies the initial + * sequence on which the first operator will be applied. After this, + * use coveo::linq::operator|() to chain LINQ operators and apply + * them in a specific order (see @ref linq_chaining "chaining"). + * + * Use like this: + * + * @code + * using namespace coveo::linq; + * auto result = from(some_sequence) + * | linq_operator(...) + * | ...; + * @endcode + */ +template +auto from(Seq&& seq) -> decltype(std::forward(seq)) { + return std::forward(seq); +} + +/** + * @ingroup linq_entry_points + * @brief LINQ expression entry point from iterators. + * @headerfile linq.h + * + * Entry point for a LINQ expression that produces a sequence + * of elements delimited by two iterators. After this, use + * coveo::linq::operator|() to chain LINQ operators and apply + * them in a specific order (see @ref linq_chaining "chaining"). + * + * Use like this: + * + * @code + * using namespace coveo::linq; + * auto result = from_range(something.begin(), something.end()) + * | linq_operator(...) + * | ...; + * @endcode + * + * @see coveo::enumerate_range() + */ +template +auto from_range(It ibeg, It iend) + -> decltype(enumerate_range(std::move(ibeg), std::move(iend))) +{ + return enumerate_range(std::move(ibeg), std::move(iend)); +} + +/** + * @ingroup linq_entry_points + * @brief LINQ expression entry point from range of numbers. + * @headerfile linq.h + * + * Entry point for a LINQ expression that produces a sequence + * of incrementing numbers from a starting point. After this, use + * coveo::linq::operator|() to chain LINQ operators and apply + * them in a specific order (see @ref linq_chaining "chaining"). + * + * Use like this: + * + * @code + * using namespace coveo::linq; + * auto result = from_int_range(1, 10) // 1, 2, 3... + * | linq_operator(...) + * | ...; + * @endcode + */ +template +auto from_int_range(IntT first, std::size_t count) + -> coveo::enumerable::type> +{ + std::vector::type> vvalues; + vvalues.reserve(count); + while (count-- > 0) { + vvalues.push_back(first++); + } + return enumerate_container(std::move(vvalues)); +} + +/** + * @ingroup linq_entry_points + * @brief LINQ expression entry point from a repeated value. + * @headerfile linq.h + * + * Entry point for a LINQ expression that produces a sequence + * created by repeating a given value multiple times.. After this, use + * coveo::linq::operator|() to chain LINQ operators and apply + * them in a specific order (see @ref linq_chaining "chaining"). + * + * Use like this: + * + * @code + * using namespace coveo::linq; + * auto result = from_repeated(std::string("Life"), 7) // "Life", "Life", "Life"... + * | linq_operator(...) + * | ...; + * @endcode + */ +template +auto from_repeated(const T& value, std::size_t count) + -> coveo::enumerable::type> +{ + std::vector::type> vvalues; + vvalues.reserve(count); + while (count-- > 0) { + vvalues.push_back(value); + } + return enumerate_container(std::move(vvalues)); +} + +/** + * @ingroup linq_chaining + * @brief Applies LINQ operators and allows chaining. + * @headerfile linq.h + * + * Applies a given LINQ operator to the current sequence. + * Can be used multiple times to chain many operators in a specific order. + * + * Use like this: + * + * @code + * using namespace coveo::linq; + * auto result = from(some_sequence) + * | linq_op_1(...) + * | linq_op_2(...); + * @endcode + */ +template +auto operator|(Seq&& seq, Op&& op) -> decltype(std::forward(op)(std::forward(seq))) { + return std::forward(op)(std::forward(seq)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_aggregate aggregate + * @brief Aggregates values in a sequence to produce a single value. + * + * The @c aggregate operator, as its name implies, can be used to @e aggregate all values + * in a sequence into a single value. To achieve this, it needs an aggregation function + * that will be called repeatedly to add each element in the sequence to the aggregate. + * + * This is a @b terminal operator. + * + * .NET equivalent: Aggregate + */ + +/** + * @ingroup linq_op_aggregate + * @brief Aggregates values using an aggregation function. + * + * Aggregates all elements in a sequence by repeatedly calling an aggregation function. + * The function receives two parameters: the current aggregate value, and the sequence element + * to add. The function must then add the element to the aggregate and return a new aggregate + * value. On the first call, the aggregate value is the first sequence element. + * + * Does not work on empty sequences. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto agg = from(NUMS) + * | aggregate([](int so_far, int i) { return so_far + i; }); + * // agg == 131 + * @endcode + * + * @param agg_f Aggregation function. + * @return (Once applied) Final aggregate value. + * @exception coveo::linq::empty_sequence The sequence contains no elements. + */ +template +auto aggregate(const F& agg_f) + -> detail::aggregate_impl_1 +{ + return detail::aggregate_impl_1(agg_f); +} + +/** + * @ingroup linq_op_aggregate + * @brief Aggregates values using an aggregation function, starting with a seed. + * + * Aggregates all elements in a sequence by repeatedly calling an aggregation function. + * The function receives two parameters: the current aggregate value, and the sequence element + * to add. The function must then add the element to the aggregate and return a new aggregate + * value. On the first call, the aggregate value is equal to the provided @c seed. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto agg = from(NUMS) + * | aggregate(11, + * [](int so_far, int i) { return so_far + i; }); + * // agg == 142 + * @endcode + * + * @param seed Initial aggregate value. + * @param agg_f Aggregation function. + * @return (Once applied) Final aggregate value. + */ +template +auto aggregate(const Acc& seed, const F& agg_f) + -> detail::aggregate_impl_2 +{ + return detail::aggregate_impl_2(seed, agg_f); +} + +/** + * @ingroup linq_op_aggregate + * @brief Aggregates values using aggregation function, seed and result selector. + * + * Aggregates all elements in a sequence by repeatedly calling an aggregation function. + * The function receives two parameters: the current aggregate value, and the sequence element + * to add. The function must then add the element to the aggregate and return a new aggregate + * value. On the first call, the aggregate value is equal to the provided @c seed. Once the + * final aggregate value is computed, it is passed to a result selector to produce + * a final value to return. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto agg = from(NUMS) + * | aggregate(11, + * [](int so_far, int i) { return so_far + i; }, + * [](int so_far) { return so_far / 2; }); + * // agg == 71 + * @endcode + * + * @param seed Initial aggregate value. + * @param agg_f Aggregation function. + * @param result_f Function used to produce final result from aggregate. + * @return (Once applied) Result returned by @c result_f. + */ +template +auto aggregate(const Acc& seed, const F& agg_f, const RF& result_f) + -> detail::aggregate_impl_3 +{ + return detail::aggregate_impl_3(seed, agg_f, result_f); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_all all + * @brief Checks if all elements in a sequence satisfy a predicate. + * + * The @c all operator scans a sequence and validates that all its elements + * satisfy a given @e predicate. + * + * This is a @b terminal operator. + * + * .NET equivalent: All + */ + +/** + * @ingroup linq_op_all + * @brief Checks elements in a sequence against a predicate. + * + * Scans a sequence and calls a @e predicate with each element. The predicate + * must return @c true if the element satisfies the predicate. The final result + * will indicate if all elements satisfy the predicate. + * + * Works on empty sequences (returns @c true in such a case). + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * bool all_big = from(NUMS) + * | all([](int i) { return i >= 10; }); + * bool all_odd = from(NUMS) + * | all([](int i) { return i % 2 != 0; }); + * // all_big == true + * // all_odd == false + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) @c true if all elements in sequence satisfy @c pred. + */ +template +auto all(const Pred& pred) + -> detail::all_impl +{ + return detail::all_impl(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_any any + * @brief Checks if a sequence has elements. + * + * The @c any operator checks if a sequence has elements, or if it has elements + * that satisfy a given @e predicate. + * + * This is a @b terminal operator. + * + * .NET equivalent: Any + */ + +/** + * @ingroup linq_op_any + * @brief Checks for any element. + * + * Checks if a sequence has elements. + * + * Use like this: + * + * @code + * const std::vector ONE = { 42, 23, 66 }; + * const std::vector TWO; + * + * using namespace coveo::linq; + * bool one_any = from(ONE) + * | any(); + * bool two_any = from(TWO) + * | any(); + * // one_any == true + * // two_any == false + * @endcode + * + * @return (Once applied) @c true if sequence has at least one element. + */ +template +auto any() + -> detail::any_impl_0<> +{ + return detail::any_impl_0<>(); +} + +/** + * @ingroup linq_op_any + * @brief Checks for any element that satisfy a predicate. + * + * Checks if a sequence has at least one element that satisfy a @e predicate. + * The predicate is called with each element and must return @c true if the + * element satisfy the predicate. The final result indicates if there's at least + * one element that satisfy the predicate. + * + * Works on empty sequences (returns @c false in such a case). + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * bool any_big = from(NUMS) + * | any([](int i) { return i >= 90; }); + * bool any_odd = from(NUMS) + * | any([](int i) { return i % 2 != 0; }); + * // any_big == false + * // any_odd == true + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) @c true if at least one element in sequence satisfies @c pred. + */ +template +auto any(const Pred& pred) + -> detail::any_impl_1 +{ + return detail::any_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_average average + * @brief Computes average of a sequence of values. + * + * The @c average operator computes the average of all values in a sequence. To achieve this, + * it needs a numerical function to extract a numeric value from each sequence element. + * + * This is a @b terminal operator. + * + * .NET equivalent: Average + */ + +/** + * @ingroup linq_op_average + * @brief Computes average using numerical function. + * + * Computes the average of all elements in a sequence by repeatedly calling a numerical function. + * The function receives an element as parameter and must return a numerical value for the element. + * The final result is the average of all such numerical values. + * + * Does not work on empty sequences. + * + * Use like this: + * + * @code + * const std::vector STRS = { "42", "23", "66" }; + * + * using namespace coveo::linq; + * auto avg = from(STRS) + * | average([](auto&& s) { return std::stoi(s); }); + * // avg == 43 + * @endcode + * + * @param num_f Function to get numerical value for each element. + * @return (Once applied) Average of all extracted numerical values. + * @exception coveo::linq::empty_sequence The sequence contains no elements. + */ +template +auto average(const F& num_f) + -> detail::average_impl +{ + return detail::average_impl(num_f); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_cast cast + * @brief Casts sequence elements to another type. + * + * The @c cast operator modifies a sequence by casting all its element to another type. + * + * .NET equivalent: Cast + */ + +/** + * @ingroup linq_op_cast + * @brief Casts sequence elements to another type. + * + * Casts each element in a sequence to another type and returns a new sequence + * of all such modified elements. The elements are cast using @c static_cast. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto dbls = from(NUMS) + * | cast(); + * // dbls == { 42.0, 23.0, 66.0 } + * @endcode + * + * @tparam U New type to cast elements to. + * @return (Once applied) Sequence of cast elements. + */ +template +auto cast() + -> detail::select_impl>> +{ + return detail::select_impl>>( + detail::indexless_selector_proxy>(detail::cast_selector())); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_concat concat + * @brief Concatenate two sequences. + * + * The @c concat operator concatenates two sequences, producing a new sequence + * containing all elements from both source sequences. + * + * .NET equivalent: Concat + */ + +/** + * @ingroup linq_op_concat + * @brief Concatenates two sequences. + * + * Concatenates two sequences, producing a sequence that contains all elements + * from both source sequences. + * + * For this to work, both source sequences must contain compatible elements. + * + * The resulting sequence's elements will be @c const if at least one source + * sequence's elements are @c const. + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66 }; + * const int TWO[] = { 67, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | concat(TWO); + * // seq == { 42, 23, 66, 67, 11, 7 } + * @endcode + * + * @param seq2 Second sequence to concatenate. + * @return (Once applied) Concatenation of both sequences. + */ +template +auto concat(Seq2&& seq2) + -> detail::concat_impl +{ + return detail::concat_impl(std::forward(seq2)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_contains contains + * @brief Looks for an element in a sequence. + * + * The @c contains operator determines if a sequence contains a specific element. + * + * This is a @b terminal operator. + * + * .NET equivalent: Contains + */ + +/** + * @ingroup linq_op_contains + * @brief Look for element in a sequence. + * + * Scans a sequence, looking for the provided element. Elements are compared + * using operator==(). + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto has_42 = from(NUMS) + * | contains(42); + * auto has_67 = from(NUMS) + * | contains(67); + * // has_42 == true + * // has_67 == false + * @endcode + * + * @param obj Element to look for. + * @return (Once applied) @c true if @c obj was found in sequence. + */ +template +auto contains(const T& obj) + -> detail::contains_impl_1 +{ + return detail::contains_impl_1(obj); +} + +/** + * @ingroup linq_op_contains + * @brief Look for element in a sequence using a predicate. + * + * Scans a sequence, looking for the provided element. Elements are compared + * using the provided predicate. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * auto fuzzy_equal = [](int i, int j) { return std::abs(i - j) <= 2; }; + * + * using namespace coveo::linq; + * auto has_67 = from(NUMS) + * | contains(67, fuzzy_equal); + * auto has_11 = from(NUMS) + * | contains(11, fuzzy_equal); + * // has_67 == true + * // has_11 == false + * @endcode + * + * @param obj Element to look for. + * @param pred Predicate used to compare the elements. Always receives + * @c obj as its second argument. + * @return (Once applied) @c true if @c obj was found in sequence. + */ +template +auto contains(const T& obj, const Pred& pred) + -> detail::contains_impl_2 +{ + return detail::contains_impl_2(obj, pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_count count + * @brief Counts elements in a sequence. + * + * The @c count operator computes the number of elements in a sequence, + * or the number of elements satisfying a given predicate. + * + * This is a @b terminal operator. + * + * .NET equivalent: Count + */ + +/** + * @ingroup linq_op_count + * @brief Counts elements in sequence. + * + * Computes the number of elements in a sequence. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto num = from(NUMS) + * | count(); + * // num == 3 + * @endcode + * + * @return (Once applied) Number of elements in sequence. + */ +template +auto count() + -> detail::count_impl_0<> +{ + return detail::count_impl_0<>(); +} + +/** + * @ingroup linq_op_count + * @brief Counts elements in sequence satisfying a predicate. + * + * Computes the number of elements in a sequence that satisfy the given predicate. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto num = from(NUMS) + * | count([](int i) { return i % 2 == 0; }); + * // num == 2 + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Number of elements in sequence for which + * @c pred has returned @c true. + */ +template +auto count(const Pred& pred) + -> detail::count_impl_1 +{ + return detail::count_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_def_empty default_if_empty + * @brief Ensures a sequence has at least one element. + * + * The @c default_if_empty operator either returns the source sequence or, + * if that sequence is empty, a new sequence with a single, default element. + * + * .NET equivalent: DefaultIfEmpty + */ + +/** + * @ingroup linq_op_def_empty + * @brief Ensures sequence has at least one element. + * + * Either returns the source sequence or, if that sequence is empty, + * a new sequence containing a single, default element. + * + * Use like this: + * + * @code + * const std::vector THREE = { 42, 23, 66 }; + * const std::vector EMPTY; + * + * using namespace coveo::linq; + * auto seq1 = from(THREE) + * | default_if_empty(); + * auto seq2 = from(EMPTY) + * | default_if_empty(); + * // seq1 == { 42, 23, 66 } + * // seq2 == { 0 } + * @endcode + * + * @return (Once applied) Source sequence, or sequence with + * single, default element. + */ +template +auto default_if_empty() + -> detail::default_if_empty_impl_0<> +{ + return detail::default_if_empty_impl_0<>(); +} + +/** + * @ingroup linq_op_def_empty + * @brief Ensures sequence has at least one element, specifying said element. + * + * Either returns the source sequence or, if that sequence is empty, + * a new sequence containing the provided default element. + * + * Use like this: + * + * @code + * const std::vector THREE = { 42, 23, 66 }; + * const std::vector EMPTY; + * + * using namespace coveo::linq; + * auto seq1 = from(THREE) + * | default_if_empty(11); + * auto seq2 = from(EMPTY) + * | default_if_empty(11); + * // seq1 == { 42, 23, 66 } + * // seq2 == { 11 } + * @endcode + * + * @param obj Default element to use if sequence is empty. + * @return (Once applied) Source sequence, or sequence with + * single element @c obj. + */ +template +auto default_if_empty(const T& obj) + -> detail::default_if_empty_impl_1 +{ + return detail::default_if_empty_impl_1(obj); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_distinct distinct + * @brief Filters out duplicate elements in a sequence. + * + * The @c distinct operator filters out duplicate elements in a sequence. + * Unique elements are returned in the same order they appear in the source sequence. + * + * .NET equivalent: Distinct + */ + +/** + * @ingroup linq_op_distinct + * @brief Filters out duplicate elements in sequence. + * + * Filters out duplicate elements in a sequence, returning the unique + * elements in the same order they appear in the source sequence. + * + * To filter out duplicates, elements are sorted using operator<(). + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 11, 66, 11, 42, 7, 66, 67 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | distinct(); + * // seq = { 42, 23, 11, 66, 7, 67 } + * @endcode + * + * @return (Once applied) Sequence containing all unique elements + * from source sequence. + */ +template +auto distinct() + -> detail::distinct_impl> +{ + return detail::distinct_impl>(detail::less<>()); +} + +/** + * @ingroup linq_op_distinct + * @brief Filters out duplicate elements in sequence using predicate. + * + * Filters out duplicate elements in a sequence, returning the unique + * elements in the same order they appear in the source sequence. + * + * To filter out duplicates, the provided predicate is used to sort + * the elements. The predicate must provide a strict ordering of the + * elements, like std::less. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 11, 66, 11, 42, 7, 66, 67 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | distinct([](int i, int j) { return i > j; }); + * // seq = { 42, 23, 11, 66, 7, 67 } + * @endcode + * + * @param pred Predicate used to order the elements in order to + * remove the duplicates. + * @return (Once applied) Sequence containing all unique elements + * from source sequence. + */ +template +auto distinct(Pred&& pred) + -> detail::distinct_impl +{ + return detail::distinct_impl(std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_elem_at element_at + * @brief Returns nth element in a sequence. + * + * The @c element_at operator returns the nth element in the sequence. + * If the sequence does not have enough elements, an exception is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: ElementAt + */ + +/** + * @ingroup linq_op_elem_at + * @brief Returns nth element in sequence. + * + * Returns the nth element in a sequence. If the sequence + * does not have enough elements, coveo::linq::out_of_range + * is thrown. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto second = from(NUMS) + * | element_at(1); + * // second == 23 + * // This throws an exception: + * // auto fourth = from(NUMS) + * // | element_at(3); + * @endcode + * + * @param n 0-based index of element to return. + * @return (Once applied) nth element in sequence. + * @exception coveo::linq::out_of_range The sequence does not have enough elements. + */ +template +auto element_at(std::size_t n) + -> detail::element_at_impl<> +{ + return detail::element_at_impl<>(n); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_elem_at_or_def element_at_or_default + * @brief Returns nth element in a sequence, or a default value. + * + * The @c element_at operator returns the nth element in the sequence. + * If the sequence does not have enough elements, a default value is returned instead. + * + * This is a @b terminal operator. + * + * .NET equivalent: ElementAtOrDefault + */ + +/** + * @ingroup linq_op_elem_at_or_def + * @brief Returns nth element in sequence or default value. + * + * Returns the nth element in a sequence. If the sequence + * does not have enough elements, a default-initialized + * value is returned instead. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto second = from(NUMS) + * | element_at(1); + * auto fourth = from(NUMS) + * | element_at(3); + * // second == 23 + * // fourth == 0 + * @endcode + * + * @param n 0-based index of element to return. + * @return (Once applied) nth element in sequence or, + * if sequence does not have enough elements, a default value. + */ +template +auto element_at_or_default(std::size_t n) + -> detail::element_at_or_default_impl<> +{ + return detail::element_at_or_default_impl<>(n); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_except except + * @brief Performs a set difference between two sequences. + * + * The @c except operator returns all elements in the first sequence + * that are not also found in the second sequence (essentially a set + * difference). The elements are returned in the order that they appear + * in the first sequence. + * + * .NET equivalent: Except + */ + +/** + * @ingroup linq_op_except + * @brief Performs set difference between two sequences. + * + * Returns a sequence containing all elements from the first source sequence + * that are not also in the second source sequence (essentially a set difference). + * Elements are returned in the order that they appear in the first sequence. + * + * To filter out elements, elements are sorted using operator<(). + * + * Use like this: + * + * @code + * const int YES[] = { 42, 23, 66, 11, 7, 67 }; + * const int NO[] = { 10, 7, 60, 42, 43, 50 }; + * + * using namespace coveo::linq; + * auto seq = from(YES) + * | except(NO); + * // seq = { 23, 66, 11, 67 } + * @endcode + * + * @param seq2 Second source sequence, containing the elements + * to filter out. + * @return (Once applied) Sequence containing elements from first + * source sequence that are not in @c seq2. + */ +template +auto except(Seq2&& seq2) + -> detail::except_impl> +{ + return detail::except_impl>(std::forward(seq2), detail::less<>()); +} + +/** + * @ingroup linq_op_except + * @brief Performs set difference between two sequences using predicate. + * + * Returns a sequence containing all elements from the first source sequence + * that are not also in the second source sequence (essentially a set difference). + * Elements are returned in the order that they appear in the first sequence. + * + * To filter out elements, the provided predicate is used to sort the elements. + * The predicate must provide a strict ordering of the elements, like std::less. + * + * Use like this: + * + * @code + * const int YES[] = { 42, 23, 66, 11, 7, 67 }; + * const int NO[] = { 10, 7, 60, 42, 43, 50 }; + * + * using namespace coveo::linq; + * auto seq = from(YES) + * | except(NO, [](int i, int j) { return i > j; }); + * // seq = { 23, 66, 11, 67 } + * @endcode + * + * @param seq2 Second source sequence, containing the elements + * to filter out. + * @param pred Predicate used to order elements to filter out. + * @return (Once applied) Sequence containing elements from first + * source sequence that are not in @c seq2. + */ +template +auto except(Seq2&& seq2, Pred&& pred) + -> detail::except_impl +{ + return detail::except_impl(std::forward(seq2), std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_first first + * @brief Returns first element in a sequence. + * + * The @c first operator returns the first element in a sequence, + * or the first element to satisfy a predicate. If the sequence does + * not have such an element, an exception is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: First + */ + +/** + * @ingroup linq_op_first + * @brief Returns first element in sequence. + * + * Returns the first element in a sequence. If the sequence + * does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto fir1 = from(YES) + * | first(); + * // fir1 == 42 + * // This throws an exception: + * // auto fir2 = from(NAY) + * // | first(); + * @endcode + * + * @return (Once applied) First element in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto first() + -> detail::first_impl_0<> +{ + return detail::first_impl_0<>(); +} + +/** + * @ingroup linq_op_first + * @brief Returns first element in sequence that satisfy predicate. + * + * Returns the first element in a sequence for which the given + * predicate returns @c true. If the sequence does not have elements, + * coveo::linq::empty_sequence is thrown; if the sequence does + * not contain an element that satisfy the predicate, + * coveo::linq::out_of_range is thrown. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto odd = from(NUMS) + * | first([](int i) { return i % 2 != 0; }); + * // odd == 23 + * // This throws an exception: + * // auto big = from(NUMS) + * // | first([](int i) { return i >= 90; }); + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) First element in sequence for which @c pred returns @c true. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + * @exception coveo::linq::out_of_range The sequence has no element that satisfy @c pred. + */ +template +auto first(const Pred& pred) + -> detail::first_impl_1 +{ + return detail::first_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_first_or_def first_or_default + * @brief Returns first element in a sequence, or a default value. + * + * The @c first_or_default operator returns the first element in a sequence, + * or the first element to satisfy a predicate. If the sequence does + * not have such an element, a default value is returned. + * + * This is a @b terminal operator. + * + * .NET equivalent: FirstOrDefault + */ + +/** + * @ingroup linq_op_first_or_def + * @brief Returns first element in sequence, or default value. + * + * Returns the first element in a sequence. If the sequence + * does not have elements, a default-initialized + * value is returned instead. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto fir1 = from(YES) + * | first(); + * auto fir2 = from(NAY) + * | first(); + * // fir1 == 42 + * // fir2 == 0 + * @endcode + * + * @return (Once applied) First element in sequence, or a default value + * if sequence does not have elements. + */ +template +auto first_or_default() + -> detail::first_or_default_impl_0<> +{ + return detail::first_or_default_impl_0<>(); +} + +/** + * @ingroup linq_op_first_or_def + * @brief Returns first element in sequence that satisfy predicate, or default value. + * + * Returns the first element in a sequence for which the given + * predicate returns @c true. If the sequence does not have elements + * or does not contain an element that satisfy the predicate, a + * default-initialized value is returned instead. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto odd = from(NUMS) + * | first([](int i) { return i % 2 != 0; }); + * auto big = from(NUMS) + * | first([](int i) { return i >= 90; }); + * // odd == 23 + * // big == 0 + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) First element in sequence for which @c pred returns @c true + * or, if no such element exists in sequence, a default value. + */ +template +auto first_or_default(const Pred& pred) + -> detail::first_or_default_impl_1 +{ + return detail::first_or_default_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_group_by group_by / group_values_by / group_by_and_fold / group_values_by_and_fold + * @brief Groups elements in a sequence according to their keys. + * + * The @c group_by operator (and its siblings) group elements in a sequence according to their keys. + * Keys are extracted from elements using a key selector. Variants of the operator can also + * extract values from elements using a value selector, or modify the resulting sequence using + * a result selector. + * + * .NET equivalent: GroupBy + */ + +/** + * @ingroup linq_op_group_by + * @brief Groups elements in sequence according to their keys. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector. Then, creates groups of elements + * that have a common key. The result is a sequence of pairs + * whose @c first element is a key and whose @c second element is a sequence + * of elements matching that key. The groups are returned in ascending order + * of key, as determined by operator<(). + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto groups = from(DATA) + * | group_by([](std::pair p) { return p.first; }); + * auto it = std::begin(groups); + * auto group1 = *it++; + * auto group2 = *it++; + * auto group3 = *it++; + * // group1.first == 23, group1.second == { { 23, "Hangar" }, { 23, "Jeep" } } + * // group2.first == 42, group2.second == { { 42, "Life" }, { 42, "Universe" } } + * // group3.first == 66, group3.second == { { 66, "Route" } } + * // it == std::end(groups) + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @return (Once applied) Sequence of pairs whose @c first element is a + * key and whose @c second element is a sequence of matching elements. + */ +template +auto group_by(KeySelector&& key_sel) + -> detail::group_by_impl, + detail::pair_of<>, + detail::less<>> +{ + return detail::group_by_impl, + detail::pair_of<>, + detail::less<>>(std::forward(key_sel), + detail::identity<>(), + detail::pair_of<>(), + detail::less<>()); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups elements in sequence according to their keys using predicate. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector. Then, creates groups of elements + * that have a common key. The result is a sequence of pairs + * whose @c first element is a key and whose @c second element is a sequence + * of elements matching that key. The groups are returned in order of key, + * as determined by the provided predicate. The predicate must provide a + * strict ordering of the keys, like std::less. + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto groups = from(DATA) + * | group_by([](std::pair p) { return p.first; }, + * [](int i, int j) { return i > j; }); + * auto it = std::begin(groups); + * auto group1 = *it++; + * auto group2 = *it++; + * auto group3 = *it++; + * // group1.first == 66, group1.second == { { 66, "Route" } } + * // group2.first == 42, group2.second == { { 42, "Life" }, { 42, "Universe" } } + * // group3.first == 23, group3.second == { { 23, "Hangar" }, { 23, "Jeep" } } + * // it == std::end(groups) + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of pairs whose @c first element is a + * key and whose @c second element is a sequence of matching elements. + */ +template +auto group_by(KeySelector&& key_sel, + Pred&& pred) + -> detail::group_by_impl, + detail::pair_of<>, + Pred> +{ + return detail::group_by_impl, + detail::pair_of<>, + Pred>(std::forward(key_sel), + detail::identity<>(), + detail::pair_of<>(), + std::forward(pred)); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups values in sequence according to their keys. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector and a value using the provided + * value selector. Then, creates groups of values that have + * a common key. The result is a sequence of pairs whose @c first + * element is a key and whose @c second element is a sequence of values + * matching that key. The groups are returned in ascending order of key, + * as determined by operator<(). + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto groups = from(DATA) + * | group_values_by([](std::pair p) { return p.first; }, + * [](std::pair p) { return p.second; }); + * auto it = std::begin(groups); + * auto group1 = *it++; + * auto group2 = *it++; + * auto group3 = *it++; + * // group1.first == 23, group1.second == { "Hangar", "Jeep" } + * // group2.first == 42, group2.second == { "Life", "Universe" } + * // group3.first == 66, group3.second == { "Route" } + * // it == std::end(groups) + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param value_sel Value selector, used to extract a value for a sequence element. + * @return (Once applied) Sequence of pairs whose @c first element is a + * key and whose @c second element is a sequence of matching values. + */ +template +auto group_values_by(KeySelector&& key_sel, + ValueSelector&& value_sel) + -> detail::group_by_impl, + detail::less<>> +{ + return detail::group_by_impl, + detail::less<>>(std::forward(key_sel), + std::forward(value_sel), + detail::pair_of<>(), + detail::less<>()); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups values in sequence according to their keys using predicate. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector and a value using the provided + * value selector. Then, creates groups of values that have + * a common key. The result is a sequence of pairs whose @c first + * element is a key and whose @c second element is a sequence of values + * matching that key. The groups are returned in order of key, as determined + * by the provided predicate. The predicate must provide a strict ordering + * of the keys, like std::less. + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto groups = from(DATA) + * | group_values_by([](std::pair p) { return p.first; }, + * [](std::pair p) { return p.second; }, + * [](int i, int j) { return i > j; }); + * auto it = std::begin(groups); + * auto group1 = *it++; + * auto group2 = *it++; + * auto group3 = *it++; + * // group1.first == 66, group1.second == { "Route" } + * // group2.first == 42, group2.second == { "Life", "Universe" } + * // group3.first == 23, group3.second == { "Hangar", "Jeep" } + * // it == std::end(groups) + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param value_sel Value selector, used to extract a value for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of pairs whose @c first element is a + * key and whose @c second element is a sequence of matching values. + */ +template +auto group_values_by(KeySelector&& key_sel, + ValueSelector&& value_sel, + Pred&& pred) + -> detail::group_by_impl, + Pred> +{ + return detail::group_by_impl, + Pred>(std::forward(key_sel), + std::forward(value_sel), + detail::pair_of<>(), + std::forward(pred)); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups elements in sequence according to their keys then folds the results. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector. Then, creates groups of elements + * that have a common key and uses the provided result selector + * to convert the groups. The result selector is called with two arguments: + * a key, and a sequence of elements matching that key. The final result is + * a sequence of the values returned by the result selector. The result + * selector is called in ascending order of key, as determined by + * operator<(). + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto res = from(DATA) + * | group_by_and_fold([](std::pair p) { return p.first; }, + * [](int k, coveo::enumerable> e) { k + return e.size(); }); + * // res == { 25, 44, 67 } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param result_sel Result selector, used to fold groups into final results. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_by_and_fold(KeySelector&& key_sel, + ResultSelector&& result_sel) + -> detail::group_by_impl, + ResultSelector, + detail::less<>> +{ + return detail::group_by_impl, + ResultSelector, + detail::less<>>(std::forward(key_sel), + detail::identity<>(), + std::forward(result_sel), + detail::less<>()); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups elements in sequence according to their keys using predicate, then folds the results. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector. Then, creates groups of elements + * that have a common key and uses the provided result selector + * to convert the groups. The result selector is called with two arguments: + * a key, and a sequence of elements matching that key. The final result is + * a sequence of the values returned by the result selector. The result + * selector is called in order of key, as determined by the provided + * predicate. The predicate must provide a strict ordering of the keys, + * like std::less. + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto res = from(DATA) + * | group_by_and_fold([](std::pair p) { return p.first; }, + * [](int k, coveo::enumerable> e) { k + return e.size(); }, + * [](int i, int j) { return i > j; }); + * // res == { 67, 44, 25 } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param result_sel Result selector, used to fold groups into final results. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_by_and_fold(KeySelector&& key_sel, + ResultSelector&& result_sel, + Pred&& pred) + -> detail::group_by_impl, + ResultSelector, + Pred> +{ + return detail::group_by_impl, + ResultSelector, + Pred>(std::forward(key_sel), + detail::identity<>(), + std::forward(result_sel), + std::forward(pred)); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups values in sequence according to their keys then folds the results. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector and a value using the provided + * value selector. Then, creates groups of values that have + * a common key and uses the provided result selector to convert + * the groups. The result selector is called with two arguments: a key, + * and a sequence of values matching that key. The final result is a + * sequence of the values returned by the result selector. The result + * selector is called in ascending order of key, as determined by + * operator<(). + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto res = from(DATA) + * | group_values_by_and_fold([](std::pair p) { return p.first; }, + * [](std::pair p) { return p.second; }, + * [](int k, coveo::enumerable e) { k + return e.begin()->size(); }); + * // res == { 29, 46, 71 } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param value_sel Value selector, used to extract a value for a sequence element. + * @param result_sel Result selector, used to fold groups into final results. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_values_by_and_fold(KeySelector&& key_sel, + ValueSelector&& value_sel, + ResultSelector&& result_sel) + -> detail::group_by_impl> +{ + return detail::group_by_impl>(std::forward(key_sel), + std::forward(value_sel), + std::forward(result_sel), + detail::less<>()); +} + +/** + * @ingroup linq_op_group_by + * @brief Groups values in sequence according to their keys using predicate, then folds the results. + * + * Scans the input sequence and, for each element, fetches a key using + * the provided key selector and a value using the provided + * value selector. Then, creates groups of values that have + * a common key and uses the provided result selector to convert + * the groups. The result selector is called with two arguments: a key, + * and a sequence of values matching that key. The final result is a + * sequence of the values returned by the result selector. The result + * selector is called in order of key, as determined by the provided + * predicate. The predicate must provide a strict ordering of the keys, + * like std::less. + * + * Use like this: + * + * @code + * const std::vector> DATA = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 42, "Universe" }, + * { 66, "Route" }, + * { 23, "Jeep" }, + * }; + * + * using namespace coveo::linq; + * auto res = from(DATA) + * | group_values_by_and_fold([](std::pair p) { return p.first; }, + * [](std::pair p) { return p.second; }, + * [](int k, coveo::enumerable e) { k + return e.begin()->size(); }, + * [](int i, int j) { return i > j; }); + * // res == { 71, 46, 29 } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param value_sel Value selector, used to extract a value for a sequence element. + * @param result_sel Result selector, used to fold groups into final results. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_values_by_and_fold(KeySelector&& key_sel, + ValueSelector&& value_sel, + ResultSelector&& result_sel, + Pred&& pred) + -> detail::group_by_impl +{ + return detail::group_by_impl(std::forward(key_sel), + std::forward(value_sel), + std::forward(result_sel), + std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_group_join group_join + * @brief Joins and groups elements in two sequences according to their keys. + * + * The @c group_join operator scans two sequences: an outer sequence and an + * inner sequence. For each element in the sequences, it extracts a key using + * some key selectors. Then, it creates groups of elements from the inner sequence + * and joins them to elements in the outer sequence with matching keys. Finally, a + * result selector is used to fold the groups into the final results. + * + * .NET equivalent: GroupJoin + */ + +/** + * @ingroup linq_op_group_join + * @brief Joins and groups elements in two sequences according to their keys. + * + * Extracts keys for the elements of an outer sequence (the one on which + * this operator is applied, e.g. the one passed to coveo::linq::from()) + * and the provided inner sequence using the provided key selectors. + * Next, creates groups of elements from the inner sequence with keys matching that + * of the elements in the outer sequence. Finally, the provided result selector + * is used to convert the groups into the final results. The result selector is called + * with two arguments: an element from the outer sequence and a group of elements from + * the inner sequence that share the same key. + * + * In order to match the keys, they are compared using operator<(). + * + * Use like this: + * + * @code + * using person_data = std::pair; + * using person_messages = std::pair; + * + * const std::vector PERSONS = { + * { 1, "John Doe" }, + * { 2, "Jane Smith" }, + * }; + * const std::vector MESSAGES = { + * { 1, "This is a test message" }, + * { 2, "Hello Jane!" }, + * { 1, "Doctors hate him!" }, + * { 1, "Welcome to the company" }, + * { 2, "Todo before we leave for vacation" }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(PERSONS) + * | group_join(MESSAGES, + * [](const person_data& pd) { return pd.first; }, + * [](const person_messages& pm) { return pm.first; } + * [](const person_data& pd, const coveo::enumerable& e) { + * std::string res = pd.second + ": "; + * for (auto&& pm : e) { + * res += pm.second + ", "; + * } + * return res; + * }); + * // seq == { + * // "John Doe: This is a test message, Doctors hate him!, Welcome to the company, ", + * // "Jane Smith: Hello Jane!, Todo before we leave for vacation, " + * // } + * @endcode + * + * @param inner_seq Inner sequence to scan to create groups. + * @param outer_key_sel Selector to get keys for elements in the outer sequence. + * @param inner_key_sel Selector to get keys for elements in the inner sequence. + * @param result_sel Result selector used to produce final results. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_join(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel) + -> detail::group_join_impl> +{ + return detail::group_join_impl>(std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + detail::less<>()); +} + +/** + * @ingroup linq_op_group_join + * @brief Joins and groups elements in two sequences according to their keys using predicate. + * + * Extracts keys for the elements of an outer sequence (the one on which + * this operator is applied, e.g. the one passed to coveo::linq::from()) + * and the provided inner sequence using the provided key selectors. + * Next, creates groups of elements from the inner sequence with keys matching that + * of the elements in the outer sequence. Finally, the provided result selector + * is used to convert the groups into the final results. The result selector is called + * with two arguments: an element from the outer sequence and a group of elements from + * the inner sequence that share the same key. + * + * In order to match the keys, they are compared using the provided predicate. The + * predicate must provide a strict ordering of the keys, like std::less. + * + * Use like this: + * + * @code + * using person_data = std::pair; + * using person_messages = std::pair; + * + * const std::vector PERSONS = { + * { 1, "John Doe" }, + * { 2, "Jane Smith" }, + * }; + * const std::vector MESSAGES = { + * { 1, "This is a test message" }, + * { 2, "Hello Jane!" }, + * { 1, "Doctors hate him!" }, + * { 1, "Welcome to the company" }, + * { 2, "Todo before we leave for vacation" }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(PERSONS) + * | group_join(MESSAGES, + * [](const person_data& pd) { return pd.first; }, + * [](const person_messages& pm) { return pm.first; } + * [](const person_data& pd, const coveo::enumerable& e) { + * std::string res = pd.second + ": "; + * for (auto&& pm : e) { + * res += pm.second + ", "; + * } + * return res; + * }, + * [](int i, int j) { return i > j; }); + * // seq == { + * // "John Doe: This is a test message, Doctors hate him!, Welcome to the company, ", + * // "Jane Smith: Hello Jane!, Todo before we leave for vacation, " + * // } + * @endcode + * + * @param inner_seq Inner sequence to scan to create groups. + * @param outer_key_sel Selector to get keys for elements in the outer sequence. + * @param inner_key_sel Selector to get keys for elements in the inner sequence. + * @param result_sel Result selector used to produce final results. + * @param pred Predicate used to compare keys. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto group_join(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, + Pred&& pred) + -> detail::group_join_impl +{ + return detail::group_join_impl(std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_intersect intersect + * @brief Performs a set intersection of two sequences. + * + * The @c intersect operator returns all elements in the first sequence + * that are also found in the second sequence (essentially a set intersection). + * The elements are returned in the order that they appear in the first sequence. + * + * .NET equivalent: Intersect + */ + +/** + * @ingroup linq_op_intersect + * @brief Performs set intersection of two sequences. + * + * Returns a sequence containing all elements from the first source sequence + * that are also in the second source sequence (essentially a set intersection). + * Elements are returned in the order that they appear in the first sequence. + * + * Elements are found using operator<() to compare them. + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66, 11, 7, 67 }; + * const int TWO[] = { 10, 7, 60, 42, 43, 23 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | intersect(TWO); + * // seq = { 42, 23, 7 } + * @endcode + * + * @param seq2 Second source sequence. + * @return (Once applied) Sequence containing elements from first + * source sequence that are also in @c seq2. + */ +template +auto intersect(Seq2&& seq2) + -> detail::intersect_impl> +{ + return detail::intersect_impl>(std::forward(seq2), + detail::less<>()); +} + +/** + * @ingroup linq_op_intersect + * @brief Performs set intersection of two sequences using predicate. + * + * Returns a sequence containing all elements from the first source sequence + * that are also in the second source sequence (essentially a set intersection). + * Elements are returned in the order that they appear in the first sequence. + * + * Elements are found using the provided predicate to compare them. The predicate + * must provide a strict ordering of the elements, like std::less. + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66, 11, 7, 67 }; + * const int TWO[] = { 10, 7, 60, 42, 43, 23 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | intersect(TWO, [](int i, int j) { return i > j; }); + * // seq = { 42, 23, 7 } + * @endcode + * + * @param seq2 Second source sequence. + * @param pred Predicate used to compare the elements. + * @return (Once applied) Sequence containing elements from first + * source sequence that are also in @c seq2. + */ +template +auto intersect(Seq2&& seq2, Pred&& pred) + -> detail::intersect_impl +{ + return detail::intersect_impl(std::forward(seq2), + std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_join join + * @brief Joins elements in two sequences according to their keys. + * + * The @c join operator scans two sequences: an outer sequence and an + * inner sequence. For each element in the sequences, it extracts a key using + * some key selectors. Then, it joins elements from the inner sequence to + * elements in the outer sequence with matching keys. Finally, a result selector + * is used to fold the elements into the final results. (This operator is similar to + * a database JOIN.) + * + * .NET equivalent: Join + */ + +/** + * @ingroup linq_op_join + * @brief Joins elements in two sequences according to their keys. + * + * Extracts keys for the elements of an outer sequence (the one on which + * this operator is applied, e.g. the one passed to coveo::linq::from()) + * and the provided inner sequence using the provided key selectors. + * Next, joins elements from the inner sequence with elements in the outer sequence + * with matching keys. Finally, the provided result selector is used to convert + * the elements into the final results. The result selector is called with two arguments: + * an element from the outer sequence and an element from the inner sequence that share + * the same key. + * + * In order to match the keys, they are compared using operator<(). + * + * Use like this: + * + * @code + * using person_data = std::pair; + * using person_messages = std::pair; + * + * const std::vector PERSONS = { + * { 1, "John Doe" }, + * { 2, "Jane Smith" }, + * }; + * const std::vector MESSAGES = { + * { 1, "This is a test message" }, + * { 2, "Hello Jane!" }, + * { 1, "Doctors hate him!" }, + * { 1, "Welcome to the company" }, + * { 2, "Todo before we leave for vacation" }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(PERSONS) + * | join(MESSAGES, + * [](const person_data& pd) { return pd.first; }, + * [](const person_messages& pm) { return pm.first; } + * [](const person_data& pd, const person_messages& pm) { + * return pd.second + ": " + pm.second; + * }); + * // seq == { + * // "John Doe: This is a test message", + * // "John Doe: Doctors hate him!", + * // "John Doe: Welcome to the company", + * // "Jane Smith: Hello Jane!", + * // "Jane Smith: Todo before we leave for vacation" + * // } + * @endcode + * + * @param inner_seq Inner sequence to scan. + * @param outer_key_sel Selector to get keys for elements in the outer sequence. + * @param inner_key_sel Selector to get keys for elements in the inner sequence. + * @param result_sel Result selector used to produce final results. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto join(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel) + -> detail::join_impl> +{ + return detail::join_impl>(std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + detail::less<>()); +} + +/** + * @ingroup linq_op_join + * @brief Joins elements in two sequences according to their keys using predicate. + * + * Extracts keys for the elements of an outer sequence (the one on which + * this operator is applied, e.g. the one passed to coveo::linq::from()) + * and the provided inner sequence using the provided key selectors. + * Next, joins elements from the inner sequence with elements in the outer sequence + * with matching keys. Finally, the provided result selector is used to convert + * the elements into the final results. The result selector is called with two arguments: + * an element from the outer sequence and an element from the inner sequence that share + * the same key. + * + * In order to match the keys, they are compared using the provided predicate. The + * predicate must provide a strict ordering of the keys, like std::less. + * + * Use like this: + * + * @code + * using person_data = std::pair; + * using person_messages = std::pair; + * + * const std::vector PERSONS = { + * { 1, "John Doe" }, + * { 2, "Jane Smith" }, + * }; + * const std::vector MESSAGES = { + * { 1, "This is a test message" }, + * { 2, "Hello Jane!" }, + * { 1, "Doctors hate him!" }, + * { 1, "Welcome to the company" }, + * { 2, "Todo before we leave for vacation" }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(PERSONS) + * | join(MESSAGES, + * [](const person_data& pd) { return pd.first; }, + * [](const person_messages& pm) { return pm.first; } + * [](const person_data& pd, const person_messages& pm) { + * return pd.second + ": " + pm.second; + * }, + * [](int i, int j) { return i > j; }); + * // seq == { + * // "John Doe: This is a test message", + * // "John Doe: Doctors hate him!", + * // "John Doe: Welcome to the company", + * // "Jane Smith: Hello Jane!", + * // "Jane Smith: Todo before we leave for vacation" + * // } + * @endcode + * + * @param inner_seq Inner sequence to scan. + * @param outer_key_sel Selector to get keys for elements in the outer sequence. + * @param inner_key_sel Selector to get keys for elements in the inner sequence. + * @param result_sel Result selector used to produce final results. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of values returned by @c result_sel. + */ +template +auto join(InnerSeq&& inner_seq, + OuterKeySelector&& outer_key_sel, + InnerKeySelector&& inner_key_sel, + ResultSelector&& result_sel, + Pred&& pred) + -> detail::join_impl +{ + return detail::join_impl(std::forward(inner_seq), + std::forward(outer_key_sel), + std::forward(inner_key_sel), + std::forward(result_sel), + std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_last last + * @brief Returns last element in a sequence. + * + * The @c last operator returns the last element in a sequence, + * or the last element to satisfy a predicate. If the sequence does + * not have such an element, an exception is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: Last + */ + +/** + * @ingroup linq_op_last + * @brief Returns last element in sequence. + * + * Returns the last element in a sequence. If the sequence + * does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto las1 = from(YES) + * | last(); + * // las1 == 66 + * // This throws an exception: + * // auto las2 = from(NAY) + * // | last(); + * @endcode + * + * @return (Once applied) Last element in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto last() + -> detail::last_impl_0<> +{ + return detail::last_impl_0<>(); +} + +/** + * @ingroup linq_op_last + * @brief Returns last element in sequence that satisfy predicate. + * + * Returns the last element in a sequence for which the given + * predicate returns @c true. If the sequence does not have elements, + * coveo::linq::empty_sequence is thrown; if the sequence does + * not contain an element that satisfy the predicate, + * coveo::linq::out_of_range is thrown. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto even = from(NUMS) + * | last([](int i) { return i % 2 == 0; }); + * // even == 66 + * // This throws an exception: + * // auto big = from(NUMS) + * // | last([](int i) { return i >= 90; }); + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Last element in sequence for which @c pred returns @c true. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + * @exception coveo::linq::out_of_range The sequence has no element that satisfy @c pred. + */ +template +auto last(const Pred& pred) + -> detail::last_impl_1 +{ + return detail::last_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_last_or_def last_or_default + * @brief Returns last element in a sequence, or a default value. + * + * The @c last_or_default operator returns the last element in a sequence, + * or the last element to satisfy a predicate. If the sequence does + * not have such an element, a default value is returned. + * + * This is a @b terminal operator. + * + * .NET equivalent: LastOrDefault + */ + +/** + * @ingroup linq_op_last_or_def + * @brief Returns last element in sequence, or default value. + * + * Returns the last element in a sequence. If the sequence + * does not have elements, a default-initialized + * value is returned instead. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto las1 = from(YES) + * | last(); + * auto las2 = from(NAY) + * | last(); + * // las1 == 66 + * // las2 == 0 + * @endcode + * + * @return (Once applied) Last element in sequence, or a default value + * if sequence does not have elements. + */ +template +auto last_or_default() + -> detail::last_or_default_impl_0<> +{ + return detail::last_or_default_impl_0<>(); +} + +/** + * @ingroup linq_op_last_or_def + * @brief Returns last element in sequence that satisfy predicate, or default value. + * + * Returns the last element in a sequence for which the given + * predicate returns @c true. If the sequence does not have elements + * or does not contain an element that satisfy the predicate, a + * default-initialized value is returned instead. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto even = from(NUMS) + * | last([](int i) { return i % 2 == 0; }); + * auto big = from(NUMS) + * | last([](int i) { return i >= 90; }); + * // even == 66 + * // big == 0 + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Last element in sequence for which @c pred returns @c true + * or, if no such element exists in sequence, a default value. + */ +template +auto last_or_default(const Pred& pred) + -> detail::last_or_default_impl_1 +{ + return detail::last_or_default_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_max max + * @brief Returns maximum element in a sequence. + * + * The @c max operator returns the maximum element in a sequence. If the + * sequence does not have elements, an exception is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: Max + */ + +/** + * @ingroup linq_op_max + * @brief Returns maximum element in sequence. + * + * Returns the maximum element in a sequence. If the sequence + * does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66, 11, 7 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto max1 = from(YES) + * | max(); + * // max1 == 66 + * // This throws an exception: + * // auto max2 = from(NAY) + * // | max(); + * @endcode + * + * @return (Once applied) Maximum element in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto max() + -> detail::max_impl_0<> +{ + return detail::max_impl_0<>(); +} + +/** + * @ingroup linq_op_max + * @brief Returns maximum projected value in sequence. + * + * Returns the maximum value in a sequence by projecting each element + * to a different value using the provided selector. If the + * sequence does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66, 11, 7 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto max1 = from(YES) + * | max([](int i) { return -i; }); + * // max1 == -7 + * // This throws an exception: + * // auto max2 = from(NAY) + * // | max([](int i) { return -i; }); + * @endcode + * + * @param sel Selector called to project each element into a different value. + * @return (Once applied) Maximum projected value in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto max(const Selector& sel) + -> detail::max_impl_1 +{ + return detail::max_impl_1(sel); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_min min + * @brief Returns minimum element in a sequence. + * + * The @c min operator returns the minimum element in a sequence. If the + * sequence does not have elements, an exception is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: Min + */ + +/** + * @ingroup linq_op_min + * @brief Returns minimum element in sequence. + * + * Returns the minimum element in a sequence. If the sequence + * does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66, 11, 7 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto min1 = from(YES) + * | min(); + * // min1 == 7 + * // This throws an exception: + * // auto min2 = from(NAY) + * // | min(); + * @endcode + * + * @return (Once applied) Minimum element in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto min() + -> detail::min_impl_0<> +{ + return detail::min_impl_0<>(); +} + +/** + * @ingroup linq_op_min + * @brief Returns minimum projected value in sequence. + * + * Returns the minimum value in a sequence by projecting each element + * to a different value using the provided selector. If the + * sequence does not have elements, coveo::linq::empty_sequence + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66, 11, 7 }; + * const std::vector NAY; + * + * using namespace coveo::linq; + * auto min1 = from(YES) + * | min([](int i) { return -i; }); + * // min1 == -66 + * // This throws an exception: + * // auto min2 = from(NAY) + * // | min([](int i) { return -i; }); + * @endcode + * + * @param sel Selector called to project each element into a different value. + * @return (Once applied) Minimum projected value in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + */ +template +auto min(const Selector& sel) + -> detail::min_impl_1 +{ + return detail::min_impl_1(sel); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_none none + * @brief Checks if no element in a sequence satisfy a predicate. + * + * The @c none operator scans a sequence and validates that none of its elements + * satisfy a given @e predicate. + * + * This is a @b terminal operator. + * + * .NET equivalent: n/a + */ + +/** + * @ingroup linq_op_none + * @brief Checks elements in a sequence against a predicate. + * + * Scans a sequence and calls a @e predicate with each element. The predicate + * must return @c true if the element satisfies the predicate. The final result + * will indicate if no elements satisfy the predicate. + * + * Works on empty sequences (returns @c true in such a case). + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * bool no_big = from(NUMS) + * | none([](int i) { return i >= 90; }); + * bool no_odd = from(NUMS) + * | none([](int i) { return i % 2 != 0; }); + * // no_big == true + * // no_odd == false + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) @c true if no element in sequence satisfy @c pred. + */ +template +auto none(const Pred& pred) + -> detail::none_impl +{ + return detail::none_impl(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_order_by order_by / order_by_descending / then_by / then_by_descending + * @brief Orders elements in a sequence. + * + * The @c order_by operator (and its siblings) extract keys from each element in a sequence, + * then orders those elements according to those keys. Depending on the operator used, the + * ordering will be ascending or descending. If multiple operators are chained (using + * coveo::linq::then_by()), elements will be ordered according to the first key + * extracted and elements with equivalent keys will be further ordered by the other keys. + * + * .NET equivalent: OrderBy / OrderByDescending / ThenBy / ThenByDescending + */ + +/** + * @ingroup linq_op_order_by + * @brief Orders elements in sequence by ascending key. + * + * For each element in the input sequence, extracts a key using the + * provided key selector. Then, returns a sequence containing + * the same elements, ordered according to the keys in ascending order. + * + * Keys are compared using operator<(). + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 10, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 14, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 20, 8 }, + * // { 3, 3, 3 }, + * // { 10, 2, -4 }, + * // { 14, 29, 1 } + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @return (Once applied) Sequence of elements ordered by ascending key. + */ +template +auto order_by(KeySelector&& key_sel) + -> detail::order_by_impl, false>> +{ + typedef detail::order_by_comparator, false> comparator; + return detail::order_by_impl(detail::make_unique(std::forward(key_sel), detail::less<>())); +} + +/** + * @ingroup linq_op_order_by + * @brief Orders elements in sequence by ascending key using predicate. + * + * For each element in the input sequence, extracts a key using the + * provided key selector. Then, returns a sequence containing + * the same elements, ordered according to the keys in ascending order. + * + * Keys are compared using the provided predicate. The predicate must + * provide a strict ordering of the keys, like std::less. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 10, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 14, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }, + * [](int i, int j) { return i < j; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 20, 8 }, + * // { 3, 3, 3 }, + * // { 10, 2, -4 }, + * // { 14, 29, 1 } + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of elements ordered by ascending key. + */ +template +auto order_by(KeySelector&& key_sel, Pred&& pred) + -> detail::order_by_impl> +{ + typedef detail::order_by_comparator comparator; + return detail::order_by_impl(detail::make_unique(std::forward(key_sel), std::forward(pred))); +} + +/** + * @ingroup linq_op_order_by + * @brief Orders elements in sequence by descending key. + * + * For each element in the input sequence, extracts a key using the + * provided key selector. Then, returns a sequence containing + * the same elements, ordered according to the keys in descending order. + * + * Keys are compared using operator<(), but the result is reversed + * to produce descending order. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 10, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 14, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by_descending([](const coord& c) { return c.x; }); + * // seq == { + * // { 14, 29, 1 } + * // { 10, 2, -4 }, + * // { 3, 3, 3 }, + * // { 2, 20, 8 }, + * // { -1, 1, 12 }, + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @return (Once applied) Sequence of elements ordered by descending key. + */ +template +auto order_by_descending(KeySelector&& key_sel) + -> detail::order_by_impl, true>> +{ + typedef detail::order_by_comparator, true> comparator; + return detail::order_by_impl(detail::make_unique(std::forward(key_sel), detail::less<>())); +} + +/** + * @ingroup linq_op_order_by + * @brief Orders elements in sequence by descending key using predicate. + * + * For each element in the input sequence, extracts a key using the + * provided key selector. Then, returns a sequence containing + * the same elements, ordered according to the keys in descending order. + * + * Keys are compared using the provided predicate, but the result is reversed + * to produce descending order. The predicate must provide a strict ordering + * of the keys, like std::less. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 10, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 14, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by_descending([](const coord& c) { return c.x; }, + * [](int i, int j) { return i < j; }); + * // seq == { + * // { 14, 29, 1 } + * // { 10, 2, -4 }, + * // { 3, 3, 3 }, + * // { 2, 20, 8 }, + * // { -1, 1, 12 }, + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of elements ordered by descending key. + */ +template +auto order_by_descending(KeySelector&& key_sel, Pred&& pred) + -> detail::order_by_impl> +{ + typedef detail::order_by_comparator comparator; + return detail::order_by_impl(detail::make_unique(std::forward(key_sel), std::forward(pred))); +} + +/** + * @ingroup linq_op_order_by + * @brief Further orders elements in sequence by ascending key. + * + * Further orders a sequence previously ordered via coveo::linq::order_by() + * or similar operator. Elements that were previously considered equivalent will + * be further ordered by ascending order of keys as extracted by the provided + * key selector. + * + * Keys are compared using operator<(). + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 2, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 3, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }) + * | then_by([](const coord& c) { return c.y; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 2, -4 }, + * // { 2, 20, 8 }, + * // { 3, 3, 3 }, + * // { 3, 29, 1 } + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @return (Once applied) Sequence of elements further ordered by ascending key. + */ +template +auto then_by(KeySelector&& key_sel) + -> decltype(order_by(std::forward(key_sel))) +{ + return order_by(std::forward(key_sel)); +} + +/** + * @ingroup linq_op_order_by + * @brief Further orders elements in sequence by ascending key using predicate. + * + * Further orders a sequence previously ordered via coveo::linq::order_by() + * or similar operator. Elements that were previously considered equivalent will + * be further ordered by ascending order of keys as extracted by the provided + * key selector. + * + * Keys are compared using the provided predicate. The predicate must + * provide a strict ordering of the keys, like std::less. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 2, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 3, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }, + * [](int i, int j) { return i < j; }) + * | then_by([](const coord& c) { return c.y; }, + * [](int i, int j) { return i < j; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 2, -4 }, + * // { 2, 20, 8 }, + * // { 3, 3, 3 }, + * // { 3, 29, 1 } + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of elements further ordered by ascending key. + */ +template +auto then_by(KeySelector&& key_sel, Pred&& pred) + -> decltype(order_by(std::forward(key_sel), std::forward(pred))) +{ + return order_by(std::forward(key_sel), std::forward(pred)); +} + +/** + * @ingroup linq_op_order_by + * @brief Further orders elements in sequence by descending key. + * + * Further orders a sequence previously ordered via coveo::linq::order_by() + * or similar operator. Elements that were previously considered equivalent will + * be further ordered by descending order of keys as extracted by the provided + * key selector. + * + * Keys are compared using operator<(), but the result is reversed + * to produce descending order. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 2, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 3, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }) + * | then_by_descending([](const coord& c) { return c.y; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 20, 8 }, + * // { 2, 2, -4 }, + * // { 3, 29, 1 } + * // { 3, 3, 3 }, + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @return (Once applied) Sequence of elements further ordered by descending key. + */ +template +auto then_by_descending(KeySelector&& key_sel) + -> decltype(order_by_descending(std::forward(key_sel))) +{ + return order_by_descending(std::forward(key_sel)); +} + +/** + * @ingroup linq_op_order_by + * @brief Further orders elements in sequence by descending key using predicate. + * + * Further orders a sequence previously ordered via coveo::linq::order_by() + * or similar operator. Elements that were previously considered equivalent will + * be further ordered by descending order of keys as extracted by the provided + * key selector. + * + * Keys are compared using the provided predicate, but the result is reversed + * to produce descending order. The predicate must provide a strict ordering + * of the keys, like std::less. + * + * Use like this: + * + * @code + * using coord = std::tuple; + * const std::vector COORDS = { + * { 2, 2, -4 }, + * { 2, 20, 8 }, + * { -1, 1, 12 }, + * { 3, 29, 1 }, + * { 3, 3, 3 }, + * }; + * + * using namespace coveo::linq; + * auto seq = from(COORDS) + * | order_by([](const coord& c) { return c.x; }, + * [](int i, int j) { return i < j; }) + * | then_by_descending([](const coord& c) { return c.y; }, + * [](int i, int j) { return i < j; }); + * // seq == { + * // { -1, 1, 12 }, + * // { 2, 20, 8 }, + * // { 2, 2, -4 }, + * // { 3, 29, 1 } + * // { 3, 3, 3 }, + * // } + * @endcode + * + * @param key_sel Key selector, used to extract a key for a sequence element. + * @param pred Predicate used to compare the keys. + * @return (Once applied) Sequence of elements further ordered by descending key. + */ +template +auto then_by_descending(KeySelector&& key_sel, Pred&& pred) + -> decltype(order_by_descending(std::forward(key_sel), std::forward(pred))) +{ + return order_by_descending(std::forward(key_sel), std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_reverse reverse + * @brief Reverses elements in a sequence. + * + * The @c reverse operator, as its name implies, scans a sequence a produces a new + * sequence in which the elements are reversed. + * + * .NET equivalent: Reverse + */ + +/** + * @ingroup linq_op_reverse + * @brief Reverses elements in sequence. + * + * Produces a new sequence in which elements in the source sequence + * are in reverse order. + * + * Use like this: + * + * @code + * const std::vector NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | reverse(); + * // seq == { 7, 11, 66, 23, 42 } + * @endcode + * + * @return (Once applied) Sequence of elements in reverse order. + */ +template +auto reverse() + -> detail::reverse_impl<> +{ + return detail::reverse_impl<>(); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_select select / select_with_index / select_many / select_many_with_index + * @brief Projects elements in a sequence into another form. + * + * The @c select operator scans a sequence and projects each of its element into another + * form using a selector, returning a sequence of the results. A little similar + * to std::transform(). + * + * .NET equivalent: Select / SelectMany + */ + +/** + * @ingroup linq_op_select + * @brief Projects elements in sequence into another form. + * + * Produces a new sequence by projecting each element in the source + * sequence into another form, a little like std::transform(). + * + * Use like this: + * + * @code + * const std::vector STRS = { + * "Life, the Universe and Everything", + * "Hangar", + * "Route", + * }; + * + * using namespace coveo::linq; + * auto seq = from(STRS) + * | select([](const std::string& s) { return s.size(); }); + * // seq == { 33, 6, 5 } + * @endcode + * + * @param sel Selector used to project each element in source sequence + * into another form. + * @return (Once applied) Sequence of projected values. + */ +template +auto select(Selector&& sel) + -> detail::select_impl> +{ + return detail::select_impl>( + detail::indexless_selector_proxy(std::forward(sel))); +} + +/** + * @ingroup linq_op_select + * @brief Projects elements in sequence into another form using element index. + * + * Produces a new sequence by projecting each element in the source + * sequence into another form, a little like std::transform(). + * The selector receives, as second argument, the index of the element + * in the source sequence. + * + * Use like this: + * + * @code + * const std::vector STRS = { + * "Life, the Universe and Everything", + * "Hangar", + * "Route", + * }; + * + * using namespace coveo::linq; + * auto seq = from(STRS) + * | select_with_index([](const std::string& s, std::size_t i) { return s.size() * (i + 1); }); + * // seq == { 33, 12, 15 } + * @endcode + * + * @param sel Selector used to project each element in source sequence + * into another form. Receives index of element as second argument. + * @return (Once applied) Sequence of projected values. + */ +template +auto select_with_index(Selector&& sel) + -> detail::select_impl +{ + return detail::select_impl(std::forward(sel)); +} + +/** + * @ingroup linq_op_select + * @brief Projects elements in sequence into many values and flattens them. + * + * Produces a new sequence by projecting each element in the source + * sequence into a sequence of new values, then flattens all those sequences. + * + * Use like this: + * + * @code + * const std::vector STRS = { + * "Life, the Universe and Everything", + * "Hangar", + * "Route", + * }; + * + * using namespace coveo::linq; + * auto seq = from(STRS) + * | select_many([](const std::string& s) { + * return std::vector{ + * s.size(), + * static_cast(s.front() - 'A'), + * }; + * }); + * // seq == { 33, 11, 6, 7, 5, 17 } + * @endcode + * + * @param sel Selector used to project each element in source sequence + * into a sequence of values. + * @return (Once applied) Sequence of flattened projected values. + */ +template +auto select_many(Selector&& sel) + -> detail::select_many_impl> +{ + return detail::select_many_impl>( + detail::indexless_selector_proxy(std::forward(sel))); +} + +/** + * @ingroup linq_op_select + * @brief Projects elements in sequence into many values using element index and flattens them. + * + * Produces a new sequence by projecting each element in the source + * sequence into a sequence of new values, then flattens all those sequences. + * The selector receives, as second argument, the index of the element + * in the source sequence. + * + * Use like this: + * + * @code + * const std::vector STRS = { + * "Life, the Universe and Everything", + * "Hangar", + * "Route", + * }; + * + * using namespace coveo::linq; + * auto seq = from(STRS) + * | select_many_with_index([](const std::string& s, std::size_t i) { + * return std::vector{ + * s.size(), + * static_cast(s.front() - 'A'), + * i + 1, + * }; + * }); + * // seq == { 33, 11, 1, 6, 7, 2, 5, 17, 3 } + * @endcode + * + * @param sel Selector used to project each element in source sequence + * into a sequence of values. Receives index of element as + * second argument. + * @return (Once applied) Sequence of flattened projected values. + */ +template +auto select_many_with_index(Selector&& sel) + -> detail::select_many_impl +{ + return detail::select_many_impl(std::forward(sel)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_seq_equal sequence_equal + * @brief Compares two sequences. + * + * The @c sequence_equal operator compares two sequences and validates + * that they contain the same elements. + * + * This is a @b terminal operator. + * + * .NET equivalent: SequenceEqual + */ + +/** + * @ingroup linq_op_seq_equal + * @brief Compares elements in two sequences. + * + * Scans two sequences and compares each corresponding element using + * operator==(). Returns @c true if the two sequences contain + * the same elements in the same order. + * + * Use like this: + * + * @code + * const int NUMS1 = { 42, 23, 66 }; + * const std::vector NUMS2 = { 42, 23, 66 }; + * const std::vector NUMS3 = { 67, 11, 7 }; + * + * using namespace coveo::linq; + * bool one_and_two = from(NUMS1) + * | sequence_equal(NUMS2); + * bool one_and_three = from(NUMS1) + * | sequence_equal(NUMS3); + * // one_and_two == true + * // one_and_three == false + * @endcode + * + * @param seq2 Second sequence to compare. The first sequence is the one on + * which the operator will be applied (e.g., the sequence passed + * to coveo::linq::from()). + * @return (Once applied) @c true both sequences contain the same elements + * in the same order. + */ +template +auto sequence_equal(const Seq2& seq2) + -> detail::sequence_equal_impl_1 +{ + return detail::sequence_equal_impl_1(seq2); +} + +/** + * @ingroup linq_op_seq_equal + * @brief Compares elements in two sequences using predicate. + * + * Scans two sequences and compares each corresponding element using + * the provided predicate. Returns @c true if the two sequences contain + * the same elements in the same order. + * + * Use like this: + * + * @code + * const int NUMS1 = { 42, 23, 66 }; + * const std::vector NUMS2 = { 41, 24, 67 }; + * const std::vector NUMS3 = { 30, 30, 90 }; + * + * auto fuzzy_equal = [](int i, int j) { return std::abs(i - j) <= 2; }; + * + * using namespace coveo::linq; + * bool one_and_two = from(NUMS1) + * | sequence_equal(NUMS2, fuzzy_equal); + * bool one_and_three = from(NUMS1) + * | sequence_equal(NUMS3, fuzzy_equal); + * // one_and_two == true + * // one_and_three == false + * @endcode + * + * @param seq2 Second sequence to compare. The first sequence is the one on + * which the operator will be applied (e.g., the sequence passed + * to coveo::linq::from()). + * @param pred Predicate used to compare the elements. + * @return (Once applied) @c true both sequences contain the same elements + * in the same order. + */ +template +auto sequence_equal(const Seq2& seq2, const Pred& pred) + -> detail::sequence_equal_impl_2 +{ + return detail::sequence_equal_impl_2(seq2, pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_single single + * @brief Returns the only element in a sequence. + * + * The @c single operator returns the only element in a sequence, + * or the only element to satisfy a predicate. If the sequence does + * not have such an element or has more than one of them, an exception + * is thrown. + * + * This is a @b terminal operator. + * + * .NET equivalent: Single + */ + +/** + * @ingroup linq_op_single + * @brief Returns only element in sequence. + * + * Returns the only element in a sequence. If the sequence + * does not have elements or has more than one element, an + * exception is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42 }; + * const std::vector NAY1; + * const std::vector NAY2 = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto val1 = from(YES) + * | single(); + * // val1 == 42 + * // This throws an exception: + * // auto val2 = from(NAY1) + * // | single(); + * // This also throws an exception: + * // auto val3 = from(NAY2) + * // | single(); + * @endcode + * + * @return (Once applied) Only element in sequence. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + * @exception coveo::linq::out_of_range The sequence has more than one element. + */ +template +auto single() + -> detail::single_impl_0<> +{ + return detail::single_impl_0<>(); +} + +/** + * @ingroup linq_op_single + * @brief Returns only element in sequence satisfying predicate. + * + * Returns the only element in a sequence that satisfy the given + * predicate. If no element in the sequence or if more than one + * element in the sequence satisfy the predicate, an exception + * is thrown. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY1 = { 67, 11, 7 }; + * const std::vector NAY2 = { 42, 24, 66 }; + * + * auto is_odd = [](int i) { return i % 2 != 0; }; + * + * using namespace coveo::linq; + * auto val1 = from(YES) + * | single(is_odd); + * // val1 == 23 + * // This throws an exception: + * // auto val2 = from(NAY1) + * // | single(is_odd); + * // This also throws an exception: + * // auto val3 = from(NAY2) + * // | single(is_odd); + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Only element in sequence for which @c pred returned @c true. + * @exception coveo::linq::empty_sequence The sequence does not have elements. + * @exception coveo::linq::out_of_range No element satisfy @c pred, or more than one + * element satisfy @c pred. + */ +template +auto single(const Pred& pred) + -> detail::single_impl_1 +{ + return detail::single_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_single_or_def single_or_default + * @brief Returns the only element in a sequence, or a default value. + * + * The @c single_or_default operator returns the only element in a sequence, + * or the only element to satisfy a predicate. If the sequence does not have + * such an element or has more than one of them, a default value is returned + * instead. + * + * This is a @b terminal operator. + * + * .NET equivalent: SingleOrDefault + */ + +/** + * @ingroup linq_op_single_or_def + * @brief Returns only element in sequence, or default value. + * + * Returns the only element in a sequence. If the sequence + * does not have elements or has more than one element, a + * default-initialized value is returned instead. + * + * Use like this: + * + * @code + * const std::vector YES = { 42 }; + * const std::vector NAY1; + * const std::vector NAY2 = { 42, 23, 66 }; + * + * using namespace coveo::linq; + * auto val1 = from(YES) + * | single_or_default(); + * auto val2 = from(NAY1) + * | single_or_default(); + * auto val3 = from(NAY2) + * | single_or_default(); + * // val1 == 42 + * // val2 == 0 + * // val3 == 0 + * @endcode + * + * @return (Once applied) Only element in sequence, or default value + * if sequence does not have elements or if it has more than + * one element. + */ +template +auto single_or_default() + -> detail::single_or_default_impl_0<> +{ + return detail::single_or_default_impl_0<>(); +} + +/** + * @ingroup linq_op_single_or_def + * @brief Returns only element in sequence satisfying predicate, or default value. + * + * Returns the only element in a sequence that satisfy the given + * predicate. If no element in the sequence or if more than one + * element in the sequence satisfy the predicate, a + * default-initialized value is returned instead. + * + * Use like this: + * + * @code + * const std::vector YES = { 42, 23, 66 }; + * const std::vector NAY1 = { 67, 11, 7 }; + * const std::vector NAY2 = { 42, 24, 66 }; + * + * auto is_odd = [](int i) { return i % 2 != 0; }; + * + * using namespace coveo::linq; + * auto val1 = from(YES) + * | single_or_default(is_odd); + * auto val2 = from(NAY1) + * | single_or_default(is_odd); + * auto val3 = from(NAY2) + * | single_or_default(is_odd); + * // val1 == 23 + * // val2 == 0 + * // val3 == 0 + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Only element in sequence for which @c pred returned @c true, + * or a default value if either no element satisfy @c pred or more than one + * element satisfy @c pred. + */ +template +auto single_or_default(const Pred& pred) + -> detail::single_or_default_impl_1 +{ + return detail::single_or_default_impl_1(pred); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_skip skip / skip_while / skip_while_with_index + * @brief Skips the first elements in a sequence. + * + * The @c skip operator (and its siblings) skip the first elements in a + * sequence, either by skipping a certain number or using a predicate. + * + * .NET equivalent: Skip / SkipWhile + */ + +/** + * @ingroup linq_op_skip + * @brief Skips the first N elements in sequence. + * + * Given a source sequence, returns a new sequence that skips + * the first @c n elements of the source sequence. If the source + * sequence contains less than @c n elements, returns an empty + * sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | skip(3); + * // seq == { 11, 7 } + * @endcode + * + * @param n Number of elements to skip. + * @return (Once applied) New sequence skipping the first @c n + * elements of source sequence. + */ +template +auto skip(std::size_t n) + -> detail::skip_impl> +{ + return detail::skip_impl>(detail::skip_n_pred<>(n), n); +} + +/** + * @ingroup linq_op_skip + * @brief Skips the first elements in sequence satisfying predicate. + * + * Given a source sequence, returns a new sequence that skips + * the first elements of the source sequence that satisfy the + * provided predicate. If the source sequence contains only + * elements that satisfy the predicate, returns an empty sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | skip_while([](int i) { return i < 60; }); + * // seq == { 66, 11, 7 } + * @endcode + * + * @param pred Predicate used to skip elements. + * @return (Once applied) New sequence skipping the first + * elements of source sequence satisfying @c pred. + */ +template +auto skip_while(Pred&& pred) + -> detail::skip_impl> +{ + return detail::skip_impl>(detail::indexless_selector_proxy(std::forward(pred)), + static_cast(-1)); +} + +/** + * @ingroup linq_op_skip + * @brief Skips the first elements in sequence satisfying predicate using element index. + * + * Given a source sequence, returns a new sequence that skips + * the first elements of the source sequence that satisfy the + * provided predicate. If the source sequence contains only + * elements that satisfy the predicate, returns an empty sequence. + * + * The predicate is called with two parameters every time: an + * element from the source sequence, and its position in the sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | skip_while_with_index([](int i, std::size_t idx) { return i < 90 && idx < 3; }); + * // seq == { 11, 7 } + * @endcode + * + * @param pred Predicate used to skip elements. + * @return (Once applied) New sequence skipping the first + * elements of source sequence satisfying @c pred. + */ +template +auto skip_while_with_index(Pred&& pred) + -> detail::skip_impl +{ + return detail::skip_impl(std::forward(pred), + static_cast(-1)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_sum sum + * @brief Computes sum of a sequence of values. + * + * The @c sum operator computes the sum of all values in a sequence. To achieve this, + * it needs a numerical function to extract a numeric value from each sequence element. + * + * This is a @b terminal operator. + * + * .NET equivalent: Sum + */ + +/** + * @ingroup linq_op_sum + * @brief Computes sum using numerical function. + * + * Computes the sum of all elements in a sequence by repeatedly calling a numerical function. + * The function receives an element as parameter and must return a numerical value for the element. + * The final result is the sum of all such numerical values. + * + * Does not work on empty sequences. + * + * Use like this: + * + * @code + * const std::vector STRS = { "42", "23", "66" }; + * + * using namespace coveo::linq; + * auto tot = from(STRS) + * | sum([](auto&& s) { return std::stoi(s); }); + * // tot == 131 + * @endcode + * + * @param num_f Function to get numerical value for each element. + * @return (Once applied) Sum of all extracted numerical values. + * @exception coveo::linq::empty_sequence The sequence contains no elements. + */ +template +auto sum(const F& num_f) + -> detail::sum_impl +{ + return detail::sum_impl(num_f); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_take take / take_while / take_while_with_index + * @brief Keeps only the first elements in a sequence. + * + * The @c take operator (and its siblings) keep only the first elements in + * a sequence, either by taking a certain number or using a predicate. + * + * .NET equivalent: Take / TakeWhile + */ + +/** + * @ingroup linq_op_take + * @brief Keeps the first N elements in sequence. + * + * Given a source sequence, returns a new sequence that contains + * only the first @c n elements of the source sequence. If the + * source sequence contains less than @c n elements, returns as + * many elements as possible. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | take(3); + * // seq == { 42, 23, 66 } + * @endcode + * + * @param n Number of elements to take. + * @return (Once applied) New sequence containing the first @c n + * elements of source sequence. + */ +template +auto take(std::size_t n) + -> detail::take_impl> +{ + return detail::take_impl>(detail::skip_n_pred<>(n), n); +} + +/** + * @ingroup linq_op_take + * @brief Keeps the first elements in sequence satisfying predicate. + * + * Given a source sequence, returns a new sequence that contains + * only the first elements of the source sequence that satisfy the + * provided predicate. If the source sequence contains no element + * that satisfy the predicate, returns an empty sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | take_while([](int i) { return i > 20; }); + * // seq == { 42, 23, 66 } + * @endcode + * + * @param pred Predicate used to take elements. + * @return (Once applied) New sequence containing the first + * elements of source sequence satisfying @c pred. + */ +template +auto take_while(Pred&& pred) + -> detail::take_impl> +{ + return detail::take_impl>(detail::indexless_selector_proxy(std::forward(pred)), + static_cast(-1)); +} + +/** + * @ingroup linq_op_take + * @brief Keeps the first elements in sequence satisfying predicate using element index. + * + * Given a source sequence, returns a new sequence that contains + * only the first elements of the source sequence that satisfy the + * provided predicate. If the source sequence contains no element + * that satisfy the predicate, returns an empty sequence. + * + * The predicate is called with two parameters every time: an + * element from the source sequence, and its position in the sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | take_while_with_index([](int i, std::size_t idx) { return i > 20 || idx < 4; }); + * // seq == { 42, 23, 66, 11 } + * @endcode + * + * @param pred Predicate used to take elements. + * @return (Once applied) New sequence containing the first + * elements of source sequence satisfying @c pred. + */ +template +auto take_while_with_index(Pred&& pred) + -> detail::take_impl +{ + return detail::take_impl(std::forward(pred), + static_cast(-1)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_to to / to_vector / to_associative / to_map + * @brief Converts a sequence into another container type. + * + * The @c to operator (and its siblings) converts a sequence into + * another type of container. + * + * This is a terminal operator if the resulting container is not + * in itself a valid sequence. + * + * .NET equivalent: ToArray / ToDictionary / ToList / ToLookup + */ + +/** + * @ingroup linq_op_to + * @brief Converts sequence into another container. + * + * Given a source sequence, returns a new container of the provided type + * that contains the source sequence's elements. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto lst = from(NUMS) + * | to>(); + * // lst == std::list{ 42, 23, 66, 11, 7 } + * @endcode + * + * @tparam Container Type of container to convert to. + * @return (Once applied) Object of type @c Container + * storing the same elements as source sequence. + */ +template +auto to() + -> detail::to_impl +{ + return detail::to_impl(); +} + +/** + * @ingroup linq_op_to + * @brief Converts sequence into std::vector. + * + * Given a source sequence, returns a new std::vector that + * contains the source sequence's elements. The vector's element type + * is auto-detected from the source sequence. + * + * Use like this: + * + * @code + * const int NUMS = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto vec = from(NUMS) + * | to_vector(); + * // vec == std::vector{ 42, 23, 66, 11, 7 } + * @endcode + * + * @return (Once applied) std::vector storing the same + * elements as source sequence. + */ +template +auto to_vector() + -> detail::to_vector_impl<> +{ + return detail::to_vector_impl<>(); +} + +/** + * @ingroup linq_op_to + * @brief Converts sequence into associative container using key selector. + * + * Given a source sequence, returns a new associative container of the + * provided type that maps each element's key, as extracted by the provided + * key selector, to the corresponding element. + * + * Use like this: + * + * @code + * using our_pair = std::pair; + * using our_mm = std::multimap; + * + * const std::vector = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 66, "Route" }, + * }; + * + * using namespace coveo::linq; + * auto omp = from(NUMS) + * | to_associative([](our_pair p) { return p.first; }); + * // omp == our_mm{ + * // 23 => { 23, "Hangar" }, + * // 42 => { 42, "Life" }, + * // 66 => { 66, "Route" } + * // } + * @endcode + * + * @tparam Container Type of associative container to convert to. + * @param key_sel Selector used to extract keys for sequence elements. + * @return (Once applied) Object of type @c Container + * mapping the source sequence's elements as specified. + */ +template +auto to_associative(const KeySelector& key_sel) + -> detail::to_associative_impl_1 +{ + return detail::to_associative_impl_1(key_sel); +} + +/** + * @ingroup linq_op_to + * @brief Converts sequence into associative container using key and element selector. + * + * Given a source sequence, returns a new associative container of the + * provided type that maps each element's key, as extracted by the provided + * key selector, to the corresponding mapped value, as extracted by + * the provided element selector. + * + * Use like this: + * + * @code + * using our_pair = std::pair; + * using our_mm = std::multimap; + * + * const std::vector = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 66, "Route" }, + * }; + * + * using namespace coveo::linq; + * auto omp = from(NUMS) + * | to_associative([](our_pair p) { return p.first; }, + * [](our_pair p) { return p.second; }); + * // omp == our_mm{ + * // 23 => "Hangar", + * // 42 => "Life", + * // 66 => "Route" + * // } + * @endcode + * + * @tparam Container Type of associative container to convert to. + * @param key_sel Selector used to extract keys for sequence elements. + * @param elem_sel Selector used to extract mapped values for sequence elements. + * @return (Once applied) Object of type @c Container + * mapping the source sequence's elements as specified. + */ +template +auto to_associative(const KeySelector& key_sel, + const ElementSelector& elem_sel) + -> detail::to_associative_impl_2 +{ + return detail::to_associative_impl_2(key_sel, elem_sel); +} + +/** + * @ingroup linq_op_to + * @brief Converts sequence into std::map using key selector. + * + * Given a source sequence, returns a new std::map that maps + * each element's key, as extracted by the provided key selector, + * to the corresponding element. The map's type is auto-detected from + * the source sequence and selector. + * + * Use like this: + * + * @code + * using our_pair = std::pair; + * + * const std::vector = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 66, "Route" }, + * }; + * + * using namespace coveo::linq; + * auto omp = from(NUMS) + * | to_map([](our_pair p) { return p.first; }); + * // omp == std::map{ + * // 23 => { 23, "Hangar" }, + * // 42 => { 42, "Life" }, + * // 66 => { 66, "Route" } + * // } + * @endcode + * + * @param key_sel Selector used to extract keys for sequence elements. + * @return (Once applied) std::map mapping the source sequence's + * elements as specified. + */ +template +auto to_map(const KeySelector& key_sel) + -> detail::to_map_impl_1 +{ + return detail::to_map_impl_1(key_sel); +} + +/** + * @ingroup linq_op_to + * @brief Converts sequence into std::map using key and element selector. + * + * Given a source sequence, returns a new std::map that maps + * each element's key, as extracted by the provided key selector, + * to the corresponding mapped value, as extracted by the provided + * element selector. The map's type is auto-detected from + * the source sequence and selectors. + * + * Use like this: + * + * @code + * using our_pair = std::pair; + * + * const std::vector = { + * { 42, "Life" }, + * { 23, "Hangar" }, + * { 66, "Route" }, + * }; + * + * using namespace coveo::linq; + * auto omp = from(NUMS) + * | to_map([](our_pair p) { return p.first; }, + * [](our_pair p) { return p.second; }); + * // omp == std::map{ + * // 23 => "Hangar", + * // 42 => "Life", + * // 66 => "Route" + * // } + * @endcode + * + * @param key_sel Selector used to extract keys for sequence elements. + * @param elem_sel Selector used to extract mapped values for sequence elements. + * @return (Once applied) std::map mapping the source sequence's + * elements as specified. + */ +template +auto to_map(const KeySelector& key_sel, + const ElementSelector& elem_sel) + -> detail::to_map_impl_2 +{ + return detail::to_map_impl_2(key_sel, elem_sel); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_union union_with + * @brief Performs a set union of two sequences. + * + * The @c union_with operator returns the union of all elements in the first and + * second sequence (essentially a set union). Similar to a concatenation, except + * duplicate elements are filtered out. The elements are returned in the order + * that they appear in the sequences. + * + * .NET equivalent: Union + */ + +/** + * @ingroup linq_op_union + * @brief Performs set union of two sequences. + * + * Returns a sequence containing all elements from the two source sequences + * (essentially a set union). Duplicate elements are filtered out. Elements + * are returned in the order that they appear in the sequences. + * + * Elements are compared using operator<(). + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66, 11, 7, 67 }; + * const int TWO[] = { 10, 7, 60, 42, 43, 23 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | union_with(TWO); + * // seq = { 42, 23, 66, 11, 7, 67, 10, 60, 43 } + * @endcode + * + * @param seq2 Second source sequence. + * @return (Once applied) Sequence containing all elements from + * first source sequence and from @c seq2. + */ +template +auto union_with(Seq2&& seq2) + -> detail::union_impl> +{ + return detail::union_impl>(std::forward(seq2), + detail::less<>()); +} + +/** + * @ingroup linq_op_union + * @brief Performs set union of two sequences using predicate. + * + * Returns a sequence containing all elements from the two source sequences + * (essentially a set union). Duplicate elements are filtered out. Elements + * are returned in the order that they appear in the sequences. + * + * Elements are compared using the provided predicate. The predicate must + * provide a strict ordering of the elements, like std::less. + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66, 11, 7, 67 }; + * const int TWO[] = { 10, 7, 60, 42, 43, 23 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | union_with(TWO, [](int i, int j) { return i > j; }); + * // seq = { 42, 23, 66, 11, 7, 67, 10, 60, 43 } + * @endcode + * + * @param seq2 Second source sequence. + * @param pred Predicate used to compare sequence elements. + * @return (Once applied) Sequence containing all elements from + * first source sequence and from @c seq2. + */ +template +auto union_with(Seq2&& seq2, Pred&& pred) + -> detail::union_impl +{ + return detail::union_impl(std::forward(seq2), + std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_where where / where_with_index + * @brief Filters a sequence. + * + * The @c where operator (and its siblings) filter the elements in a sequence, + * returning a new sequence containing only the elements that satisfy a predicate. + * + * .NET equivalent: Where + */ + +/** + * @ingroup linq_op_where + * @brief Filters sequence elements using predicate. + * + * Returns a sequence that contains only the elements from the + * source sequence that satisfy the given predicate. Order of + * the elements is preserved. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | where([](int i) { return i % 2 != 0; }); + * // seq = { 23, 11, 7 } + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Sequence containing all elements from + * source sequence for which @c pred returned @c true. + */ +template +auto where(Pred&& pred) + -> detail::where_impl> +{ + return detail::where_impl>( + detail::indexless_selector_proxy(std::forward(pred))); +} + +/** + * @ingroup linq_op_where + * @brief Filters sequence elements using predicate and element index. + * + * Returns a sequence that contains only the elements from the + * source sequence that satisfy the given predicate. Order of + * the elements is preserved. + * + * The predicate receives two arguments: a sequence element and its + * position in the sequence. + * + * Use like this: + * + * @code + * const int NUMS[] = { 42, 23, 66, 11, 7 }; + * + * using namespace coveo::linq; + * auto seq = from(NUMS) + * | where_with_index([](int i, std::size_t idx) { return i % 2 != 0 || idx == 0; }); + * // seq = { 42, 23, 11, 7 } + * @endcode + * + * @param pred Predicate to satisfy. + * @return (Once applied) Sequence containing all elements from + * source sequence for which @c pred returned @c true. + */ +template +auto where_with_index(Pred&& pred) + -> detail::where_impl +{ + return detail::where_impl(std::forward(pred)); +} + +/** + * @ingroup linq_operators_list + * @defgroup linq_op_zip zip + * @brief Zips two sequences by combining corresponding elements. + * + * The @c zip operator scans two source sequences and and passes elements + * with the same index to a result selector, then produces a + * sequence containing the results returned by that selector. + * + * .NET equivalent: Zip + */ + +/** + * @ingroup linq_op_zip + * @brief Zips two sequences. + * + * Scans two source sequences and calls the provided + * result selector with elements of the two + * sequences that have the same index. Then, produces + * a sequence of the results returned by that selector. + * The resulting sequence will be as long as the shortest + * of the two input sequences. + * + * Use like this: + * + * @code + * const int ONE[] = { 42, 23, 66, 11, 7 }; + * const int TWO[] = { 8, 21, 90, 4 }; + * + * using namespace coveo::linq; + * auto seq = from(ONE) + * | zip(TWO, [](int i, int j) { return i + j; }); + * // seq = { 50, 44, 156, 15 } + * @endcode + * + * @param seq2 Second sequence to scan. The first sequence is the + * one to which the operator is applied, e.g. the + * one passed to coveo::linq::from(). + * @param result_sel Selector used to "zip" two corresponding + * sequence elements. + * @return (Once applied) Sequence containing zipped elements + * from the two sequences. + */ +template +auto zip(Seq2&& seq2, ResultSelector&& result_sel) + -> detail::zip_impl +{ + return detail::zip_impl(std::forward(seq2), + std::forward(result_sel)); +} + +} // linq +} // coveo + +#endif // COVEO_LINQ_H diff --git a/3rdParty/coveo_linq/lib/coveo/seq/detail/enumerable_detail.h b/3rdParty/coveo_linq/lib/coveo/seq/detail/enumerable_detail.h new file mode 100644 index 0000000..d6f1d29 --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/seq/detail/enumerable_detail.h @@ -0,0 +1,95 @@ +/** + * @file + * @brief Implementation details of coveo::enumerable. + * + * This file contains implementation details used by coveo::enumerable. + * It should not be necessary to use this file directly when using the class. + * Code in the coveo::detail namespace is subject to change + * in-between versions. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_ENUMERABLE_DETAIL_H +#define COVEO_ENUMERABLE_DETAIL_H + +#include +#include +#include +#include +#include + +namespace coveo { +namespace detail { + +/** + * @internal + * @brief Type trait that detects @c begin. + * @headerfile enumerable_detail.h + * + * Type trait that can be used to know if std::begin(T) is valid. + * Detects both std::begin() specializations and begin() methods. + * + * @tparam Type for which we need @c begin. + */ +template +class has_begin +{ + static_assert(sizeof(std::int_least8_t) != sizeof(std::int_least32_t), + "has_begin only works if int_least8_t has a different size than int_least32_t"); + + template static std::int_least8_t test(decltype(std::begin(std::declval()))*); // Will be selected if std::begin(C) works + template static std::int_least32_t test(...); // Will be selected otherwise +public: + static constexpr bool value = sizeof(test(nullptr)) == sizeof(std::int_least8_t); +}; + +/** + * @internal + * @brief Type trait that detects @c end. + * @headerfile enumerable_detail.h + * + * Type trait that can be used to know if std::end(T) is valid. + * Detects both std::end() specializations and end() methods. + * + * @tparam Type for which we need @c end. + */ +template +class has_end +{ + static_assert(sizeof(std::int_least8_t) != sizeof(std::int_least32_t), + "has_end only works if int_least8_t has a different size than int_least32_t"); + + template static std::int_least8_t test(decltype(std::end(std::declval()))*); // Will be selected if std::end(C) works + template static std::int_least32_t test(...); // Will be selected otherwise +public: + static const bool value = sizeof(test(nullptr)) == sizeof(std::int_least8_t); +}; + +/** + * @internal + * @brief Type trait that detects @c size. + * @headerfile enumerable_detail.h + * + * Type trait that can be used to know if type @c T has a size() + * @c const method that returns a non-void result. + * + * @tparam Type for which we need @c size. + */ +template +class has_size_const_method +{ + static_assert(sizeof(std::int_least8_t) != sizeof(std::int_least32_t), + "has_size_const_method only works if int_least8_t has a different size than int_least32_t"); + + template static std::int_least8_t test(typename std::enable_if().size())>::value, void*>::type); // Will be selected if C has size() that does not return void + template static std::int_least32_t test(...); // Will be selected otherwise +public: + static const bool value = sizeof(test(nullptr)) == sizeof(std::int_least8_t); +}; + +} // detail +} // coveo + +#endif // COVEO_ENUMERABLE_DETAIL_H diff --git a/3rdParty/coveo_linq/lib/coveo/seq/enumerable.h b/3rdParty/coveo_linq/lib/coveo/seq/enumerable.h new file mode 100644 index 0000000..2374cf4 --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/seq/enumerable.h @@ -0,0 +1,922 @@ +/** + * @file + * @brief C++ implementation of .NET's IEnumerable-like data structure. + * + * This file contains the definition of coveo::enumerable, which + * is a wrapper for a sequence similar to .NET's IEnumerable<T>. + * Also includes helper methods and functions to create enumerables + * from single values, containers, etc. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_ENUMERABLE_H +#define COVEO_ENUMERABLE_H + +#include "coveo/seq/sequence_util.h" +#include + +#include +#include +#include +#include +#include + +namespace coveo { + +/** + * @brief Type-erased sequence wrapper. + * @headerfile enumerable.h + * + * Inspired by .NET's IEnumerable<T>, coveo::enumerable + * is a type-erased wrapper for a multipass, forward-only sequence of elements. + * It is possible to iterate over the elements using begin() / end() + * (or cbegin() / cend(), which is equivalent since this class is + * immutable). + * + * In order to fetch the elements to return, this class uses a next delegate - + * a function that returns a pointer to the next element every time it is called, + * finally returning @c nullptr when the enumeration is over. The next delegate + * is provided at construction time and cannot change. + * + * Optionally, it is also possible to specify a size delegeate for the + * @c enumerable at construction time. If provided, it will be used to fetch the + * number of elements in the sequence when size() is called; otherwise, + * a slower algorithm is used (iterating over the entire sequence using + * std::distance()). It is possible to determine if the @c enumerable + * has a fast size delegate by using has_fast_size(). + * + * While this class is immutable, the elements it returns are not necessarily so. + * In order to wrap a sequence of @c const elements, @c const must also be specified + * in this class' template argument: + * + * @code + * coveo::enumerable e1; // Iterates over const integers + * coveo::enumerable e2; // Iterates over non-const integers + * @endcode + * + * @tparam T Type of elements stored in the sequence. + */ +template +class enumerable +{ +/// @cond + + // Allows compatible enumerables to be friend with each other + template friend class enumerable; + +/// @endcond + +public: + /** + * @brief Type of element in the sequence. + * + * Type of element stored in the sequence. + */ + using value_type = typename seq_element_traits::value_type; + + /** + * @brief Raw type of element in the sequence. + * + * Same as coveo::enumerable::value_type, but "raw", e.g. without @c const or @c volatile. + */ + using raw_value_type = typename seq_element_traits::raw_value_type; + + /** + * @brief Pointer to a sequence element. + * + * Pointer to an element in the sequence. + * Corresponds to coveo::enumerable::value_type*. + * This is the type of pointer returned by the coveo::enumerable::next_delegate. + */ + using pointer = typename seq_element_traits::pointer; + + /** + * @brief Reference to a sequence element. + * + * Reference to an element in the sequence. + * Corresponds to coveo::enumerable::value_type&. + * This is the type of reference returned by the coveo::enumerable::iterators. + */ + using reference = typename seq_element_traits::reference; + + /** + * @brief Delegate to fetch next element. + * + * Delegate that will be called by the @c enumerable to fetch + * the next element in the sequence when needed. The delegate + * must return a coveo::enumerable::pointer to the next + * element if there is one, or @c nullptr when done. + */ + using next_delegate = std::function; + + /** + * @brief Delegate to fetch sequence size. + * + * Delegate that will be called by the @c enumerable to fetch + * the number of elements in the sequence when size() + * is called. + */ + using size_delegate = std::function; + + // Forward declaration of iterator class. + class iterator; + + /** + * @brief @c iterator alias. + * + * Alias for coveo::enumerable::iterator. Provided because even though + * begin() and end() are already @c const, some generic code + * might want to use @c const_iterator. + */ + using const_iterator = iterator; + +private: + next_delegate zero_; // Next delegate which we will clone to iterate sequence. + size_delegate size_; // Optional size delegate. + +public: + /** + * @brief Default constructor. + * + * Default constructor. Wraps an empty sequence. + * + * Using this constructor is equivalent to calling empty(). + * + * @see empty() + */ + enumerable() + : zero_([]() -> pointer { return nullptr; }), + size_([]() -> std::size_t { return 0; }) { } + + /** + * @brief Constructor with delegates. + * + * Constructor that accepts a coveo::enumerable::next_delegate + * as well as an optional coveo::enumerable::size_delegate. + * The next delegate is used to fetch the elements to return + * during iteration. The size delegate, if provided, will + * be called to know the size() of the sequence. + * + * @param next Function object to use as next delegate. + * @param siz Optional function object to use as size delegate. Defaults to @c nullptr. + * @see coveo::enumerable::next_delegate + * @see coveo::enumerable::size_delegate + * @see size() + * @see has_fast_size() + */ + template::type>::value && + (!detail::has_begin::type>::value || + !detail::has_end::type>::value), void>::type> + enumerable(F&& next, _S&& siz = nullptr) + : zero_(std::forward(next)), size_(std::forward<_S>(siz)) { } + + /** + * @brief Constructor with container. + * + * Constructor that wraps a container. The container must have the + * following methods to be accepted by this constructor: + * + * - @c begin (or a specialization of std::begin()) + * - @c end (or a specialization of std::end()) + * + * The container will be wrapped in the enumerable, either by + * keeping a reference to it or by storing it internally, depending + * on how the container is passed to the method (if it is moved, + * it will be stored internally). + * + * Using this constructor is equivalent to calling for_container(). + * It is provided implicitely so that it is possible to call a function + * declared as accepting a coveo::enumerable with a container + * instance directly. + * + * @param cnt Container to wrap. + * @see for_container() + */ + template::type>::value && + detail::has_begin::type>::value && + detail::has_end::type>::value, void>::type> + enumerable(C&& cnt) + : zero_(), size_() { *this = for_container(std::forward(cnt)); } + + /** + * @brief Constructor for non-const to @c const conversion. + * + * Constructor that creates an enumerable over @c const elements from + * an enumerable over non-const elements of the same type. + * Allows implicit non-const to @c const conversion. + * + * @param e coveo::enumerable to convert. + */ + template::value && + std::is_same::type>::value, void>::type> + enumerable(enumerable e) + : zero_(std::move(e.zero_)), size_(std::move(e.size_)) { } + + /** + * @brief Assignment operator for non-const to @c const conversion. + * + * Assignment operator that allows an enumerable over non-const + * elements to be assigned to an enumerable over @c const elements of + * the same type. Allows non-const to @c const conversion. + * + * @param e coveo::enumerable to convert. + * @return Reference to @c this enumerable. + */ + template::value && + std::is_same::type>::value, void>::type> + enumerable& operator=(enumerable e) { + zero_ = std::move(e.zero_); + size_ = std::move(e.size_); + return *this; + } + + /** + * @brief Iterator to beginning of sequence. + * + * Returns a coveo::enumerable::iterator pointing at + * the beginning of the sequence. Together with end(), + * it can be used to enumerate the elements in the sequence. + * + * The iterator allows the elements to be modified if the + * enumerable's type @c T is not @c const. + * + * @return @c iterator to beginning of sequence. + * @see end() + */ + iterator begin() const { + return iterator(*this, false); + } + + /** + * @brief Alias for begin(). + * + * Since this class is immutable, calling this is + * equivalent to calling begin(). + * + * @return @c iterator to beginning of sequence. + * @see begin() + * @see cend() + */ + iterator cbegin() const { + return begin(); + } + + /** + * @brief Iterator to end of sequence. + * + * Returns a coveo::enumerable::iterator pointing at + * the end of the sequence. Together with begin(), + * it can be used to enumerable the elements in the sequence. + * + * @return @c iterator to end of sequence. + * @see begin() + */ + iterator end() const { + return iterator(*this, true); + } + + /** + * @brief Alias for end(). + * + * Since this class is immutable, calling this is + * equivalent to calling end(). + * + * @return @c iterator to end of sequence. + * @see end() + * @see cbegin() + */ + iterator cend() const { + return end(); + } + + /** + * @brief Determines if size() is fast. + * + * Method that can be used to know if this @c enumerable has a + * coveo::enumerable::size_delegate. If so, calling + * size() should be reasonably fast; otherwise, + * size() will have to iterate over the entire sequence + * in order to return a result. + * + * @return @c true if size() should be reasonably fast. + * @see size() + */ + bool has_fast_size() const { + // If we have a delegate, size() should be reasonably fast + return size_ != nullptr; + } + + /** + * @brief Size of sequence. + * + * Returns the number of elements in the sequence. If this @c enumerable + * has a coveo::enumerable::size_delegate, it will be used to + * fetch the information; otherwise, a more lenghty process is used + * (e.g., using std::distance(), which forces the iteration + * over the entire sequence). + * + * @return Number of elements in the sequence. + * @see has_fast_size() + */ + std::size_t size() const { + // If we have a delegate, use it, otherwise use distance. + return size_ != nullptr ? size_() + : static_cast(std::distance(begin(), end())); + } + + /** + * @brief Non-const to @c const explicit conversion. + * + * Returns a copy of this @c enumerable, but iterating over + * @c const elements. Can be used to explicitely perform a + * non-const to @c const conversion. + * + * @return @c enumerable over @c const elements. + */ + auto as_const() const -> enumerable::type> { + return enumerable::type>(*this); + } + +public: + /** + * @brief Iterator for elements in the sequence. + * + * Iterator for elements in a coveo::enumerable's sequence. + * Uses the enumerable's next delegate to fetch + * references to the elements during iteration. + * + * Elements are always late-initialized, e.g. they are not fetched + * from the next delegate until they are accessed. + * + * @see coveo::enumerable::begin() + * @see coveo::enumerable::end() + */ + class iterator + { + public: + /** + * @brief Iterator category. + * + * Standard iterator typedef for iterator category. Because @c enumerable is a forward-only + * sequence, this corresponds to std::forward_iterator_tag to identify a + * ForwardIterator. + */ + using iterator_category = std::forward_iterator_tag; + + /** + * @brief Type of elements returned by iterator. + * + * Type of the elements returned by this iterator. + * + * @see coveo::enumerable::value_type + */ + using value_type = typename enumerable::value_type; + + /** + * @brief Raw type of elements returned by iterator. + * + * Type of the elements returned by this iterator, but "raw", + * e.g. without @c const or @c volatile. + * + * @see coveo::enumerable::raw_value_type + */ + using raw_value_type = typename enumerable::raw_value_type; + + /** + * @brief Difference type. + * + * Standard iterator typedef for difference between iterators. + * Corresponds to std::ptfdiff_t. + */ + using difference_type = std::ptrdiff_t; + + /** + * @brief Pointer to elements returned by iterator. + * + * Pointer to the type of elements returned by this iterator. + * + * @see coveo::enumerable::pointer + */ + using pointer = typename enumerable::pointer; + + /** + * @brief Reference to elements returned by iterator. + * + * Reference to the type of elements returned by this iterator. + * This is the type of reference returned by operator*(). + * + * @see coveo::enumerable::reference. + */ + using reference = typename enumerable::reference; + + private: + const enumerable* pparent_ = nullptr; // Parent enumerable or nullptr if unbound + mutable next_delegate next_; // Delegate used to fetch elements + mutable pointer pcur_ = nullptr; // Pointer to current element or nullptr when at end of sequence + mutable bool has_cur_ = true; // Whether pcur_ has been loaded for current element + size_t pos_ = 0; // Position in sequence, to compare iterators + + // Fetches value of pcur_, late-loading it if needed + pointer get_pcur() const { + if (!has_cur_) { + pcur_ = next_(); + has_cur_ = true; + } + return pcur_; + } + + public: + /** + * @brief Default constructor. + * + * Default constructor. Creates an invalid iterator that + * cannot be dereferenced. + */ + iterator() = default; + + /** + * @brief Constructor from @c enumerable. + * + * Constructor that is used by coveo::enumerable + * to create its iterators. + * + * @param parent Parent @c enumerable to iterate. + * @param is_end Whether this iterator should point to the + * beginning of the sequence (@c false) or to + * the end of the sequence (@c true). + */ + iterator(const enumerable& parent, bool is_end) + : pparent_(&parent), next_(!is_end ? parent.zero_ : next_delegate()), has_cur_(is_end) { } + + /** + * @brief Copy constructor. + * + * Copy constructor. Creates a copy of another iterator that + * will point to the same point in the enumeration. + * + * @param obj Iterator to copy. + */ + iterator(const iterator& obj) = default; + + /** + * @brief Move constructor. + * + * Move constructor. Creates an iterator by moving the internals + * from another iterator. After this method returns, @c obj is + * invalid and cannot be used. + * + * @param obj Iterator to move. + */ + iterator(iterator&& obj) + : pparent_(obj.pparent_), next_(std::move(obj.next_)), pcur_(obj.pcur_), + has_cur_(obj.has_cur_), pos_(obj.pos_) + { + obj.pparent_ = nullptr; + obj.pcur_ = nullptr; + obj.has_cur_ = true; + obj.pos_ = 0; + } + + /** + * @brief Assignment operator. + * + * Assignment operator. Copies another iterator, now pointing + * to the same point in the enumeration. + * + * @param obj Iterator to copy. + * @return Reference to @c this iterator. + */ + iterator& operator=(const iterator& obj) = default; + + /** + * @brief Move assignment operator. + * + * Move assignment operator. Moves the internals of another + * iterator to this one. After this method returns, @c obj + * is invalid and cannot be used. + * + * @param obj Iterator to move. + * @return Reference to @c this iterator. + */ + iterator& operator=(iterator&& obj) { + pparent_ = obj.pparent_; + next_ = std::move(obj.next_); + pcur_ = obj.pcur_; + has_cur_ = obj.has_cur_; + pos_ = obj.pos_; + + obj.pparent_ = nullptr; + obj.pcur_ = nullptr; + obj.has_cur_ = true; + obj.pos_ = 0; + + return *this; + } + + /** + * @brief Element access. + * + * Returns a reference to the current element in the enumeration, + * fetching it from the next delegate on the first call. + * + * @return Reference to current element. + */ + reference operator*() const { + return *get_pcur(); + } + + /** + * @brief Element pointer access. + * + * Returns a pointer to the current element in the enumeration, + * fetching it from the next delegate on the first call. Can be + * used to call methods or access members of the element. + * + * @return Pointer to current element. + */ + pointer operator->() const { + return get_pcur(); + } + + /** + * @brief Moves to next element (pre-increment). + * + * Moves forward to the next element in the enumeration. + * Cannot be used if this iterator points to the end of + * the enumeration. + * + * @return Reference to @c this iterator AFTER the move. + */ + iterator& operator++() { + if (has_cur_) { + pcur_ = nullptr; + has_cur_ = false; + } else { + next_(); + } + ++pos_; + return *this; + } + + /** + * @brief Moves to next element (post-increment). + * + * Moves forward to the next element in the enumeration. + * Cannot be used if this iterator points to the end of + * the enumeration. + * + * @return Iterator BEFORE the move. + */ + iterator operator++(int) { + iterator it(*this); + ++*this; + return it; + } + + /** + * @brief Compares iterator for equality. + * + * Determines if two iterators point to the same point in + * the same sequence. + * + * @param left First iterator to compare. + * @param right Second iterator to compare. + * @return @c true if @c left and @c right point to the same + * point in the same sequence. + */ + friend bool operator==(const iterator& left, const iterator& right) { + return left.pparent_ == right.pparent_ && + (left.get_pcur() == nullptr) == (right.get_pcur() == nullptr) && + (left.get_pcur() == nullptr || left.pos_ == right.pos_); + } + + /** + * @brief Compares iterator for inequality. + * + * Determines if two iterators point to a different point + * in perhaps different sequences. + * + * @param left First iterator to compare. + * @param right Second iterator to compare. + * @return @c true if @c left does not point to the same point + * in the sequence as @c right. + * @see operator==() + */ + friend bool operator!=(const iterator& left, const iterator& right) { + return !(left == right); + } + }; + +public: + // Helper static methods + + /** + * @brief Returns @c enumerable over empty sequence. + * + * Static method that returns a coveo::enumerable over an + * empty sequence. Equivalent to using the default constructor. + * + * @return @c enumerable over empty sequence. + */ + static enumerable empty() { + return enumerable(); + } + + /** + * @brief Returns @c enumerable over sequence of one element (stored internally). + * + * Static method that returns a coveo::enumerable over + * a sequence of one element. The @c enumerable will store the + * element internally, moving it if possible. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_one(). + * + * @param obj Object to store as the sequence's only element. + * @return @c enumerable over sequence of one element. + * @see coveo::enumerate_one() + */ + template + static enumerable for_one(U&& obj) { + auto spobj = std::make_shared(std::forward(obj)); + bool available = true; + return enumerable([spobj, available]() mutable { + pointer pobj = nullptr; + if (available) { + pobj = spobj.get(); + available = false; + } + return pobj; + }, []() -> std::size_t { + return 1; + }); + } + + /** + * @brief Returns @c enumerable over sequence of one element (stored externally). + * + * Static method that returns a coveo::enumerable over + * a sequence of one element. The @c enumerable will only store + * a reference to the element. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_one_ref(). + * + * @param obj Object to use as the sequence's only element. + * @return @c enumerable over sequence of one element. + * @see coveo::enumerate_one_ref() + */ + static enumerable for_one_ref(reference obj) { + bool available = true; + return enumerable([&obj, available]() mutable { + pointer pobj = nullptr; + if (available) { + pobj = std::addressof(obj); + available = false; + } + return pobj; + }, []() -> std::size_t { + return 1; + }); + } + + /** + * @brief Returns @c enumerable over sequence bound by iterators. + * + * Static method that returns a coveo::enumerable + * over a sequence bound by two iterators. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_range(). + * + * @param ibeg Iterator pointing at beginning of range. + * @param iend Iterator pointing at end of range. + * @return @c enumerable over sequence bound by [ibeg, iend[. + * @see coveo::enumerate_range() + */ + template + static enumerable for_range(It ibeg, It iend) { + auto it = std::move(ibeg); + return enumerable([it, iend]() mutable { + pointer pobj = nullptr; + if (it != iend) { + reference robj = *it; + pobj = std::addressof(robj); + ++it; + } + return pobj; + }, try_get_size_delegate_for_iterators(it, iend)); + } + + /** + * @brief Returns @c enumerable over container's sequence (stored externally). + * + * Static method that returns a coveo::enumerable over + * a sequence stored in a container. Only a reference to the + * container is kept by the @c enumerable. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_container(). + * + * @param cnt Container whose elements to wrap. + * @return @c enumerable over sequence of elements from @c cnt. + * @see coveo::enumerate_container() + */ + template + static enumerable for_container(C& cnt) { + auto it = std::begin(cnt); + auto end = std::end(cnt); + return enumerable([it, end]() mutable { + pointer pobj = nullptr; + if (it != end) { + reference robj = *it; + pobj = std::addressof(robj); + ++it; + } + return pobj; + }, try_get_size_delegate(cnt)); + } + + /** + * @brief Returns @c enumerable over container's sequence (stored internally). + * + * Static method that returns a coveo::enumerable over + * a sequence stored in a container. The container is moved to + * the @c enumerable and stored internally. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_container(). + * + * @param cnt Container to store in the enumerable. + * @return @c enumerable over sequence of elements from @c cnt. + * @see coveo::enumerate_container() + */ + template::value, void>::type> + static enumerable for_container(C&& cnt) { + auto spcnt = std::make_shared(std::move(cnt)); + auto it = std::begin(*spcnt); + auto end = std::end(*spcnt); + return enumerable([spcnt, it, end]() mutable { + pointer pobj = nullptr; + if (it != end) { + reference robj = *it; + pobj = std::addressof(robj); + ++it; + } + return pobj; + }, try_get_size_delegate(*spcnt)); + } + + /** + * @brief Returns @c enumerable over array's sequence. + * + * Static method that returns a coveo::enumerable over + * a sequence stored in a dynamic array. Equivalent to using + * for_range() without using pointer arithmetic. + * + * In order to auto-discover the enumerable's type, use + * coveo::enumerate_array(). + * + * @param parr Pointer to beginning of array. + * @param siz Size of array. + * @return @c enumerable over sequence in array. + * @see coveo::enumerate_array() + */ + static enumerable for_array(pointer parr, size_t siz) { + return for_range(parr, parr + siz); + } +}; + +// Helper functions corresponding to the helper static methods in enumerable. + +/** + * @brief Returns @c enumerable over sequence of one element (stored internally). + * + * Returns a coveo::enumerable over a sequence of one element. + * The @c enumerable will store the element internally, moving it if possible. + * + * The enumerable's type is deduced from the type of the element. + * + * @param obj Object to store as the sequence's only element. + * @return @c enumerable over sequence of one element. + * @see coveo::enumerable::for_one() + */ +template +auto enumerate_one(U&& obj) -> enumerable::const_value_type> { + return enumerable::const_value_type>::for_one(std::forward(obj)); +} + +/** + * @brief Returns @c enumerable over sequence of one element (stored externally). + * + * Returns a coveo::enumerable over a sequence of one element. + * The @c enumerable will only store a reference to the element. + * + * The enumerable's type is deduced from the type of the element. + * + * @param obj Object to use as the sequence's only element. + * @return @c enumerable over sequence of one element. + * @see coveo::enumerable::for_one_ref() + */ +template +auto enumerate_one_ref(T& obj) -> enumerable { + return enumerable::for_one_ref(obj); +} + +/** + * @brief Returns @c enumerable over sequence bound by iterators. + * + * Returns a coveo::enumerable over a sequence bound by two iterators. + * + * The enumerable's type is deduced from the type of element returned + * by the iterators. + * + * @param ibeg Iterator pointing at beginning of range. + * @param iend Iterator pointing at end of range. + * @return @c enumerable over sequence bound by [ibeg, iend[. + * @see coveo::enumerable::for_range() + */ +template +auto enumerate_range(It ibeg, It iend) + -> enumerable())>::value_type> +{ + return enumerable())>::value_type>::for_range( + std::move(ibeg), std::move(iend)); +} + +/** + * @brief Returns @c enumerable over container's sequence (stored externally). + * + * Returns a coveo::enumerable over a sequence stored + * in a container. Only a reference to the container is kept by + * the @c enumerable. + * + * The enumerable's type is deduced from the type of + * elements stored in the container. + * + * @param cnt Container whose elements to wrap. + * @return @c enumerable over sequence of elements from @c cnt. + * @see coveo::enumerable::for_container() + */ +template +auto enumerate_container(C& cnt) + -> enumerable()))>::value_type> +{ + return enumerable()))>::value_type>::for_container( + cnt); +} + +/** + * @brief Returns @c enumerable over container's sequence (stored internally). + * + * Returns a coveo::enumerable over a sequence stored + * in a container. The container is moved to the @c enumerable + * and stored internally. + * + * The enumerable's type is deduced from the type of + * elements stored in the container. + * + * @param cnt Container to store in the enumerable. + * @return @c enumerable over sequence of elements from @c cnt. + * @see coveo::enumerable::for_container() + */ +template::value, void>::type> +auto enumerate_container(C&& cnt) + -> enumerable()))>::const_value_type> +{ + return enumerable()))>::const_value_type>::for_container( + std::move(cnt)); +} + +/** + * @brief Returns @c enumerable over array's sequence. + * + * Returns a coveo::enumerable over a sequence stored in a + * dynamic array. Equivalent to using coveo::enumerate_range() + * without using pointer arithmetic. + * + * The enumerable's type is deduced from the type of elements + * stored in the array. + * + * @param parr Pointer to beginning of array. + * @param siz Size of array. + * @return @c enumerable over sequence in array. + * @see coveo::enumerable::for_array() + */ +template +auto enumerate_array(T* parr, size_t siz) -> enumerable { + return enumerable::for_array(parr, siz); +} + +} // coveo + +#endif // COVEO_ENUMERABLE_H diff --git a/3rdParty/coveo_linq/lib/coveo/seq/sequence_util.h b/3rdParty/coveo_linq/lib/coveo/seq/sequence_util.h new file mode 100644 index 0000000..02cc868 --- /dev/null +++ b/3rdParty/coveo_linq/lib/coveo/seq/sequence_util.h @@ -0,0 +1,302 @@ +/** + * @file + * @brief Utilities used by coveo::enumerable. + * + * @copyright 2016-2019, Coveo Solutions Inc. + * Distributed under the Apache License, Version 2.0 (see LICENSE). + */ + +#ifndef COVEO_SEQUENCE_UTIL_H +#define COVEO_SEQUENCE_UTIL_H + +#include + +#include +#include +#include +#include +#include + +namespace coveo { + +/// @cond + +// Forward declaration of enumerable, for type traits +template class enumerable; + +/// @endcond + +/** + * @brief Traits class for elements in a sequence. + * @headerfile sequence_util.h + * + * Traits class containing definitions pertaining to the elements of a sequence. + * For sequences of references or std::reference_wrappers, provides + * information about the referred type instead. + * + * @tparam T Type of elements in the sequence. + */ +template +struct seq_element_traits +{ + /** + * @brief Type of element in the sequence. + * + * Type of the sequence's elements. Corresponds to @c T. + */ + using value_type = T; + + /** + * @brief Type of element in the sequence, but @c const. + * + * Same as coveo::seq_element_traits::value_type, but @c const. + */ + using const_value_type = const value_type; + + /** + * @brief Raw type of element in the sequence. + * + * Same as coveo::seq_element_traits::value_type, but "raw", e.g. without @c const or @c volatile. + */ + using raw_value_type = typename std::remove_cv::type; + + /** + * @brief Pointer to a sequence element. + * + * Pointer to an element in the sequence. + * Corresponds to coveo::seq_element_traits::value_type*. + */ + using pointer = value_type*; + + /** + * @brief Reference to a sequence element. + * + * Reference to an element in the sequence. + * Corresponds to coveo::seq_element_traits::value_type&. + */ + using reference = value_type&; + + /** + * @brief Pointer to a @c const sequence element. + * + * Pointer to a @c const element in the sequence. + * Corresponds to coveo::seq_element_traits::const_value_type*. + */ + using const_pointer = const_value_type*; + + /** + * @brief Reference to a @c const sequence element. + * + * Reference to a @c const element in the sequence. + * Corresponds to coveo::seq_element_traits::const_value_type&. + */ + using const_reference = const_value_type&; +}; +/// @cond +template struct seq_element_traits : seq_element_traits { }; +template struct seq_element_traits : seq_element_traits { }; +template struct seq_element_traits> : seq_element_traits { }; +/// @endcond + +/** + * @brief Traits class for a sequence. + * @headerfile sequence_util.h + * + * Traits class containing definitions pertaining to a sequence. A shorthand + * for coveo::seq_element_traits that infers the sequence's @c value_type + * from the return value of its iterators. Also provides the type of iterator + * used by the sequence. + * + * @tparam Seq Type of sequence. + */ +template +struct seq_traits : public seq_element_traits()))> +{ + /** + * @brief Type of iterator used by the sequence. + * + * Type of iterator used by the sequence, which is defined as the type + * of iterator returned when calling std::begin(). + */ + using iterator_type = typename std::decay()))>::type; +}; +/// @cond +template struct seq_traits : seq_traits { }; +template struct seq_traits : seq_traits { }; +template struct seq_traits> : seq_traits { }; +/// @endcond + +#ifdef DOXYGEN_INVOKED +/** + * @brief Traits class to identify enumerable objects. + * @headerfile sequence_util.h + * + * Traits class that can be used to identify enumerable objects. + * + * @tparam T Type to identify. + */ +template struct is_enumerable; +#else +template struct is_enumerable : std::false_type { }; +template struct is_enumerable> : std::true_type { }; +#endif + +/** + * @brief Helper function to quickly reserve space in a container if possible. + * @headerfile sequence_util.h + * + * Attempts to @c reserve space in container @c cnt to hold as many + * elements as found in sequence @c seq. This is performed only if + * it's possible to do so "quickly". + * + * - If @c seq is a coveo::enumerable, space is reserved + * by using its coveo::enumerable::size() method if + * coveo::enumerable::has_fast_size() returns @c true. + * - If @c seq is a container with a size() method, space + * is reserved by using that method. + * - If @c seq is a sequence that uses random-access iterators, + * space is reserved by using std::distance(). + * - Otherwise, space is not reserved. + * + * @param cnt Container in which to reserve space. + * @param seq Sequence to use to try to reserve space for. + * @return @c true if space has actually been reserved in @c cnt. + */ +template +auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if::value, bool>::type +{ + const bool can_reserve = seq.has_fast_size(); + if (can_reserve) { + cnt.reserve(seq.size()); + } + return can_reserve; +} +template +auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if::value && + coveo::detail::has_size_const_method::value, bool>::type +{ + cnt.reserve(seq.size()); + return true; +} +template +auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if::value && + std::is_base_of::iterator_type>::iterator_category>::value, + bool>::type +{ + cnt.reserve(std::distance(std::begin(seq), std::end(seq))); + return true; +} +template +auto try_reserve(C&, const Seq&) -> typename std::enable_if::type>::value && + !std::is_base_of::iterator_type>::iterator_category>::value, + bool>::type +{ + // Can't reserve, no fast way of doing so + return false; +} + +/** + * @brief Helper function to get a fast size delegate if possible. + * @headerfile sequence_util.h + * + * Attempts to create a coveo::enumerable::size_delegate + * that can quickly calculate the number of elements found in + * sequence @c seq. A size delegate is returned only if it's + * possible to calculate the number of elements "quickly". + * + * - If @c seq is a coveo::enumerable, its + * coveo::enumerable::size() method is used to produce + * the size delegate if coveo::enumerable::has_fast_size() + * returns @c true. + * - If @c seq is a container with a size() method, a size + * delegate is produced using that method. + * - If @c seq is a sequence that uses random-access iterators, + * a size delegate is produced by using std::distance(). + * - Otherwise, no size delegate is produced. + * + * @param seq Sequence to calculate the number of elements of + * in order to produce the size delegate. + * @return coveo::enumerable::size_delegate instance, + * or @c nullptr if it's not possible to quickly calculate + * the number of elements in @c seq. + * @see coveo::enumerable::size_delegate + */ +template +auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if::value, + std::function>::type +{ + std::function siz; + if (seq.has_fast_size()) { + const std::size_t size = seq.size(); + siz = [size]() -> std::size_t { return size; }; + } + return siz; +} +template +auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if::value && + coveo::detail::has_size_const_method::value, + std::function>::type +{ + const std::size_t size = seq.size(); + return [size]() -> std::size_t { return size; }; +} +template +auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if::value && + std::is_base_of::iterator_type>::iterator_category>::value, + std::function>::type +{ + const std::size_t size = static_cast(std::distance(std::begin(seq), std::end(seq))); + return [size]() -> std::size_t { return size; }; +} +template +auto try_get_size_delegate(const Seq&) -> typename std::enable_if::value && + !std::is_base_of::iterator_type>::iterator_category>::value, + std::function>::type +{ + // No way to quickly determine size, don't try + return nullptr; +} + +/** + * @brief Helper function to get a fast size delegate from iterators if possible. + * @headerfile sequence_util.h + * + * Attempts to create a coveo::enumerable::size_delegate that can + * quickly calculate the number of elements in range [beg, end[. + * A size delegate is returned only if it's possible to calculate the number + * of elements "quickly". + * + * - If @c beg and @c end are random-access iterators, + * a size delegate is produced by using std::distance(). + * - Otherwise, no size delegate is produced. + * + * @param beg Iterator pointing at beginning of range. + * @param end Iterator pointing at end of range. + * @return coveo::enumerable::size_delegate instance, + * or @c nullptr if it's not possible to quickly calculate + * the number of elements in [beg, end[ + * @see coveo::enumerable::size_delegate + */ +template +auto try_get_size_delegate_for_iterators(const It& beg, const It& end) -> typename std::enable_if::iterator_category>::value, + std::function>::type +{ + return [beg, end]() -> std::size_t { return std::distance(beg, end); }; +} +template +auto try_get_size_delegate_for_iterators(const It&, const It&) -> typename std::enable_if::iterator_category>::value, + std::function>::type +{ + // No way to quickly determine size, don't try + return nullptr; +} + +} // coveo + +#endif // COVEO_SEQUENCE_UTIL_H diff --git a/Installer/Setup.iss b/Installer/Setup.iss index 465c18a..613ebb0 100644 --- a/Installer/Setup.iss +++ b/Installer/Setup.iss @@ -77,6 +77,7 @@ Source: ..\bin\Win32\Release\PathCopyCopyCOMPluginExecutor32.exe; DestDir: {app} Source: ..\bin\x64\Release\PathCopyCopyCOMPluginExecutor64.exe; DestDir: {app}; Flags: ignoreversion restartreplace overwritereadonly uninsrestartdelete uninsremovereadonly; Check: Is64BitInstallMode Source: ..\LICENSE; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly; DestName: LICENSE.TXT Source: ..\LICENSE.CommandLineArguments; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly; DestName: LICENSE.CommandLineArguments.TXT +Source: ..\LICENSE.coveo_linq; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly; DestName: LICENSE.coveo_linq.TXT Source: ..\LICENSE.microsoft_gsl; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly; DestName: LICENSE.microsoft_gsl.TXT Source: ..\HISTORY; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly; DestName: HISTORY.TXT Source: ..\Schemas\PipelinePluginCollection.xsd; DestDir: {app}\Schemas; Flags: overwritereadonly uninsremovereadonly diff --git a/LICENSE.coveo_linq b/LICENSE.coveo_linq new file mode 100644 index 0000000..52ca77d --- /dev/null +++ b/LICENSE.coveo_linq @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-2019 Coveo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/PathCopyCopy/PathCopyCopy.vcxproj b/PathCopyCopy/PathCopyCopy.vcxproj index dc74f1b..3db2cba 100644 --- a/PathCopyCopy/PathCopyCopy.vcxproj +++ b/PathCopyCopy/PathCopyCopy.vcxproj @@ -128,7 +128,7 @@ true - .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;%(AdditionalIncludeDirectories) + .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;$(SolutionDir)3rdParty\coveo_linq\lib;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;_DEBUG;_USRDLL;_MERGE_PROXYSTUB;%(PreprocessorDefinitions) MultiThreadedDebug Use @@ -172,7 +172,7 @@ PathCopyCopy_p.c - .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;%(AdditionalIncludeDirectories) + .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;$(SolutionDir)3rdParty\coveo_linq\lib;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;_DEBUG;_USRDLL;PCC_WIN64;_MERGE_PROXYSTUB;%(PreprocessorDefinitions) MultiThreadedDebug Use @@ -220,7 +220,7 @@ true - .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;%(AdditionalIncludeDirectories) + .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;$(SolutionDir)3rdParty\coveo_linq\lib;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;NDEBUG;_USRDLL;_MERGE_PROXYSTUB;%(PreprocessorDefinitions) MultiThreaded Use @@ -262,7 +262,7 @@ PathCopyCopy_p.c - .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;%(AdditionalIncludeDirectories) + .\prihdr;.\plugins\prihdr;.\actions\prihdr;.\generated;.\rsrc;$(SolutionDir)3rdParty\microsoft_gsl\include;$(SolutionDir)3rdParty\coveo_linq\lib;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;NDEBUG;_USRDLL;PCC_WIN64;_MERGE_PROXYSTUB;%(PreprocessorDefinitions) MultiThreaded Use diff --git a/PathCopyCopy/prihdr/stdafx.h b/PathCopyCopy/prihdr/stdafx.h index 5734d12..a482677 100644 --- a/PathCopyCopy/prihdr/stdafx.h +++ b/PathCopyCopy/prihdr/stdafx.h @@ -48,7 +48,7 @@ #include #pragma warning( pop ) -// Under the Windows min and max macros, since they conflict with STL +// Undef the Windows min and max macros, since they conflict with STL // We can't define NOMINMAX because GDI actually needs those macros :( #undef min #undef max @@ -75,4 +75,6 @@ // Including this header allows us to suppress C++ Core Guideline warnings more easily #include +#include + #include