ReactivePlusPlus
ReactiveX implementation for C++20
Loading...
Searching...
No Matches
utils.hpp
1// ReactivePlusPlus library
2//
3// Copyright Aleksey Loginov 2023 - present.
4// Distributed under the Boost Software License, Version 1.0.
5// (See accompanying file LICENSE_1_0.txt or copy at
6// https://www.boost.org/LICENSE_1_0.txt)
7//
8// Project home: https://github.com/victimsnino/ReactivePlusPlus
9//
10
11#pragma once
12
13#include <rpp/defs.hpp>
14#include <rpp/utils/constraints.hpp>
15#include <rpp/utils/tuple.hpp>
16
17#include <algorithm>
18#include <mutex>
19#include <variant>
20
21namespace rpp::utils
22{
23
24 struct none
25 {
26 };
27
28 template<typename... Args>
29 struct types
30 {
31 };
32
33 template<constraint::iterable T>
34 using iterable_value_t = std::iter_value_t<decltype(std::begin(std::declval<T>()))>;
35
36 namespace details
37 {
38 template<template<typename...> typename Base>
39 struct traits
40 {
41 template<typename... Types>
42 constexpr static rpp::utils::tuple<Types...> extract_params(const Base<Types...>*);
43 constexpr static std::false_type extract_params(...);
44 };
45 } // namespace details
46
47 template<typename T, template<typename...> typename Base>
48 concept is_base_of_v = !std::is_same_v<decltype(details::traits<Base>::extract_params(std::declval<std::decay_t<T>*>())), std::false_type>;
49
50 template<typename T, template<typename...> typename Base>
52 using extract_base_type_params_t = decltype(details::traits<Base>::extract_params(std::declval<std::decay_t<T>*>()));
53
54 template<class T>
55 constexpr std::add_const_t<T>& as_const(const T& v) noexcept
56 {
57 return v;
58 }
59
60 template<class T>
61 constexpr T&& as_const(T&& v) noexcept
62 requires std::is_rvalue_reference_v<T&&>
63 {
64 return std::forward<T>(v);
65 }
66
68 {
69 convertible_to_any() = default;
70
71 template<typename T>
72 operator T&() const;
73
74 template<typename T>
75 operator const T &() const;
76
77 template<typename T>
78 operator T&&() const;
79 };
80
81 template<typename Cont, std::invocable<iterable_value_t<Cont>> Fn>
82 void for_each(Cont&& container, Fn&& fn)
83 {
84 std::for_each(std::begin(container), std::end(container), std::forward<Fn>(fn));
85 }
86
87 template<typename Cont, std::predicate<iterable_value_t<Cont>> Fn>
88 bool all_of(const Cont& container, const Fn& fn)
89 {
90 return std::all_of(std::cbegin(container), std::cend(container), fn);
91 }
92
93 template<auto Fn, bool Inverse = false>
94 requires std::is_member_function_pointer_v<decltype(Fn)>
96 {
97 template<typename TT>
98 requires (Inverse == false && std::invocable<decltype(Fn), TT &&>)
99 auto operator()(TT&& d) const
100 {
101 return (std::forward<TT>(d).*Fn)();
102 }
103
104 template<typename TT>
105 requires (Inverse == true && std::invocable<decltype(Fn), TT &&>)
106 auto operator()(TT&& d) const
107 {
108 return !(std::forward<TT>(d).*Fn)();
109 }
110 };
111
112 template<auto Fn>
114
118 template<std::invocable Fn>
120 {
121 public:
122 explicit finally_action(Fn&& fn)
123 : m_fn{std::move(fn)}
124 {
125 }
126
127 explicit finally_action(const Fn& fn)
128 : m_fn{fn}
129 {
130 }
131
132 finally_action(const finally_action&) = delete;
133 finally_action(finally_action&&) noexcept = delete;
134
135 ~finally_action() noexcept { m_fn(); }
136
137 private:
138 RPP_NO_UNIQUE_ADDRESS Fn m_fn;
139 };
140
141 template<rpp::constraint::decayed_type T>
143 {
144 public:
145 repeated_container(T&& value, size_t count)
146 : m_value{std::move(value)}
147 , m_count{count}
148 {
149 }
150
151 repeated_container(const T& value, size_t count)
152 : m_value{value}
153 , m_count{count}
154 {
155 }
156
158 {
159 public:
160 iterator(const repeated_container* container, size_t index)
161 : m_container{container}
162 , m_index{index}
163 {
164 }
165
166 using iterator_category = std::input_iterator_tag;
167 using difference_type = std::ptrdiff_t;
168 using value_type = T;
169 using pointer = T*;
170
171 const value_type& operator*() const { return m_container->m_value; }
172
173 iterator& operator++()
174 {
175 ++m_index;
176 return *this;
177 }
178
179 iterator operator++(int)
180 {
181 auto old = *this;
182 ++(*this);
183 return old;
184 }
185
186 bool operator==(const iterator&) const = default;
187 bool operator!=(const iterator&) const = default;
188
189 private:
190 const repeated_container* m_container;
191 size_t m_index;
192 };
193
194 iterator begin() const { return {this, 0}; }
195
196 iterator end() const { return {this, m_count}; }
197
198 private:
199 RPP_NO_UNIQUE_ADDRESS T m_value;
200 size_t m_count;
201 };
202
203 template<rpp::constraint::decayed_type T>
205 {
206 public:
208 : m_value{std::move(value)}
209 {
210 }
211
212 infinite_repeated_container(const T& value)
213 : m_value{value}
214 {
215 }
216
218 {
219 public:
220 iterator(const infinite_repeated_container* container)
221 : m_container{container}
222 {
223 }
224
225 using iterator_category = std::input_iterator_tag;
226 using difference_type = std::ptrdiff_t;
227 using value_type = T;
228 using pointer = T*;
229
230 const value_type& operator*() const { return m_container->m_value; }
231
232 iterator& operator++() { return *this; }
233
234 iterator operator++(int) { return *this; }
235
236 bool operator==(const iterator&) const = default;
237 bool operator!=(const iterator&) const = default;
238
239 private:
240 const infinite_repeated_container* m_container;
241 };
242
243 iterator begin() const { return {this}; }
244
245 iterator end() const { return {nullptr}; }
246
247 private:
248 RPP_NO_UNIQUE_ADDRESS T m_value;
249 };
250
252 {
253 static constexpr void lock() {}
254 static constexpr void unlock() {}
255 static constexpr void try_lock() {}
256 };
257
258 template<typename T>
260 {
261 public:
262 value_with_mutex() = default;
263
264 explicit value_with_mutex(const T& v)
265 : m_value{v}
266 {
267 }
268
269 explicit value_with_mutex(T&& v)
270 : m_value{std::move(v)}
271 {
272 }
273
275 {
276 public:
277 pointer_under_lock(value_with_mutex<T>&& value) = delete;
278
280 : pointer_under_lock{value.m_value, value.m_mutex}
281 {
282 }
283
284 private:
285 pointer_under_lock(T& val, std::mutex& mutex)
286 : m_ptr{&val}
287 , m_lock{mutex}
288 {
289 }
290
291 public:
292 T* operator->() { return m_ptr; }
293 const T* operator->() const { return m_ptr; }
294
295 T& operator*() { return *m_ptr; }
296 const T& operator*() const { return *m_ptr; }
297
298 private:
299 T* m_ptr;
300 std::scoped_lock<std::mutex> m_lock;
301 };
302
303 pointer_under_lock lock() { return *this; }
304
305 std::mutex& get_mutex() { return m_mutex; }
306 T& get_value_unsafe() { return m_value; }
307
308 private:
309 RPP_NO_UNIQUE_ADDRESS T m_value{};
310 std::mutex m_mutex{};
311 };
312
313 template<typename T>
314 using pointer_under_lock = typename value_with_mutex<T>::pointer_under_lock;
315
316 namespace details
317 {
318 template<typename T, typename... Ts>
319 struct unique_variant_t : std::type_identity<T>
320 {
321 };
322
323 template<typename... Ts, typename U, typename... Us>
324 requires (std::is_same_v<U, Ts> || ...)
325 struct unique_variant_t<std::variant<Ts...>, U, Us...> : unique_variant_t<std::variant<Ts...>, Us...>
326 {
327 };
328
329 template<typename... Ts, typename U, typename... Us>
330 struct unique_variant_t<std::variant<Ts...>, U, Us...> : public unique_variant_t<std::variant<Ts..., U>, Us...>
331 {
332 };
333 } // namespace details
334
335 template<typename... Ts>
336 using unique_variant = typename details::unique_variant_t<std::variant<>, Ts...>::type; // inspired by https://stackoverflow.com/a/57528226/17771792
337
338
339#define RPP_CALL_DURING_CONSTRUCTION(...) RPP_NO_UNIQUE_ADDRESS rpp::utils::none _ = [&]() { \
340 __VA_ARGS__; \
341 return rpp::utils::none{}; \
342}()
343} // namespace rpp::utils
Calls passed function during destruction.
Definition utils.hpp:120
Definition utils.hpp:143
Definition tuple.hpp:105
Definition utils.hpp:260
Definition utils.hpp:48
Definition utils.hpp:68
Definition utils.hpp:40
Definition utils.hpp:252
Definition utils.hpp:25
Definition utils.hpp:96
Definition utils.hpp:30