AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
values.h
1/*
2 * AUI Framework - Declarative UI toolkit for modern C++20
3 * Copyright (C) 2020-2024 Alex2772 and Contributors
4 *
5 * SPDX-License-Identifier: MPL-2.0
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#pragma once
13
14#include <type_traits>
15#include <cassert>
16#include <utility>
17#include <functional>
18#include <optional>
19#include <glm/glm.hpp>
20#include <AUI/Common/SharedPtrTypes.h>
21#include <AUI/Common/AOptional.h>
22#include <AUI/Thread/AMutex.h>
23#include <AUI/Traits/concepts.h>
24
25namespace aui {
40 struct noncopyable {
41 noncopyable() = default;
42 noncopyable(const noncopyable&) = delete;
43 noncopyable& operator=(const noncopyable&) = delete;
44 };
45
46
51 template<typename T>
53 private:
54 T mValue;
55
56 public:
57 assert_not_used_when_null(T value) noexcept: mValue(std::move(value)) {}
58
59 template<typename AnyType>
60 operator AnyType() noexcept {
61 if constexpr(!std::is_same_v<AnyType, bool>) {
62 AUI_ASSERTX(mValue != nullptr, "value is used when null");
63 }
64 return AnyType(mValue);
65 }
66
67 [[nodiscard]]
68 T value() const noexcept {
69 return mValue;
70 }
71
72 template<typename AnyType>
73 bool operator==(const AnyType& v) const noexcept {
74 return mValue == v;
75 }
76
77 template<typename AnyType>
78 bool operator!=(const AnyType& v) const noexcept {
79 return mValue != v;
80 }
81 };
82
83
84 template<typename T>
86 private:
87 void checkForNull() const { AUI_ASSERTX(value != nullptr, "this value couldn't be null"); }
88 public:
89 T value;
91
92 }
93
94 non_null_lateinit(T value): value(std::move(value)) {
95 checkForNull();
96 }
97
98 operator T() const noexcept {
99 checkForNull();
100 return value;
101 }
102 auto operator->() const {
103 checkForNull();
104 return &*value;
105 }
106 };
107
108 template<typename T>
110 non_null(T value): non_null_lateinit<T>(std::move(value)) {}
111 };
112
126 template<typename T>
127 struct no_escape {
128 static_assert(!std::is_reference<T>::value, "use undecorated type (without reference)");
129 static_assert(!std::is_pointer_v<T>, "use undecorated type (without pointer)");
130 private:
131 T* value;
132
133 public:
134 no_escape(T& value): value(&value) {
135 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
136 }
137 // referring to a temporary value; no_escape should never be used anything else than as argument
138 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
139 no_escape(T&& value): value(&value) {
140 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
141 }
142 no_escape(T* value): value(value) {
143 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
144 }
145
146 no_escape(const _<T>& value): value(&*value) {
147 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
148 }
149 no_escape(const _unique<T>& value): value(&*value) {
150 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
151 }
152
153 template<typename DerivedFromT, std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
154 no_escape(const _<DerivedFromT>& value): value(&*value) {
155 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
156 }
157
158 template<typename DerivedFromT, std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
159 no_escape(const _unique<DerivedFromT>& value): value(&*value) {
160 AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
161 }
162
163 [[nodiscard]]
164 T* ptr() const noexcept {
165 return value;
166 }
167
168 T* operator->() const noexcept {
169 return value;
170 }
171
172 T& operator*() const noexcept {
173 return *value;
174 }
175 };
176
181 template<typename T = void>
182 struct lazy {
183 private:
184 mutable AOptional<T> value;
185 std::function<T()> initializer;
186 public:
187 template<typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
188 lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
189
190 lazy(const lazy<T>& other) noexcept: value(other.value), initializer(other.initializer) {}
191 lazy(lazy<T>&& other) noexcept: value(std::move(other.value)), initializer(std::move(other.initializer)) {}
192
193 T& get() {
194 if (!value) {
195 value = initializer();
196 }
197 return *value;
198 }
199 const T& get() const {
200 return const_cast<lazy<T>*>(this)->get();
201 }
202
203 operator T&() {
204 return get();
205 }
206 operator const T&() const {
207 return get();
208 }
209
210 T& operator*() {
211 return get();
212 }
213 const T& operator*() const {
214 return get();
215 }
216
217 T* operator->() {
218 return &get();
219 }
220 T const * operator->() const {
221 return &get();
222 }
223
224 lazy<T>& operator=(T&& t) {
225 value = std::move(t);
226 return *this;
227 }
228 lazy<T>& operator=(const T& t) {
229 value = t;
230 return *this;
231 }
232
233 void reset() {
234 value.reset();
235 }
236
237 [[nodiscard]]
238 bool hasValue() const noexcept {
239 return value.hasValue();
240 }
241 };
242
247 template<>
248 struct lazy<void> {
249 private:
250 mutable bool value = false;
251 std::function<void()> initializer;
252 public:
253 template<typename Factory, std::enable_if_t<std::is_invocable_r_v<void, Factory>, bool> = true>
254 lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
255
256 lazy(const lazy<void>& other) noexcept: value(other.value), initializer(other.initializer) {}
257 lazy(lazy<void>&& other) noexcept: value(other.value), initializer(std::move(other.initializer)) {}
258
259 void get() {
260 if (!value) {
261 value = true;
262 initializer();
263 }
264 }
265 void get() const {
266 return const_cast<lazy<void>*>(this)->get();
267 }
268
269 void operator*() {
270 return get();
271 }
272 const void operator*() const {
273 return get();
274 }
275
276
277 void reset() {
278 value = false;
279 }
280
281 [[nodiscard]]
282 bool hasValue() const noexcept {
283 return value;
284 }
285 };
286
292 template<typename T>
293 struct atomic_lazy {
294 private:
295 mutable AMutex sync;
296 mutable AOptional<T> value;
297 std::function<T()> initializer;
298 public:
299 template<typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
300 atomic_lazy(Factory&& initializer) : initializer(std::forward<Factory>(initializer)) {}
301
302 atomic_lazy(const atomic_lazy<T>& other) {
303 std::unique_lock lock(other.sync);
304 value = other.value;
305 initializer = other.initializer;
306 }
307 atomic_lazy(atomic_lazy<T>&& other) noexcept {
308 std::unique_lock lock(other.sync);
309 value = std::move(other.value);
310 initializer = std::move(other.initializer);
311 }
312
313 T& get() {
314 if (!value) {
315 std::unique_lock lock(sync);
316 if (!value) {
317 value = initializer();
318 }
319 }
320 return *value;
321 }
322 const T& get() const {
323 return const_cast<atomic_lazy<T>*>(this)->get();
324 }
325
326 operator T&() {
327 return get();
328 }
329 operator const T&() const {
330 return get();
331 }
332
333 T& operator*() {
334 return get();
335 }
336 const T& operator*() const {
337 return get();
338 }
339
340 T* operator->() {
341 return &get();
342 }
343 T const * operator->() const {
344 return &get();
345 }
346
347 atomic_lazy<T>& operator=(T&& t) {
348 std::unique_lock lock(sync);
349 value = std::move(t);
350 return *this;
351 }
352
353 atomic_lazy<T>& operator=(const T& t) {
354 std::unique_lock lock(sync);
355 value = t;
356 return *this;
357 }
358
359 void reset() {
360 std::unique_lock lock(sync);
361 value.reset();
362 }
363
364 [[nodiscard]]
365 bool hasValue() const noexcept {
366 std::unique_lock lock(sync);
367 return value.hasValue();
368 }
369 };
370
371 namespace constraint {
378 template<typename T>
380 private:
381 T* value;
382
383 public:
384 avoid_copy(T& value): value(&value) { // implicit initializer
385
386 }
387 operator T&() const { // implicit conversion
388 return *value;
389 }
390
391 T& operator*() const { // std dereference
392 return *value;
393 }
394 T* operator->() const { // allow pointer-style calls
395 return value;
396 }
397 };
398
405 template<typename T>
406 class move_only {
407 private:
408 T value;
409
410 public:
411 move_only(T&& rhs): value(std::move(rhs)) {
412
413 }
414 move_only(move_only&& rhs) noexcept: value(std::move(rhs.value)) {
415
416 }
417 move_only(const move_only&) = delete;
418
419 operator const T&() const {
420 return value;
421 }
422 operator T&() {
423 return value;
424 }
425
426 const T& operator*() const {
427 return value;
428 }
429 const T* operator->() const {
430 return &value;
431 }
432 T* operator->() {
433 return &value;
434 }
435 };
436 }
437
438
439
447 template<aui::arithmetic UnderlyingType,
448 auto /* UnderlyingType*/ min,
449 auto /* UnderlyingType*/ max> // min and max are defined auto because some static analyzers won't work with
450 // float value template arguments
451 requires aui::convertible_to<decltype(min), UnderlyingType> && aui::convertible_to<decltype(max), UnderlyingType>
453 public:
454 static constexpr auto MIN = min;
455 static constexpr auto MAX = max;
456
457 ranged_number(UnderlyingType value): value(glm::clamp(value, static_cast<UnderlyingType>(min), static_cast<UnderlyingType>(max))) {}
458 ranged_number(): value(min) {}
459
460 operator UnderlyingType() const { // make it possible to work with ranged_number like with the underlying type
461 return value;
462 }
463
464 private:
465 UnderlyingType value;
466 };
467
469}
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition: AOptional.h:32
Definition: Factory.h:18
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:177
Definition: values.h:52
Avoids copy of the wrapped value, pointing to a reference.
Definition: values.h:379
Wraps the object forbidding copy.
Definition: values.h:406
Concept shortcut to std::is_arithmetic_v.
Definition: concepts.h:144
#define AUI_ASSERTX(condition, what)
Asserts that the passed condition evaluates to true. Adds extra message string.
Definition: Assert.h:74
Basic syscall-based synchronization primitive.
Definition: AMutex.h:33
A value that initializes when accessed for the first time. Unlike aui::lazy, internal logic of aui::a...
Definition: values.h:293
A value that initializes when accessed for the first time.
Definition: values.h:248
A value that initializes when accessed for the first time.
Definition: values.h:182
Does not allow escaping, allowing to accept lvalue ref, rvalue ref, shared_ptr and etc without overhe...
Definition: values.h:127
Definition: values.h:85
Definition: values.h:109
Forbids copy of your class.
Definition: values.h:40
Definition: SharedPtrTypes.h:114
Clamps the possible values for a number to the specified range: [min;max].
Definition: values.h:452