diff options
Diffstat (limited to 'src/shared/lsp/algorithm.h')
-rw-r--r-- | src/shared/lsp/algorithm.h | 1518 |
1 files changed, 1518 insertions, 0 deletions
diff --git a/src/shared/lsp/algorithm.h b/src/shared/lsp/algorithm.h new file mode 100644 index 000000000..4c41fb649 --- /dev/null +++ b/src/shared/lsp/algorithm.h @@ -0,0 +1,1518 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "predicates.h" + +#include <qcompilerdetection.h> // for Q_REQUIRED_RESULT + +#include <algorithm> +#include <map> +#include <memory> +#include <set> +#include <tuple> +#include <unordered_map> +#include <unordered_set> + +#include <QHash> +#include <QObject> +#include <QSet> +#include <QStringList> + +#include <memory> +#include <optional> +#include <type_traits> + +namespace lsp::Utils { + +///////////////////////// +// anyOf +///////////////////////// +template<typename T, typename F> +bool anyOf(const T &container, F predicate); +template<typename T, typename R, typename S> +bool anyOf(const T &container, R (S::*predicate)() const); +template<typename T, typename R, typename S> +bool anyOf(const T &container, R S::*member); + +///////////////////////// +// count +///////////////////////// +template<typename T, typename F> +int count(const T &container, F predicate); + +///////////////////////// +// allOf +///////////////////////// +template<typename T, typename F> +bool allOf(const T &container, F predicate); + +///////////////////////// +// erase +///////////////////////// +template<typename T, typename F> +void erase(T &container, F predicate); +template<typename T, typename F> +bool eraseOne(T &container, F predicate); + +///////////////////////// +// contains +///////////////////////// +template<typename T, typename F> +bool contains(const T &container, F function); +template<typename T, typename R, typename S> +bool contains(const T &container, R (S::*function)() const); +template<typename C, typename R, typename S> +bool contains(const C &container, R S::*member); + +///////////////////////// +// findOr +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT typename C::value_type findOr(const C &container, + typename C::value_type other, + F function); +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT typename T::value_type findOr(const T &container, + typename T::value_type other, + R (S::*function)() const); +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT typename T::value_type findOr(const T &container, + typename T::value_type other, + R S::*member); + +///////////////////////// +// findOrDefault +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, F function); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, R (S::*function)() const); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, R S::*member); + +///////////////////////// +// indexOf +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT int indexOf(const C &container, F function); + +///////////////////////// +// maxElementOr +///////////////////////// +template<typename T> +typename T::value_type maxElementOr(const T &container, typename T::value_type other); + +///////////////////////// +// filtered +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT C filtered(const C &container, F predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT C filtered(const C &container, R (S::*predicate)() const); + +///////////////////////// +// partition +///////////////////////// +// Recommended usage: +// C hit; +// C miss; +// std::tie(hit, miss) = Utils::partition(container, predicate); +template<typename C, typename F> +Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, F predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, R (S::*predicate)() const); + +///////////////////////// +// filteredUnique +///////////////////////// +template<typename C> +Q_REQUIRED_RESULT C filteredUnique(const C &container); + +///////////////////////// +// qobject_container_cast +///////////////////////// +template<class T, template<typename> class Container, typename Base> +Container<T> qobject_container_cast(const Container<Base> &container); + +///////////////////////// +// static_container_cast +///////////////////////// +template<class T, template<typename> class Container, typename Base> +Container<T> static_container_cast(const Container<Base> &container); + +///////////////////////// +// sort +///////////////////////// +template<typename Container> +inline void sort(Container &container); +template<typename Container, typename Predicate> +inline void sort(Container &container, Predicate p); +template<typename Container, typename R, typename S> +inline void sort(Container &container, R S::*member); +template<typename Container, typename R, typename S> +inline void sort(Container &container, R (S::*function)() const); + +///////////////////////// +// reverseForeach +///////////////////////// +template<typename Container, typename Op> +inline void reverseForeach(const Container &c, const Op &operation); + +///////////////////////// +// toReferences +///////////////////////// +template<template<typename...> class ResultContainer, typename SourceContainer> +auto toReferences(SourceContainer &sources); +template<typename SourceContainer> +auto toReferences(SourceContainer &sources); + +///////////////////////// +// toConstReferences +///////////////////////// +template<template<typename...> class ResultContainer, typename SourceContainer> +auto toConstReferences(const SourceContainer &sources); +template<typename SourceContainer> +auto toConstReferences(const SourceContainer &sources); + +///////////////////////// +// take +///////////////////////// +template<class C, typename P> +Q_REQUIRED_RESULT std::optional<typename C::value_type> take(C &container, P predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R S::*member); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R (S::*function)() const); + +///////////////////////// +// setUnionMerge +///////////////////////// +// Works like std::set_union but provides a merge function for items that match +// !(a > b) && !(b > a) which normally means that there is an "equal" match. +// It uses iterators to support move_iterators. +template<class InputIt1, class InputIt2, class OutputIt, class Merge, class Compare> +OutputIt setUnionMerge(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first, + Merge merge, + Compare comp); +template<class InputIt1, class InputIt2, class OutputIt, class Merge> +OutputIt setUnionMerge( + InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first, Merge merge); +template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge, class Compare> +OutputContainer setUnionMerge(InputContainer1 &&input1, + InputContainer2 &&input2, + Merge merge, + Compare comp); +template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge> +OutputContainer setUnionMerge(InputContainer1 &&input1, InputContainer2 &&input2, Merge merge); + +///////////////////////// +// setUnion +///////////////////////// +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp); +template<typename InputIterator1, typename InputIterator2, typename OutputIterator> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result); + +///////////////////////// +// transform +///////////////////////// +// function without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename F> // function type +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); + +// function with result type deduction: +template<template<typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value = typename std::decay_t<SC>::value_type, + typename Result = std::decay_t<std::result_of_t<F(Value &)>>, + typename ResultContainer = C<Result>> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); +#ifdef Q_CC_CLANG +// "Matching of template template-arguments excludes compatible templates" +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0522r0.html (P0522R0) +// in C++17 makes the above match e.g. C=std::vector even though that takes two +// template parameters. Unfortunately the following one matches too, and there is no additional +// partial ordering rule, resulting in an ambiguous call for this previously valid code. +// GCC and MSVC ignore that issue and follow the standard to the letter, but Clang only +// enables the new behavior when given -frelaxed-template-template-args . +// To avoid requiring everyone using this header to enable that feature, keep the old implementation +// for Clang. +template<template<typename, typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value = typename std::decay_t<SC>::value_type, + typename Result = std::decay_t<std::result_of_t<F(Value &)>>, + typename ResultContainer = C<Result, std::allocator<Result>>> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); +#endif + +// member function without result type deduction: +template<template<typename...> class C, // result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const); + +// member function with result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const); + +// member without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p); + +// member with result type deduction: +template<template<typename...> class C, // result container + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p); + +// same container types for input and output, const input +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, F function); + +// same container types for input and output, const input +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R (S::*p)() const); + +// same container types for input and output, const input +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R S::*p); + +// same container types for input and output, non-const input +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, F function); + +// same container types for input and output, non-const input +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R (S::*p)() const); + +// same container types for input and output, non-const input +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R S::*p); + +///////////////////////////////////////////////////////////////////////////// +//////// Implementations ////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +////////////////// +// anyOf +///////////////// +template<typename T, typename F> +bool anyOf(const T &container, F predicate) +{ + return std::any_of(std::begin(container), std::end(container), predicate); +} + +// anyOf taking a member function pointer +template<typename T, typename R, typename S> +bool anyOf(const T &container, R (S::*predicate)() const) +{ + return std::any_of(std::begin(container), std::end(container), std::mem_fn(predicate)); +} + +// anyOf taking a member pointer +template<typename T, typename R, typename S> +bool anyOf(const T &container, R S::*member) +{ + return std::any_of(std::begin(container), std::end(container), std::mem_fn(member)); +} + + +////////////////// +// count +///////////////// +template<typename T, typename F> +int count(const T &container, F predicate) +{ + return std::count_if(std::begin(container), std::end(container), predicate); +} + +////////////////// +// allOf +///////////////// +template<typename T, typename F> +bool allOf(const T &container, F predicate) +{ + return std::all_of(std::begin(container), std::end(container), predicate); +} + +// allOf taking a member function pointer +template<typename T, typename R, typename S> +bool allOf(const T &container, R (S::*predicate)() const) +{ + return std::all_of(std::begin(container), std::end(container), std::mem_fn(predicate)); +} + +// allOf taking a member pointer +template<typename T, typename R, typename S> +bool allOf(const T &container, R S::*member) +{ + return std::all_of(std::begin(container), std::end(container), std::mem_fn(member)); +} + +////////////////// +// erase +///////////////// +template<typename T, typename F> +void erase(T &container, F predicate) +{ + container.erase(std::remove_if(std::begin(container), std::end(container), predicate), + std::end(container)); +} +template<typename T, typename F> +bool eraseOne(T &container, F predicate) +{ + const auto it = std::find_if(std::begin(container), std::end(container), predicate); + if (it == std::end(container)) + return false; + container.erase(it); + return true; +} + +////////////////// +// contains +///////////////// +template<typename T, typename F> +bool contains(const T &container, F function) +{ + return anyOf(container, function); +} + +template<typename T, typename R, typename S> +bool contains(const T &container, R (S::*function)() const) +{ + return anyOf(container, function); +} + +template<typename C, typename R, typename S> +bool contains(const C &container, R S::*member) +{ + return anyOf(container, std::mem_fn(member)); +} + +////////////////// +// findOr +///////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT +typename C::value_type findOr(const C &container, typename C::value_type other, F function) +{ + typename C::const_iterator begin = std::begin(container); + typename C::const_iterator end = std::end(container); + + typename C::const_iterator it = std::find_if(begin, end, function); + return it == end ? other : *it; +} + +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT +typename T::value_type findOr(const T &container, typename T::value_type other, R (S::*function)() const) +{ + return findOr(container, other, std::mem_fn(function)); +} + +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT +typename T::value_type findOr(const T &container, typename T::value_type other, R S::*member) +{ + return findOr(container, other, std::mem_fn(member)); +} + +////////////////// +// findOrDefault +////////////////// +// Default implementation: +template<typename C, typename F> +Q_REQUIRED_RESULT +typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type> +findOrDefault(const C &container, F function) +{ + return findOr(container, typename C::value_type(), function); +} + +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT +typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type> +findOrDefault(const C &container, R (S::*function)() const) +{ + return findOr(container, typename C::value_type(), std::mem_fn(function)); +} + +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT +typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type> +findOrDefault(const C &container, R S::*member) +{ + return findOr(container, typename C::value_type(), std::mem_fn(member)); +} + +////////////////// +// index of: +////////////////// + +template<typename C, typename F> +Q_REQUIRED_RESULT +int indexOf(const C& container, F function) +{ + typename C::const_iterator begin = std::begin(container); + typename C::const_iterator end = std::end(container); + + typename C::const_iterator it = std::find_if(begin, end, function); + return it == end ? -1 : std::distance(begin, it); +} + + +////////////////// +// max element +////////////////// + +template<typename T> +typename T::value_type maxElementOr(const T &container, typename T::value_type other) +{ + typename T::const_iterator begin = std::begin(container); + typename T::const_iterator end = std::end(container); + + typename T::const_iterator it = std::max_element(begin, end); + if (it == end) + return other; + return *it; +} + + +////////////////// +// transform +///////////////// + +namespace { +///////////////// +// helper code for transform to use back_inserter and thus push_back for everything +// and insert for QSet<> +// + +// SetInsertIterator, straight from the standard for insert_iterator +// just without the additional parameter to insert +template<class Container> +class SetInsertIterator +{ +protected: + Container *container; + +public: + using iterator_category = std::output_iterator_tag; + using container_type = Container; + explicit SetInsertIterator(Container &x) + : container(&x) + {} + SetInsertIterator<Container> &operator=(const typename Container::value_type &value) + { + container->insert(value); + return *this; + } + SetInsertIterator<Container> &operator=(typename Container::value_type &&value) + { + container->insert(std::move(value)); + return *this; + } + SetInsertIterator<Container> &operator*() { return *this; } + SetInsertIterator<Container> &operator++() { return *this; } + SetInsertIterator<Container> operator++(int) { return *this; } +}; + +// for QMap / QHash, inserting a std::pair / QPair +template<class Container> +class MapInsertIterator +{ +protected: + Container *container; + +public: + using iterator_category = std::output_iterator_tag; + using container_type = Container; + explicit MapInsertIterator(Container &x) + : container(&x) + {} + MapInsertIterator<Container> &operator=( + const std::pair<const typename Container::key_type, typename Container::mapped_type> &value) + { container->insert(value.first, value.second); return *this; } + MapInsertIterator<Container> &operator=( + const QPair<typename Container::key_type, typename Container::mapped_type> &value) + { container->insert(value.first, value.second); return *this; } + MapInsertIterator<Container> &operator*() { return *this; } + MapInsertIterator<Container> &operator++() { return *this; } + MapInsertIterator<Container> operator++(int) { return *this; } +}; + +// because Qt container are not implementing the standard interface we need +// this helper functions for generic code +template<typename Type> +void append(QList<Type> *container, QList<Type> &&input) +{ + container->append(std::move(input)); +} + +template<typename Type> +void append(QList<Type> *container, const QList<Type> &input) +{ + container->append(input); +} + +template<typename Container> +void append(Container *container, Container &&input) +{ + container->insert(container->end(), + std::make_move_iterator(input.begin()), + std::make_move_iterator(input.end())); +} + +template<typename Container> +void append(Container *container, const Container &input) +{ + container->insert(container->end(), input.begin(), input.end()); +} + +// BackInsertIterator behaves like std::back_insert_iterator except is adds the back insertion for +// container of the same type +template<typename Container> +class BackInsertIterator +{ +public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + + explicit constexpr BackInsertIterator(Container &container) + : m_container(std::addressof(container)) + {} + + constexpr BackInsertIterator &operator=(const typename Container::value_type &value) + { + m_container->push_back(value); + return *this; + } + + constexpr BackInsertIterator &operator=(typename Container::value_type &&value) + { + m_container->push_back(std::move(value)); + return *this; + } + + constexpr BackInsertIterator &operator=(const Container &container) + { + append(m_container, container); + return *this; + } + + constexpr BackInsertIterator &operator=(Container &&container) + { + append(m_container, container); + return *this; + } + + [[nodiscard]] constexpr BackInsertIterator &operator*() { return *this; } + + constexpr BackInsertIterator &operator++() { return *this; } + + constexpr BackInsertIterator operator++(int) { return *this; } + +private: + Container *m_container; +}; + +// inserter helper function, returns a BackInsertIterator for most containers +// and is overloaded for QSet<> and other containers without push_back, returning custom inserters +template<typename Container> +inline BackInsertIterator<Container> inserter(Container &container) +{ + return BackInsertIterator(container); +} + +template<typename X> +inline SetInsertIterator<QSet<X>> +inserter(QSet<X> &container) +{ + return SetInsertIterator<QSet<X>>(container); +} + +template<typename K, typename C, typename A> +inline SetInsertIterator<std::set<K, C, A>> +inserter(std::set<K, C, A> &container) +{ + return SetInsertIterator<std::set<K, C, A>>(container); +} + +template<typename K, typename H, typename C, typename A> +inline SetInsertIterator<std::unordered_set<K, H, C, A>> +inserter(std::unordered_set<K, H, C, A> &container) +{ + return SetInsertIterator<std::unordered_set<K, H, C, A>>(container); +} + +template<typename K, typename V, typename C, typename A> +inline SetInsertIterator<std::map<K, V, C, A>> +inserter(std::map<K, V, C, A> &container) +{ + return SetInsertIterator<std::map<K, V, C, A>>(container); +} + +template<typename K, typename V, typename H, typename C, typename A> +inline SetInsertIterator<std::unordered_map<K, V, H, C, A>> +inserter(std::unordered_map<K, V, H, C, A> &container) +{ + return SetInsertIterator<std::unordered_map<K, V, H, C, A>>(container); +} + +template<typename K, typename V> +inline MapInsertIterator<QMap<K, V>> +inserter(QMap<K, V> &container) +{ + return MapInsertIterator<QMap<K, V>>(container); +} + +template<typename K, typename V> +inline MapInsertIterator<QHash<K, V>> +inserter(QHash<K, V> &container) +{ + return MapInsertIterator<QHash<K, V>>(container); +} + +// Helper code for container.reserve that makes it possible to effectively disable it for +// specific cases + +// default: do reserve +// Template arguments are more specific than the second version below, so this is tried first +template<template<typename...> class C, typename... CArgs, + typename = decltype(&C<CArgs...>::reserve)> +void reserve(C<CArgs...> &c, typename C<CArgs...>::size_type s) +{ + c.reserve(s); +} + +// containers that don't have reserve() +template<typename C> +void reserve(C &, typename C::size_type) { } + +} // anonymous + +// -------------------------------------------------------------------- +// Different containers for input and output: +// -------------------------------------------------------------------- + +// different container types for input and output, e.g. transforming a QList into a QSet + +// function without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename F> // function type +Q_REQUIRED_RESULT +decltype(auto) transform(SC &&container, F function) +{ + ResultContainer result; + reserve(result, typename ResultContainer::size_type(container.size())); + std::transform(std::begin(container), std::end(container), inserter(result), function); + return result; +} + +// function with result type deduction: +template<template<typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value, + typename Result, + typename ResultContainer> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function) +{ + return transform<ResultContainer>(std::forward<SC>(container), function); +} + +#ifdef Q_CC_CLANG +template<template<typename, typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value, + typename Result, + typename ResultContainer> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function) +{ + return transform<ResultContainer>(std::forward<SC>(container), function); +} +#endif + +// member function without result type deduction: +template<template<typename...> class C, // result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(SC &&container, R (S::*p)() const) +{ + return transform<C>(std::forward<SC>(container), std::mem_fn(p)); +} + +// member function with result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(SC &&container, R (S::*p)() const) +{ + return transform<ResultContainer>(std::forward<SC>(container), std::mem_fn(p)); +} + +// member without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(SC &&container, R S::*p) +{ + return transform<ResultContainer>(std::forward<SC>(container), std::mem_fn(p)); +} + +// member with result type deduction: +template<template<typename...> class C, // result container + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(SC &&container, R S::*p) +{ + return transform<C>(std::forward<SC>(container), std::mem_fn(p)); +} + +// same container types for input and output, const input + +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(const C<CArgs...> &container, F function) +{ + return transform<C, const C<CArgs...> &>(container, function); +} + +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(const C<CArgs...> &container, R (S::*p)() const) +{ + return transform<C, const C<CArgs...> &>(container, std::mem_fn(p)); +} + +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(const C<CArgs...> &container, R S::*p) +{ + return transform<C, const C<CArgs...> &>(container, std::mem_fn(p)); +} + +// same container types for input and output, non-const input + +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(C<CArgs...> &container, F function) +{ + return transform<C, C<CArgs...> &>(container, function); +} + +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(C<CArgs...> &container, R (S::*p)() const) +{ + return transform<C, C<CArgs...> &>(container, std::mem_fn(p)); +} + +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT +decltype(auto) transform(C<CArgs...> &container, R S::*p) +{ + return transform<C, C<CArgs...> &>(container, std::mem_fn(p)); +} + +// Specialization for QStringList: + +template<template<typename...> class C = QList, // result container + typename F> // Arguments to C +Q_REQUIRED_RESULT +decltype(auto) transform(const QStringList &container, F function) +{ + return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), function); +} + +// member function: +template<template<typename...> class C = QList, // result container type + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(const QStringList &container, R (S::*p)() const) +{ + return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), std::mem_fn(p)); +} + +// members: +template<template<typename...> class C = QList, // result container + typename R, + typename S> +Q_REQUIRED_RESULT +decltype(auto) transform(const QStringList &container, R S::*p) +{ + return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), std::mem_fn(p)); +} + +////////////////// +// filtered +///////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT +C filtered(const C &container, F predicate) +{ + C out; + std::copy_if(std::begin(container), std::end(container), + inserter(out), predicate); + return out; +} + +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT +C filtered(const C &container, R (S::*predicate)() const) +{ + C out; + std::copy_if(std::begin(container), std::end(container), + inserter(out), std::mem_fn(predicate)); + return out; +} + +////////////////// +// filteredCast +///////////////// +template<typename R, typename C, typename F> +Q_REQUIRED_RESULT R filteredCast(const C &container, F predicate) +{ + R out; + std::copy_if(std::begin(container), std::end(container), inserter(out), predicate); + return out; +} + +////////////////// +// partition +///////////////// + +// Recommended usage: +// C hit; +// C miss; +// std::tie(hit, miss) = Utils::partition(container, predicate); + +template<typename C, typename F> +Q_REQUIRED_RESULT +std::tuple<C, C> partition(const C &container, F predicate) +{ + C hit; + C miss; + reserve(hit, container.size()); + reserve(miss, container.size()); + auto hitIns = inserter(hit); + auto missIns = inserter(miss); + for (const auto &i : container) { + if (predicate(i)) + hitIns = i; + else + missIns = i; + } + return std::make_tuple(hit, miss); +} + +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT +std::tuple<C, C> partition(const C &container, R (S::*predicate)() const) +{ + return partition(container, std::mem_fn(predicate)); +} + +////////////////// +// filteredUnique +///////////////// + +template<typename C> +Q_REQUIRED_RESULT +C filteredUnique(const C &container) +{ + C result; + auto ins = inserter(result); + + QSet<typename C::value_type> seen; + int setSize = 0; + + auto endIt = std::end(container); + for (auto it = std::begin(container); it != endIt; ++it) { + seen.insert(*it); + if (setSize == seen.size()) // unchanged size => was already seen + continue; + ++setSize; + ins = *it; + } + return result; +} + +////////////////// +// qobject_container_cast +///////////////// +template <class T, template<typename> class Container, typename Base> +Container<T> qobject_container_cast(const Container<Base> &container) +{ + Container<T> result; + auto ins = inserter(result); + for (Base val : container) { + if (T target = qobject_cast<T>(val)) + ins = target; + } + return result; +} + +////////////////// +// static_container_cast +///////////////// +template <class T, template<typename> class Container, typename Base> +Container<T> static_container_cast(const Container<Base> &container) +{ + Container<T> result; + reserve(result, container.size()); + auto ins = inserter(result); + for (Base val : container) + ins = static_cast<T>(val); + return result; +} + +////////////////// +// sort +///////////////// +template <typename Container> +inline void sort(Container &container) +{ + std::stable_sort(std::begin(container), std::end(container)); +} + +template <typename Container, typename Predicate> +inline void sort(Container &container, Predicate p) +{ + std::stable_sort(std::begin(container), std::end(container), p); +} + +// const lvalue +template<typename Container> +inline Container sorted(const Container &container) +{ + Container c = container; + sort(c); + return c; +} + +// non-const lvalue +// This is needed because otherwise the "universal" reference below is used, modifying the input +// container. +template<typename Container> +inline Container sorted(Container &container) +{ + Container c = container; + sort(c); + return c; +} + +// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above) +template<typename Container> +inline Container sorted(Container &&container) +{ + sort(container); + return std::move(container); +} + +// const rvalue +template<typename Container> +inline Container sorted(const Container &&container) +{ + return sorted(container); +} + +// const lvalue +template<typename Container, typename Predicate> +inline Container sorted(const Container &container, Predicate p) +{ + Container c = container; + sort(c, p); + return c; +} + +// non-const lvalue +// This is needed because otherwise the "universal" reference below is used, modifying the input +// container. +template<typename Container, typename Predicate> +inline Container sorted(Container &container, Predicate p) +{ + Container c = container; + sort(c, p); + return c; +} + +// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above) +template<typename Container, typename Predicate> +inline Container sorted(Container &&container, Predicate p) +{ + sort(container, p); + return std::move(container); +} + +// const rvalue +template<typename Container, typename Predicate> +inline Container sorted(const Container &&container, Predicate p) +{ + return sorted(container, p); +} + +// pointer to member +template<typename Container, typename R, typename S> +inline void sort(Container &container, R S::*member) +{ + auto f = std::mem_fn(member); + using const_ref = typename Container::const_reference; + std::stable_sort(std::begin(container), std::end(container), + [&f](const_ref a, const_ref b) { + return f(a) < f(b); + }); +} + +// const lvalue +template<typename Container, typename R, typename S> +inline Container sorted(const Container &container, R S::*member) +{ + Container c = container; + sort(c, member); + return c; +} + +// non-const lvalue +// This is needed because otherwise the "universal" reference below is used, modifying the input +// container. +template<typename Container, typename R, typename S> +inline Container sorted(Container &container, R S::*member) +{ + Container c = container; + sort(c, member); + return c; +} + +// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above) +template<typename Container, typename R, typename S> +inline Container sorted(Container &&container, R S::*member) +{ + sort(container, member); + return std::move(container); +} + +// const rvalue +template<typename Container, typename R, typename S> +inline Container sorted(const Container &&container, R S::*member) +{ + return sorted(container, member); +} + +// pointer to member function +template<typename Container, typename R, typename S> +inline void sort(Container &container, R (S::*function)() const) +{ + auto f = std::mem_fn(function); + using const_ref = typename Container::const_reference; + std::stable_sort(std::begin(container), std::end(container), + [&f](const_ref a, const_ref b) { + return f(a) < f(b); + }); +} + +// const lvalue +template<typename Container, typename R, typename S> +inline Container sorted(const Container &container, R (S::*function)() const) +{ + Container c = container; + sort(c, function); + return c; +} + +// non-const lvalue +// This is needed because otherwise the "universal" reference below is used, modifying the input +// container. +template<typename Container, typename R, typename S> +inline Container sorted(Container &container, R (S::*function)() const) +{ + Container c = container; + sort(c, function); + return c; +} + +// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above) +template<typename Container, typename R, typename S> +inline Container sorted(Container &&container, R (S::*function)() const) +{ + sort(container, function); + return std::move(container); +} + +// const rvalue +template<typename Container, typename R, typename S> +inline Container sorted(const Container &&container, R (S::*function)() const) +{ + return sorted(container, function); +} + +////////////////// +// reverseForeach +///////////////// +template <typename Container, typename Op> +inline void reverseForeach(const Container &c, const Op &operation) +{ + auto rend = c.rend(); + for (auto it = c.rbegin(); it != rend; ++it) + operation(*it); +} + +////////////////// +// toReferences +///////////////// +template <template<typename...> class ResultContainer, + typename SourceContainer> +auto toReferences(SourceContainer &sources) +{ + return transform<ResultContainer>(sources, [] (auto &value) { return std::ref(value); }); +} + +template <typename SourceContainer> +auto toReferences(SourceContainer &sources) +{ + return transform(sources, [] (auto &value) { return std::ref(value); }); +} + +////////////////// +// toConstReferences +///////////////// +template <template<typename...> class ResultContainer, + typename SourceContainer> +auto toConstReferences(const SourceContainer &sources) +{ + return transform<ResultContainer>(sources, [] (const auto &value) { return std::cref(value); }); +} + +template <typename SourceContainer> +auto toConstReferences(const SourceContainer &sources) +{ + return transform(sources, [] (const auto &value) { return std::cref(value); }); +} + +////////////////// +// take: +///////////////// + +template<class C, typename P> +Q_REQUIRED_RESULT std::optional<typename C::value_type> take(C &container, P predicate) +{ + const auto end = std::end(container); + + const auto it = std::find_if(std::begin(container), end, predicate); + if (it == end) + return std::nullopt; + + std::optional<typename C::value_type> result = std::make_optional(std::move(*it)); + container.erase(it); + return result; +} + +// pointer to member +template <typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R S::*member) +{ + return take(container, std::mem_fn(member)); +} + +// pointer to member function +template <typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R (S::*function)() const) +{ + return take(container, std::mem_fn(function)); +} + +////////////////// +// setUnionMerge: Works like std::set_union but provides a merge function for items that match +// !(a > b) && !(b > a) which normally means that there is an "equal" match. +// It uses iterators to support move_iterators. +///////////////// + +template<class InputIt1, + class InputIt2, + class OutputIt, + class Merge, + class Compare> +OutputIt setUnionMerge(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first, + Merge merge, + Compare comp) +{ + for (; first1 != last1; ++d_first) { + if (first2 == last2) + return std::copy(first1, last1, d_first); + if (comp(*first2, *first1)) { + *d_first = *first2++; + } else { + if (comp(*first1, *first2)) { + *d_first = *first1; + } else { + *d_first = merge(*first1, *first2); + ++first2; + } + ++first1; + } + } + return std::copy(first2, last2, d_first); +} + +template<class InputIt1, + class InputIt2, + class OutputIt, + class Merge> +OutputIt setUnionMerge(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first, + Merge merge) +{ + return setUnionMerge(first1, + last1, + first2, + last2, + d_first, + merge, + std::less<std::decay_t<decltype(*first1)>>{}); +} + +template<class OutputContainer, + class InputContainer1, + class InputContainer2, + class Merge, + class Compare> +OutputContainer setUnionMerge(InputContainer1 &&input1, + InputContainer2 &&input2, + Merge merge, + Compare comp) +{ + OutputContainer results; + results.reserve(input1.size() + input2.size()); + + setUnionMerge(std::make_move_iterator(std::begin(input1)), + std::make_move_iterator(std::end(input1)), + std::make_move_iterator(std::begin(input2)), + std::make_move_iterator(std::end(input2)), + std::back_inserter(results), + merge, + comp); + + return results; +} + +template<class OutputContainer, + class InputContainer1, + class InputContainer2, + class Merge> +OutputContainer setUnionMerge(InputContainer1 &&input1, + InputContainer2 &&input2, + Merge merge) +{ + return setUnionMerge<OutputContainer>(std::forward<InputContainer1>(input1), + std::forward<InputContainer2>(input2), + merge, + std::less<std::decay_t<decltype(*std::begin(input1))>>{}); +} + +template<typename Container> +auto usize(const Container &container) +{ + return static_cast<std::make_unsigned_t<decltype(std::size(container))>>(std::size(container)); +} + +template<typename Container> +auto ssize(const Container &container) +{ + return static_cast<std::make_signed_t<decltype(std::size(container))>>(std::size(container)); +} + +template<typename Compare> +struct CompareIter +{ + Compare compare; + + explicit constexpr CompareIter(Compare compare) + : compare(std::move(compare)) + {} + + template<typename Iterator1, typename Iterator2> + constexpr bool operator()(Iterator1 it1, Iterator2 it2) + { + return bool(compare(*it1, *it2)); + } +}; + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union_impl(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp) +{ + auto compare = CompareIter<Compare>(comp); + + while (first1 != last1 && first2 != last2) { + if (compare(first1, first2)) { + *result = *first1; + ++first1; + } else if (compare(first2, first1)) { + *result = *first2; + ++first2; + } else { + *result = *first1; + ++first1; + ++first2; + } + ++result; + } + + return std::copy(first2, last2, std::copy(first1, last1, result)); +} + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp) +{ + return set_union_impl(first1, last1, first2, last2, result, comp); +} + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result) +{ + return set_union_impl( + first1, last1, first2, last2, result, std::less<typename InputIterator1::value_type>{}); +} + +// Replacement for deprecated Qt functionality + +template <class T> +QSet<T> toSet(const QList<T> &list) +{ + return QSet<T>(list.begin(), list.end()); +} + +template<class T> +QList<T> toList(const QSet<T> &set) +{ + return QList<T>(set.begin(), set.end()); +} + +template <class Key, class T> +void addToHash(QHash<Key, T> *result, const QHash<Key, T> &additionalContents) +{ + result->insert(additionalContents); +} + +// Workaround for missing information from QSet::insert() +// Return type could be a pair like for std::set, but we never use the iterator anyway. +template<typename T, typename U> [[nodiscard]] bool insert(QSet<T> &s, const U &v) +{ + const int oldSize = s.size(); + s.insert(v); + return s.size() > oldSize; +} + +} // namespace lsp::Utils |