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
   87template <typename T>
   88struct non_null_lateinit {
   89private:
   90    void checkForNull() const { AUI_ASSERTX(value != nullptr, "this value couldn't be null"); }
   91
   92public:
   93    T value;
   94    non_null_lateinit() {}
   95
   96    non_null_lateinit(T value) : value(std::move(value)) { checkForNull(); }
   97
   98    operator T() const noexcept {
   99        checkForNull();
  100        return value;
  101    }
  102    auto operator->() const {
  103        checkForNull();
  104        return &*value;
  105    }
  106};
  107
  108template <typename T>
  109struct non_null : non_null_lateinit<T> {
  110    non_null(T value) : non_null_lateinit<T>(std::move(value)) {}
  111};
  112
  127template <typename T>
  128struct no_escape {
  129    static_assert(
  130        !std::is_reference<T>::value,
  131        "====================> AUI: attempt to use aui::no_escape with reference type. Please use undecorated type "
  132        "(without reference)");
  133    static_assert(
  134        !std::is_pointer_v<T>,
  135        "====================> AUI: attempt to use aui::no_escape with pointer type. Please use undecorated type "
  136        "(without pointer)");
  137
  138private:
  139    T* value;
  140
  141public:
  142    no_escape(T& value) : value(&value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  143    // referring to a temporary value; no_escape should never be used anything else than as argument
  144    // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
  145    no_escape(T&& value) : value(&value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  146    no_escape(T* value) : value(value) { AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null"); }
  147
  148    no_escape(const _<T>& value) : value(&*value) {
  149        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  150    }
  151    no_escape(const _unique<T>& value) : value(&*value) {
  152        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  153    }
  154
  155    template <
  156        typename DerivedFromT,
  157        std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
  158    no_escape(const _<DerivedFromT>& value) : value(&*value) {
  159        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  160    }
  161
  162    template <
  163        typename DerivedFromT,
  164        std::enable_if_t<std::is_base_of_v<T, DerivedFromT> && !std::is_same_v<DerivedFromT, T>, bool> = true>
  165    no_escape(const _unique<DerivedFromT>& value) : value(&*value) {
  166        AUI_ASSERTX(no_escape::value != nullptr, "the argument could not be null");
  167    }
  168
  169    [[nodiscard]]
  170    T* ptr() const noexcept {
  171        return value;
  172    }
  173
  174    T* operator->() const noexcept { return value; }
  175
  176    T& operator*() const noexcept { return *value; }
  177};
  178
  183template <typename T = void>
  184struct lazy {
  185private:
  186    struct EvaluationLoopTrap {};
  187    mutable std::variant<std::nullopt_t, T, EvaluationLoopTrap> value = std::nullopt;
  188    std::function<T()> initializer;
  189
  190public:
  191    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
  192    lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
  193
  194    lazy(const lazy<T>& other) noexcept : value(other.value), initializer(other.initializer) {}
  195    lazy(lazy<T>&& other) noexcept : value(std::move(other.value)), initializer(std::move(other.initializer)) {}
  196
  197    T& get() {
  198        if (std::holds_alternative<T>(value)) {
  199            return std::get<T>(value);
  200        }
  201        if (std::holds_alternative<std::nullopt_t>(value)) {
  202            setEvaluationLoopTrap();
  203            value = initializer();
  204            return std::get<T>(value);
  205        }
  206        detail::evaluationLoop();
  207        throw;
  208    }
  209
  210    void setEvaluationLoopTrap() { value = EvaluationLoopTrap {}; }
  211
  212    const T& get() const { return const_cast<lazy<T>*>(this)->get(); }
  213
  214    operator T&() { return get(); }
  215    operator const T&() const { return get(); }
  216
  217    T& operator*() { return get(); }
  218    const T& operator*() const { return get(); }
  219
  220    T* operator->() { return &get(); }
  221    T const* operator->() const { return &get(); }
  222
  223    lazy<T>& operator=(T&& t) {
  224        value = std::move(t);
  225        return *this;
  226    }
  227    lazy<T>& operator=(const T& t) {
  228        value = t;
  229        return *this;
  230    }
  231
  232    void reset() { value = std::nullopt; }
  233
  234    explicit operator bool() const noexcept
  235        requires requires { !aui::same_as<T, bool>; }
  236    {
  237        return hasValue();
  238    }
  239
  240    [[nodiscard]]
  241    bool hasValue() const noexcept {
  242        return std::holds_alternative<T>(value);
  243    }
  244};
  245
  250template <>
  251struct lazy<void> {
  252private:
  253    mutable bool value = false;
  254    std::function<void()> initializer;
  255
  256public:
  257    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<void, Factory>, bool> = true>
  258    lazy(Factory&& initializer) noexcept : initializer(std::forward<Factory>(initializer)) {}
  259
  260    lazy(const lazy<void>& other) noexcept : value(other.value), initializer(other.initializer) {}
  261    lazy(lazy<void>&& other) noexcept : value(other.value), initializer(std::move(other.initializer)) {}
  262
  263    void get() {
  264        if (!value) {
  265            value = true;
  266            initializer();
  267        }
  268    }
  269    void get() const { return const_cast<lazy<void>*>(this)->get(); }
  270
  271    void operator*() { return get(); }
  272    const void operator*() const { return get(); }
  273
  274    void reset() { value = false; }
  275
  276    [[nodiscard]]
  277    bool hasValue() const noexcept {
  278        return value;
  279    }
  280};
  281
  287template <typename T>
  288struct atomic_lazy {
  289private:
  290    mutable AMutex sync;
  291    mutable AOptional<T> value;
  292    std::function<T()> initializer;
  293
  294public:
  295    template <typename Factory, std::enable_if_t<std::is_invocable_r_v<T, Factory>, bool> = true>
  296    atomic_lazy(Factory&& initializer) : initializer(std::forward<Factory>(initializer)) {}
  297
  298    atomic_lazy(const atomic_lazy<T>& other) {
  299        std::unique_lock lock(other.sync);
  300        value = other.value;
  301        initializer = other.initializer;
  302    }
  303    atomic_lazy(atomic_lazy<T>&& other) noexcept {
  304        std::unique_lock lock(other.sync);
  305        value = std::move(other.value);
  306        initializer = std::move(other.initializer);
  307    }
  308
  309    T& get() {
  310        if (!value) {
  311            std::unique_lock lock(sync);
  312            if (!value) {
  313                value = initializer();
  314            }
  315        }
  316        return *value;
  317    }
  318    const T& get() const { return const_cast<atomic_lazy<T>*>(this)->get(); }
  319
  320    operator T&() { return get(); }
  321    operator const T&() const { return get(); }
  322
  323    T& operator*() { return get(); }
  324    const T& operator*() const { return get(); }
  325
  326    T* operator->() { return &get(); }
  327    T const* operator->() const { return &get(); }
  328
  329    atomic_lazy<T>& operator=(T&& t) {
  330        std::unique_lock lock(sync);
  331        value = std::move(t);
  332        return *this;
  333    }
  334
  335    atomic_lazy<T>& operator=(const T& t) {
  336        std::unique_lock lock(sync);
  337        value = t;
  338        return *this;
  339    }
  340
  341    void reset() {
  342        std::unique_lock lock(sync);
  343        value.reset();
  344    }
  345
  346    [[nodiscard]]
  347    bool hasValue() const noexcept {
  348        std::unique_lock lock(sync);
  349        return value.hasValue();
  350    }
  351};
  352
  353namespace constraint {
  360template <typename T>
  361class avoid_copy {
  362private:
  363    T* value;
  364
  365public:
  366    avoid_copy(T& value) : value(&value) {   // implicit initializer
  367    }
  368    operator T&() const {   // implicit conversion
  369        return *value;
  370    }
  371
  372    T& operator*() const {   // std dereference
  373        return *value;
  374    }
  375    T* operator->() const {   // allow pointer-style calls
  376        return value;
  377    }
  378};
  379
  386template <typename T>
  387class move_only {
  388private:
  389    T value;
  390
  391public:
  392    move_only(T&& rhs) : value(std::move(rhs)) {}
  393    move_only(move_only&& rhs) noexcept : value(std::move(rhs.value)) {}
  394    move_only(const move_only&) = delete;
  395
  396    operator const T&() const { return value; }
  397    operator T&() { return value; }
  398
  399    const T& operator*() const { return value; }
  400    const T* operator->() const { return &value; }
  401    T* operator->() { return &value; }
  402};
  403}   // namespace constraint
  404
  412template <aui::arithmetic UnderlyingType, auto /* UnderlyingType*/ min, auto /* UnderlyingType*/ max>   // min and max
  413                                                                                                        // are defined
  414                                                                                                        // auto because
  415                                                                                                        // some static
  416                                                                                                        // analyzers
  417                                                                                                        // won't work
  418                                                                                                        // with float
  419                                                                                                        // value
  420                                                                                                        // template
  421                                                                                                        // arguments
  422    requires aui::convertible_to<decltype(min), UnderlyingType> && aui::convertible_to<decltype(max), UnderlyingType>
  423struct ranged_number {
  424public:
  425    static constexpr auto MIN = min;
  426    static constexpr auto MAX = max;
  427
  428    ranged_number(UnderlyingType value)
  429      : value(glm::clamp(value, static_cast<UnderlyingType>(min), static_cast<UnderlyingType>(max))) {}
  430    ranged_number() : value(min) {}
  431
  432    operator UnderlyingType() const {   // make it possible to work with ranged_number like with the underlying type
  433        return value;
  434    }
  435
  436private:
  437    UnderlyingType value;
  438};
  439
  440using float_within_0_1 = ranged_number<float, 0, 1>;
  441}   // 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:179
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:423