AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
values.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 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 <variant>
   17#include <utility>
   18#include <functional>
   19#include <optional>
   20#include <glm/glm.hpp>
   21#include <AUI/Common/SharedPtrTypes.h>
   22#include <AUI/Common/AOptional.h>
   23#include <AUI/Thread/AMutex.h>
   24#include <AUI/Traits/concepts.h>
   25
   26namespace aui {
   27namespace detail {
   28API_AUI_CORE void evaluationLoop();
   29}
   30
   45struct noncopyable {
   46    noncopyable() = default;
   47    noncopyable(const noncopyable&) = delete;
   48    noncopyable& operator=(const noncopyable&) = delete;
   49};
   50
   55template <typename T>
   56class assert_not_used_when_null {
   57private:
   58    T mValue;
   59
   60public:
   61    assert_not_used_when_null(T value) noexcept : mValue(std::move(value)) {}
   62
   63    template <typename AnyType>
   64    operator AnyType() noexcept {
   65        if constexpr (!std::is_same_v<AnyType, bool>) {
   66            AUI_ASSERTX(mValue != nullptr, "value is used when null");
   67        }
   68        return AnyType(mValue);
   69    }
   70
   71    [[nodiscard]]
   72    T value() const noexcept {
   73        return mValue;
   74    }
   75
   76    template <typename AnyType>
   77    bool operator==(const AnyType& v) const noexcept {
   78        return mValue == v;
   79    }
   80
   81    template <typename AnyType>
   82    bool operator!=(const AnyType& v) const noexcept {
   83        return mValue != v;
   84    }
   85};
   86
   99template <typename T>
  100struct non_null_lateinit {
  101private:
  102    static_assert(!std::is_reference_v<T>, "====================> aui::non_null: reference type is not allowed");
  103    void checkForNull() const { AUI_ASSERTX(value != nullptr, "this value couldn't be nullptr"); }
  104
  105public:
  106    T value;
  107    non_null_lateinit() {}
  108
  109    template <convertible_to<T> F>
  110    non_null_lateinit(F&& value) : value(std::forward<F>(value)) {
  111        static_assert(
  112            !std::is_same_v<std::decay_t<F>, std::nullptr_t>, "====================> aui::non_null: this value couldn't be nullptr");
  113        checkForNull();
  114    }
  115
  116    operator const T&() const noexcept {
  117        checkForNull();
  118        return value;
  119    }
  120
  121    operator T&() noexcept {
  122        checkForNull();
  123        return value;
  124    }
  125
  126    auto operator->() const {
  127        checkForNull();
  128        return &*value;
  129    }
  130};
  131
  152template <typename T>
  153struct non_null : non_null_lateinit<T> {
  154    template <convertible_to<T> F>
  155    non_null(F&& value) : non_null_lateinit<T>(std::forward<F>(value)) {}
  156
  157    non_null() = delete;   // disallow the default ctor inherited from lateinit.
  158};
  159
  175template <typename T>
  176struct no_escape {
  177    static_assert(
  178        !std::is_reference<T>::value,
  179        "====================> AUI: attempt to use aui::no_escape with reference type. Please use undecorated type "
  180        "(without reference)");
  181    static_assert(
  182        !std::is_pointer_v<T>,
  183        "====================> AUI: attempt to use aui::no_escape with pointer type. Please use undecorated type "
  184        "(without pointer)");
  185
  186private:
  187    T* value;
  188
  189public:
  190    no_escape(T& value) : value(&value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  191    // referring to a temporary value; no_escape should never be used anything else than as argument
  192    // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
  193    no_escape(T&& value) : value(&value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  194    no_escape(T* value) : value(value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  195
  196    no_escape(const _<T>& value) : value(&*value) {
  197        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  198    }
  199    no_escape(const _unique<T>& value) : value(&*value) {
  200        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  201    }
  202
  203    template <
  204        typename DerivedFromT,
  205        std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
  206    no_escape(const _<DerivedFromT>& value) : value(&*value) {
  207        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  208    }
  209
  210    template <
  211        typename DerivedFromT,
  212        std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
  213    no_escape(const _unique<DerivedFromT>& value) : value(&*value) {
  214        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  215    }
  216
  217    [[nodiscard]]
  218    T* ptr() const noexcept {
  219        return value;
  220    }
  221
  222    T* operator->() const noexcept { return value; }
  223
  224    T& operator*() const noexcept { return *value; }
  225};
  226
  231template <typename T = void>
  232struct lazy {
  233private:
  234    struct EvaluationLoopTrap {};
  235    mutable std::variant<std::nullopt_t, T, EvaluationLoopTrap> value = std::nullopt;
  236    std::function<T()> initializer;
  237
  238public:
  239    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
  240    lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
  241
  242    lazy(const lazy<T>& other) noexcept : value(other.value), initializer(other.initializer) {}
  243    lazy(lazy<T>&& other) noexcept : value(std::move(other.value)), initializer(std::move(other.initializer)) {}
  244
  245    T& get() {
  246        if (std::holds_alternative<T>(value)) {
  247            return std::get<T>(value);
  248        }
  249        if (std::holds_alternative<std::nullopt_t>(value)) {
  250            setEvaluationLoopTrap();
  251            try {
  252                value = initializer();
  253            } catch (...) {
  254                value = std::nullopt;
  255                throw;
  256            }
  257            return std::get<T>(value);
  258        }
  259        detail::evaluationLoop();
  260        throw;
  261    }
  262
  263    void setEvaluationLoopTrap() { value = EvaluationLoopTrap {}; }
  264
  265    const T& get() const { return const_cast<lazy<T>*>(this)->get(); }
  266
  267    operator T&() { return get(); }
  268    operator const T&() const { return get(); }
  269
  270    T& operator*() { return get(); }
  271    const T& operator*() const { return get(); }
  272
  273    T* operator->() { return &get(); }
  274    T const* operator->() const { return &get(); }
  275
  276    lazy<T>& operator=(T&& t) {
  277        value = std::move(t);
  278        return *this;
  279    }
  280    lazy<T>& operator=(const T& t) {
  281        value = t;
  282        return *this;
  283    }
  284
  285    void reset() { value = std::nullopt; }
  286
  287    explicit operator bool() const noexcept
  288        requires requires { !aui::same_as<T, bool>; }
  289    {
  290        return hasValue();
  291    }
  292
  293    [[nodiscard]]
  294    bool hasValue() const noexcept {
  295        return std::holds_alternative<T>(value);
  296    }
  297};
  298
  303template <>
  304struct lazy<void> {
  305private:
  306    mutable bool value = false;
  307    std::function<void()> initializer;
  308
  309public:
  310    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<void, Factory>, bool> = true>
  311    lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
  312
  313    lazy(const lazy<void>& other) noexcept : value(other.value), initializer(other.initializer) {}
  314    lazy(lazy<void>&& other) noexcept : value(other.value), initializer(std::move(other.initializer)) {}
  315
  316    void get() {
  317        if (!value) {
  318            value = true;
  319            initializer();
  320        }
  321    }
  322    void get() const { return const_cast<lazy<void>*>(this)->get(); }
  323
  324    void operator*() { return get(); }
  325    const void operator*() const { return get(); }
  326
  327    void reset() { value = false; }
  328
  329    [[nodiscard]]
  330    bool hasValue() const noexcept {
  331        return value;
  332    }
  333};
  334
  340template <typename T>
  341struct atomic_lazy {
  342private:
  343    mutable AMutex sync;
  344    mutable AOptional<T> value;
  345    std::function<T()> initializer;
  346
  347public:
  348    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
  349    atomic_lazy(Factory&& initializer) : initializer(std::forward<Factory>(initializer)) {}
  350
  351    atomic_lazy(const atomic_lazy<T>& other) {
  352        std::unique_lock lock(other.sync);
  353        value = other.value;
  354        initializer = other.initializer;
  355    }
  356    atomic_lazy(atomic_lazy<T>&& other) noexcept {
  357        std::unique_lock lock(other.sync);
  358        value = std::move(other.value);
  359        initializer = std::move(other.initializer);
  360    }
  361
  362    T& get() {
  363        if (!value) {
  364            std::unique_lock lock(sync);
  365            if (!value) {
  366                value = initializer();
  367            }
  368        }
  369        return *value;
  370    }
  371    const T& get() const { return const_cast<atomic_lazy<T>*>(this)->get(); }
  372
  373    operator T&() { return get(); }
  374    operator const T&() const { return get(); }
  375
  376    T& operator*() { return get(); }
  377    const T& operator*() const { return get(); }
  378
  379    T* operator->() { return &get(); }
  380    T const* operator->() const { return &get(); }
  381
  382    atomic_lazy<T>& operator=(T&& t) {
  383        std::unique_lock lock(sync);
  384        value = std::move(t);
  385        return *this;
  386    }
  387
  388    atomic_lazy<T>& operator=(const T& t) {
  389        std::unique_lock lock(sync);
  390        value = t;
  391        return *this;
  392    }
  393
  394    void reset() {
  395        std::unique_lock lock(sync);
  396        value.reset();
  397    }
  398
  399    [[nodiscard]]
  400    bool hasValue() const noexcept {
  401        std::unique_lock lock(sync);
  402        return value.hasValue();
  403    }
  404};
  405
  406namespace constraint {
  413template <typename T>
  414class avoid_copy {
  415private:
  416    T* value;
  417
  418public:
  419    avoid_copy(T& value) : value(&value) {   // implicit initializer
  420    }
  421    operator T&() const {   // implicit conversion
  422        return *value;
  423    }
  424
  425    T& operator*() const {   // std dereference
  426        return *value;
  427    }
  428    T* operator->() const {   // allow pointer-style calls
  429        return value;
  430    }
  431};
  432
  439template <typename T>
  440class move_only {
  441private:
  442    T value;
  443
  444public:
  445    move_only(T&& rhs) : value(std::move(rhs)) {}
  446    move_only(move_only&& rhs) noexcept : value(std::move(rhs.value)) {}
  447    move_only(const move_only&) = delete;
  448
  449    operator const T&() const { return value; }
  450    operator T&() { return value; }
  451
  452    const T& operator*() const { return value; }
  453    const T* operator->() const { return &value; }
  454    T* operator->() { return &value; }
  455};
  456}   // namespace constraint
  457
  465template <aui::arithmetic UnderlyingType, auto /* UnderlyingType*/ min, auto /* UnderlyingType*/ max>   // min and max
  466                                                                                                        // are defined
  467                                                                                                        // auto because
  468                                                                                                        // some static
  469                                                                                                        // analyzers
  470                                                                                                        // won't work
  471                                                                                                        // with float
  472                                                                                                        // value
  473                                                                                                        // template
  474                                                                                                        // arguments
  475    requires aui::convertible_to<decltype(min), UnderlyingType> && aui::convertible_to<decltype(max), UnderlyingType>
  476struct ranged_number {
  477public:
  478    static constexpr auto MIN = min;
  479    static constexpr auto MAX = max;
  480
  481    ranged_number(UnderlyingType value)
  482      : value(glm::clamp(value, static_cast<UnderlyingType>(min), static_cast<UnderlyingType>(max))) {}
  483    ranged_number() : value(min) {}
  484
  485    operator UnderlyingType() const {   // make it possible to work with ranged_number like with the underlying type
  486        return value;
  487    }
  488
  489private:
  490    UnderlyingType value;
  491};
  492
  493using float_within_0_1 = ranged_number<float, 0, 1>;
  494}   // namespace aui
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition AOptional.h:33
Definition Factory.h:18
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:215
Concept shortcut to std::is_arithmetic_v.
Definition concepts.h:145
Definition concepts.h:42
Definition concepts.h:70
#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
Clamps the possible values for a number to the specified range: [min;max].
Definition values.h:476