QXmpp  Version: 1.15.1
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 
14 namespace QXmpp::Private {
15 
16 template<typename T>
17 concept HasShrinkToFit = requires(const T &t) {
18  t.shrink_to_fit();
19 };
20 
21 template<typename T>
22 concept HasSqueeze = requires(const T &t) {
23  t.squeeze();
24 };
25 
26 template<typename T>
27 concept HasPushBack = requires(T t, T::value_type value) {
28  t.push_back(value);
29 };
30 
31 template<typename T>
32 concept HasInsert = requires(T t, T::value_type value) {
33  t.insert(value);
34 };
35 
36 template<typename OutputVector, typename InputVector, typename Converter>
37 auto 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 
57 template<typename OutputVector, typename InputVector, typename Converter>
58 auto 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 
78 template<typename OutputVector, typename InputVector, typename Converter>
79 auto 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
102 template<typename Container, typename... Args>
103 auto contains(const Container &container, Args &&...args) -> bool
104 {
105  return std::ranges::find(container, std::forward<Args>(args)...) != std::end(container);
106 }
107 
108 template<typename Container, typename... Args>
109 auto 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 
118 template<typename Container, typename... Args>
119 auto 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 
125 template<typename T, typename Function>
126 auto 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 
134 template<typename T, typename Function>
135 auto 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 
143 template<typename Out, typename Function, typename... InTypes>
144 auto 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 
157 template<typename Out, typename Function, typename... InTypes>
158 auto 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 
171 template<typename To, typename From>
172 auto into(std::optional<From> &&value) -> std::optional<To>
173 {
174  if (value) {
175  return To { *value };
176  }
177  return {};
178 }
179 
180 template<typename To, typename From>
181 auto into(const std::optional<From> &value) -> std::optional<To>
182 {
183  if (value) {
184  return To { *value };
185  }
186  return {};
187 }
188 
189 template<typename GreaterVariant, typename... BaseTypes>
190 auto into(std::variant<BaseTypes...> &&variant)
191 {
192  return std::visit([](auto &&value) -> GreaterVariant { return value; }, std::move(variant));
193 }
194 
195 template<typename GreaterVariant, typename... BaseTypes>
196 auto 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
Definition: Algorithms.h:14