-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathOptional.h
More file actions
145 lines (122 loc) · 4.39 KB
/
Copy pathOptional.h
File metadata and controls
145 lines (122 loc) · 4.39 KB
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#pragma once
#include "meta19/nullptr_to.h"
#include <cstdint> // uint8_t
#include <new> // std::launder
#include <type_traits> // std::is_trivially_destructable_v
#include <utility>
namespace optional19 {
using meta19::nullptr_to;
struct Inplace;
constexpr static auto inplace = nullptr_to<Inplace>;
namespace details {
template<class T> struct OptionalStorage {
using Storage = uint8_t[sizeof(T)];
alignas(T) Storage m_storage{};
bool m_valid{};
OptionalStorage() = default;
~OptionalStorage() noexcept {
if (m_valid) {
auto p = std::launder(reinterpret_cast<T*>(m_storage));
p->~T();
}
}
OptionalStorage(T v) {
new (m_storage) T(std::move(v));
m_valid = true;
}
template<class... Args> OptionalStorage(Inplace*, Args&&... args) {
new (m_storage) T{(Args &&) args...};
m_valid = true;
}
OptionalStorage(const OptionalStorage& o) noexcept(std::is_nothrow_copy_constructible_v<T>) {
if (o.m_valid) {
new (m_storage) T(o.value());
m_valid = true;
}
}
OptionalStorage& operator=(const OptionalStorage& o) noexcept(std::is_nothrow_copy_assignable_v<T>) {
if (o.m_valid) {
if (m_valid)
amend() = o.value();
else {
new (m_storage) T(o.value());
m_valid = true;
}
}
else if (m_valid) {
amend().~T();
m_valid = false;
}
return *this;
}
OptionalStorage(OptionalStorage&& o) noexcept {
if (o.m_valid) {
new (m_storage) T(std::move(o.amend()));
m_valid = true;
}
}
OptionalStorage& operator=(OptionalStorage&& o) noexcept {
if (o.m_valid) {
if (m_valid)
amend() = std::move(o.amend());
else {
new (m_storage) T(std::move(o.amend()));
m_valid = true;
}
}
else if (m_valid) {
amend().~T();
m_valid = false;
}
return *this;
}
template<class... Args> constexpr void emplace(Args&&... args) {
if (this->m_valid) amend().~T();
new (this->m_storage) T{(Args &&) args...};
this->m_valid = true;
}
auto value() const -> const T& { return *std::launder(reinterpret_cast<const T*>(this->m_storage)); }
auto amend() -> T& { return *std::launder(reinterpret_cast<T*>(this->m_storage)); }
};
/// simplified specialization if no destructor is required
/// * storing actual type saves a lot of casting overhead
template<class T> requires(std::is_trivially_default_constructible_v<T> && std::is_trivially_destructible_v<T>)
struct OptionalStorage<T> {
T m_value{};
bool m_valid{};
constexpr OptionalStorage() = default;
constexpr OptionalStorage(T v) : m_value(std::move(v)), m_valid(true) {}
template<class... Args> OptionalStorage(Inplace*, Args&&... args) : m_value{(Args &&) args...}, m_valid(true) {}
template<class... Args> constexpr void emplace(Args&&... args) {
m_value = T{(Args &&) args...};
m_valid = true;
}
constexpr auto value() const -> const T& { return m_value; }
constexpr auto amend() -> T& { return m_value; }
};
} // namespace details
template<class T> struct Optional : private details::OptionalStorage<T> {
private:
using Base = details::OptionalStorage<T>;
public:
using Value = T;
using Base::Base;
using Base::emplace;
using Base::amend;
using Base::value;
constexpr explicit operator bool() const { return Base::m_valid; }
/// @return predicate(value()) if optional is set else return false
template<class F> requires(std::is_invocable_r_v<bool, F, T>) constexpr auto operator&&(F&& f) const -> bool {
return static_cast<bool>(*this) && f(value());
}
/// @return value if optional is set or generated by callable
template<class F> requires(std::is_invocable_r_v<T, F>) constexpr auto operator||(F&& f) const -> T {
return Base::m_valid ? value() : f();
}
constexpr auto operator||(const T& v) const -> T { return Base::m_valid ? value() : v; }
template<class F> constexpr auto map(F&& f) const -> decltype(f(*nullptr_to<T>)) {
if (Base::m_valid) return f(value());
if constexpr (!std::is_void_v<decltype(f(*nullptr_to<T>))>) return {};
}
};
} // namespace optional19