QXmpp Version: 1.11.3
Loading...
Searching...
No Matches
Algorithms.h
1// SPDX-FileCopyrightText: 2024 Linus Jahn <lnj@kaidan.im>
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#ifndef ALGORITHMS_H
6#define ALGORITHMS_H
7
8#include <algorithm>
9#include <functional>
10#include <optional>
11#include <ranges>
12#include <variant>
13
14namespace QXmpp::Private {
15
16template<typename T>
17concept HasShrinkToFit = requires(const T &t) {
18 t.shrink_to_fit();
19};
20
21template<typename T>
22concept HasSqueeze = requires(const T &t) {
23 t.squeeze();
24};
25
26template<typename T>
27concept HasPushBack = requires(T t, T::value_type value) {
28 t.push_back(value);
29};
30
31template<typename T>
32concept HasInsert = requires(T t, T::value_type value) {
33 t.insert(value);
34};
35
36template<typename OutputVector, typename InputVector, typename Converter>
37auto transform(const InputVector &input, Converter convert)
38{
39 static_assert(
40 HasPushBack<OutputVector> || HasInsert<OutputVector>,
41 "OutputVector must support push_back() or insert()");
42
43 OutputVector output;
44 if constexpr (std::ranges::sized_range<InputVector>) {
45 output.reserve(input.size());
46 }
47 for (const auto &value : input) {
48 if constexpr (HasPushBack<OutputVector>) {
49 output.push_back(std::invoke(convert, value));
50 } else if constexpr (HasInsert<OutputVector>) {
51 output.insert(std::invoke(convert, value));
52 }
53 }
54 return output;
55}
56
57template<typename OutputVector, typename InputVector, typename Converter>
58auto transform(InputVector &&input, Converter convert)
59{
60 static_assert(
61 HasPushBack<OutputVector> || HasInsert<OutputVector>,
62 "OutputVector must support push_back() or insert()");
63
64 OutputVector output;
65 if constexpr (std::ranges::sized_range<InputVector>) {
66 output.reserve(input.size());
67 }
68 for (auto it = input.begin(); it != input.end(); ++it) {
69 if constexpr (HasPushBack<OutputVector>) {
70 output.push_back(std::invoke(convert, std::move(*it)));
71 } else if constexpr (HasInsert<OutputVector>) {
72 output.insert(std::invoke(convert, std::move(*it)));
73 }
74 }
75 return output;
76}
77
78template<typename OutputVector, typename InputVector, typename Converter>
79auto transformFilter(const InputVector &input, Converter convert)
80{
81 using OutputValue = typename OutputVector::value_type;
82 OutputVector output;
83 if constexpr (std::ranges::sized_range<InputVector>) {
84 output.reserve(input.size());
85 }
86 for (const auto &value : input) {
87 if (const std::optional<OutputValue> result = std::invoke(convert, value)) {
88 output.push_back(*result);
89 }
90 }
91 if constexpr (std::ranges::sized_range<InputVector>) {
92 if constexpr (HasShrinkToFit<OutputVector>) {
93 output.shrink_to_fit();
94 } else if constexpr (HasSqueeze<OutputVector>) {
95 output.squeeze();
96 }
97 }
98 return output;
99}
100
101// std::ranges::contains is C++23
102template<typename Container, typename... Args>
103auto contains(const Container &container, Args &&...args) -> bool
104{
105 return std::ranges::find(container, std::forward<Args>(args)...) != std::end(container);
106}
107
108template<typename Container, typename... Args>
109auto find(const Container &container, Args &&...args) -> std::optional<typename Container::value_type>
110{
111 auto it = std::ranges::find(container, std::forward<Args>(args)...);
112 if (it != std::end(container)) {
113 return *it;
114 }
115 return {};
116}
117
118template<typename Container, typename... Args>
119auto removeIf(Container &container, Args &&...args)
120{
121 auto removedRange = std::ranges::remove_if(container, std::forward<Args>(args)...);
122 container.erase(removedRange.begin(), removedRange.end());
123}
124
125template<typename T, typename Function>
126auto map(Function mapValue, std::optional<T> &&optValue) -> std::optional<std::invoke_result_t<Function, T &&>>
127{
128 if (optValue) {
129 return mapValue(std::move(*optValue));
130 }
131 return {};
132}
133
134template<typename T, typename Function>
135auto map(Function mapValue, const std::optional<T> &optValue) -> std::optional<std::invoke_result_t<Function, T &&>>
136{
137 if (optValue) {
138 return mapValue(*optValue);
139 }
140 return {};
141}
142
143template<typename Out, typename Function, typename... InTypes>
144auto map(Function mapValue, std::variant<InTypes...> &&variant)
145{
146 return std::visit(
147 [&](auto &&v) -> Out {
148 if constexpr (std::is_invocable_v<Function, decltype(v)>) {
149 return { mapValue(std::move(v)) };
150 } else {
151 return { v };
152 }
153 },
154 std::move(variant));
155}
156
157template<typename Out, typename Function, typename... InTypes>
158auto map(Function mapValue, const std::variant<InTypes...> &variant)
159{
160 return std::visit(
161 [&](const auto &v) -> Out {
162 if constexpr (std::is_invocable_v<Function, decltype(v)>) {
163 return { mapValue(v) };
164 } else {
165 return { v };
166 }
167 },
168 variant);
169}
170
171template<typename To, typename From>
172auto into(std::optional<From> &&value) -> std::optional<To>
173{
174 if (value) {
175 return To { *value };
176 }
177 return {};
178}
179
180template<typename To, typename From>
181auto into(const std::optional<From> &value) -> std::optional<To>
182{
183 if (value) {
184 return To { *value };
185 }
186 return {};
187}
188
189template<typename GreaterVariant, typename... BaseTypes>
190auto into(std::variant<BaseTypes...> &&variant)
191{
192 return std::visit([](auto &&value) -> GreaterVariant { return value; }, std::move(variant));
193}
194
195template<typename GreaterVariant, typename... BaseTypes>
196auto into(const std::variant<BaseTypes...> &variant)
197{
198 return std::visit([](const auto &value) -> GreaterVariant { return value; }, variant);
199}
200
201} // namespace QXmpp::Private
202
203#endif // ALGORITHMS_H