aboutsummaryrefslogtreecommitdiffstats
path: root/common/aloptional.h
blob: 443da0b46659a0162f346b9cbd7811d1b11ed5e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#ifndef AL_OPTIONAL_H
#define AL_OPTIONAL_H

#include <type_traits>

#include "almalloc.h"

namespace al {

#define REQUIRES(...) bool _rt=true, typename std::enable_if<_rt && (__VA_ARGS__),int>::type = 0

struct nullopt_t { };
struct in_place_t { };

constexpr nullopt_t nullopt{};
constexpr in_place_t in_place{};

template<typename T>
class optional {
public:
    using value_type = T;

    optional() noexcept = default;
    optional(nullopt_t) noexcept { }
    template<REQUIRES(std::is_copy_constructible<T>::value)>
    optional(const optional &rhs) : mHasValue{rhs.mHasValue}
    {
        if(mHasValue)
            std::uninitialized_copy_n(std::addressof(*rhs), 1, std::addressof(mValue));
    }
    template<REQUIRES(std::is_move_constructible<T>::value)>
    optional(optional&& rhs) : mHasValue{rhs.mHasValue}
    {
        if(mHasValue)
            al::uninitialized_move_n(std::addressof(*rhs), 1, std::addressof(mValue));
    }
    template<typename... Args>
    explicit optional(in_place_t, Args&& ...args)
      : mHasValue{true}, mValue{std::forward<Args>(args)...}
    { }
    ~optional() { reset(); }

    optional& operator=(nullopt_t) noexcept { reset(); return *this; }
    template<REQUIRES(std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value)>
    optional& operator=(const optional &rhs)
    {
        if(!rhs)
            reset();
        else if(*this)
            mValue = *rhs;
        else
        {
            std::uninitialized_copy_n(std::addressof(*rhs), 1, std::addressof(mValue));
            mHasValue = true;
        }
        return *this;
    }
    template<REQUIRES(std::is_move_constructible<T>::value && std::is_move_assignable<T>::value)>
    optional& operator=(optional&& rhs)
    {
        if(!rhs)
            reset();
        else if(*this)
            mValue = std::move(*rhs);
        else
        {
            al::uninitialized_move_n(std::addressof(*rhs), 1, std::addressof(mValue));
            mHasValue = true;
        }
        return *this;
    }

    const T* operator->() const { return std::addressof(mValue); }
    T* operator->() { return std::addressof(mValue); }
    const T& operator*() const& { return mValue; }
    T& operator*() & { return mValue; }
    const T&& operator*() const&& { return std::move(mValue); }
    T&& operator*() && { return std::move(mValue); }

    operator bool() const noexcept { return mHasValue; }
    bool has_value() const noexcept { return mHasValue; }

    T& value() & { return mValue; }
    const T& value() const& { return mValue; }
    T&& value() && { return std::move(mValue); }
    const T&& value() const&& { return std::move(mValue); }

    template<typename U>
    T value_or(U&& defval) const&
    { return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
    template<typename U>
    T value_or(U&& defval) &&
    { return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }

    void reset() noexcept
    {
        if(mHasValue)
            al::destroy_at(std::addressof(mValue));
        mHasValue = false;
    }

private:
    bool mHasValue{false};
    union {
        char mDummy[sizeof(T)]{};
        T mValue;
    };
};

#undef REQUIRES

} // namespace al

#endif /* AL_SPAN_H */