LeviLamina
Loading...
Searching...
No Matches
Optional.h
1#pragma once
2
3#include <concepts>
4#include <cstddef>
5#include <optional>
6#include <stdexcept>
7#include <type_traits>
8
9#include "ll/api/base/Meta.h"
10
11namespace ll::command {
12
13template <class T>
14struct OptionalOffsetGetter;
15
16template <class T>
17class Optional {
18protected:
20 T mValue;
21 bool hasValue;
22
23public:
24 using value_type = T;
25
26 template <class... Args>
27 [[nodiscard]] constexpr Optional(Args&&... args) : mValue(std::forward<Args>(args)...),
28 hasValue{false} {}
29
30 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
31 Optional& operator=(Optional&&) noexcept(std::is_nothrow_move_assignable_v<T>) = default;
32
33 Optional& operator=(Optional const&) noexcept(std::is_nothrow_copy_assignable_v<T>) = default;
34
35 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
36 [[nodiscard]] constexpr Optional(Optional&&) noexcept(std::is_nothrow_move_constructible_v<T>) = default;
37
38 [[nodiscard]] constexpr Optional(Optional const&) noexcept(std::is_nothrow_copy_constructible_v<T>) = default;
39
40 [[nodiscard]] constexpr explicit operator bool() const noexcept { return hasValue; }
41
42 [[nodiscard]] constexpr bool has_value() const noexcept { return hasValue; }
43
44 [[nodiscard]] constexpr T const* as_ptr() const noexcept { return hasValue ? &mValue : nullptr; }
45
46 [[nodiscard]] constexpr T* as_ptr() noexcept { return hasValue ? &mValue : nullptr; }
47
48 [[nodiscard]] constexpr T const& get() const& {
49 if (!has_value()) {
50 throw std::bad_optional_access{};
51 }
52 return mValue;
53 }
54
55 [[nodiscard]] constexpr T& get() & {
56 if (!has_value()) {
57 throw std::bad_optional_access{};
58 }
59 return mValue;
60 }
61
62 [[nodiscard]] constexpr T const&& get() const&& {
63 if (!has_value()) {
64 throw std::bad_optional_access{};
65 }
66 return std::move(mValue);
67 }
68
69 [[nodiscard]] constexpr T&& get() && {
70 if (!has_value()) {
71 throw std::bad_optional_access{};
72 }
73 return std::move(mValue);
74 }
75
76 [[nodiscard]] constexpr T const&& value() const&& { return std::move(get()); }
77 [[nodiscard]] constexpr T&& value() && { return std::move(get()); }
78 [[nodiscard]] constexpr T const& value() const& { return get(); }
79 [[nodiscard]] constexpr T& value() & { return get(); }
80
81 [[nodiscard]] constexpr T const&& operator*() const&& { return std::move(get()); }
82 [[nodiscard]] constexpr T&& operator*() && { return std::move(get()); }
83 [[nodiscard]] constexpr T const& operator*() const& { return get(); }
84 [[nodiscard]] constexpr T& operator*() & { return get(); }
85
86 [[nodiscard]] constexpr T const* operator->() const { return &get(); }
87 [[nodiscard]] constexpr T* operator->() { return &get(); }
88
89 [[nodiscard]] constexpr operator T const&&() const&& { return std::move(get()); }
90 [[nodiscard]] constexpr operator T&&() && { return std::move(get()); }
91 [[nodiscard]] constexpr operator T const&() const& { return get(); }
92 [[nodiscard]] constexpr operator T&() & { return get(); }
93
94 [[nodiscard]] constexpr operator T const*() const { return &get(); }
95 [[nodiscard]] constexpr operator T*() { return &get(); }
96
97 template <class U>
98 [[nodiscard]] constexpr T value_or(U&& right) const& {
99 if (has_value()) {
100 return mValue;
101 }
102 return static_cast<T const&>(std::forward<U>(right));
103 }
104
105 template <class U>
106 [[nodiscard]] constexpr T&& value_or(U&& right) && {
107 if (has_value()) {
108 return std::move(mValue);
109 }
110 return static_cast<T&&>(std::forward<U>(right));
111 }
112
113 template <class Fn>
114 constexpr auto and_then(Fn&& fn) & {
115 using Ret = std::invoke_result_t<Fn, T&>;
116 if (has_value()) {
117 return std::invoke(std::forward<Fn>(fn), static_cast<T&>(mValue));
118 } else {
119 return std::remove_cvref_t<Ret>{};
120 }
121 }
122
123 template <class Fn>
124 constexpr auto and_then(Fn&& fn) const& {
125 using Ret = std::invoke_result_t<Fn, T const&>;
126 if (has_value()) {
127 return std::invoke(std::forward<Fn>(fn), static_cast<T const&>(mValue));
128 } else {
129 return std::remove_cvref_t<Ret>{};
130 }
131 }
132
133 template <class Fn>
134 constexpr auto and_then(Fn&& fn) && {
135 using Ret = std::invoke_result_t<Fn, T>;
136 if (has_value()) {
137 return std::invoke(std::forward<Fn>(fn), static_cast<T&&>(mValue));
138 } else {
139 return std::remove_cvref_t<Ret>{};
140 }
141 }
142
143 template <class Fn>
144 constexpr auto and_then(Fn&& fn) const&& {
145 using Ret = std::invoke_result_t<Fn, T const>;
146 if (has_value()) {
147 return std::invoke(std::forward<Fn>(fn), static_cast<T const&&>(mValue));
148 } else {
149 return std::remove_cvref_t<Ret>{};
150 }
151 }
152
153 template <class Fn>
154 constexpr auto transform(Fn&& fn) & {
155 using Ret = std::remove_cv_t<std::invoke_result_t<Fn, T&>>;
156 if (has_value()) {
157 return std::optional<Ret>{
158
159 meta::elide(std::forward<Fn>(fn), static_cast<T&>(mValue))
160 };
161 } else {
162 return std::optional<Ret>{};
163 }
164 }
165
166 template <class Fn>
167 constexpr auto transform(Fn&& fn) const& {
168 using Ret = std::remove_cv_t<std::invoke_result_t<Fn, T const&>>;
169 if (has_value()) {
170 return std::optional<Ret>{meta::elide(std::forward<Fn>(fn), static_cast<T const&>(mValue))};
171 } else {
172 return std::optional<Ret>{};
173 }
174 }
175
176 template <class Fn>
177 constexpr auto transform(Fn&& fn) && {
178 using Ret = std::remove_cv_t<std::invoke_result_t<Fn, T>>;
179 if (has_value()) {
180 return std::optional<Ret>{meta::elide(std::forward<Fn>(fn), static_cast<T&&>(mValue))};
181 } else {
182 return std::optional<Ret>{};
183 }
184 }
185
186 template <class Fn>
187 constexpr auto transform(Fn&& fn) const&& {
188 using Ret = std::remove_cv_t<std::invoke_result_t<Fn, T const>>;
189 if (has_value()) {
190 return std::optional<Ret>{meta::elide(std::forward<Fn>(fn), static_cast<T const&&>(mValue))};
191 } else {
192 return std::optional<Ret>{};
193 }
194 }
195
196 template <std::invocable<> Fn>
197 requires std::copy_constructible<T>
198 constexpr std::optional<T> or_else(Fn&& fn) const& {
199 if (has_value()) {
200 return *this;
201 } else {
202 return std::invoke(std::forward<Fn>(fn));
203 }
204 }
205
206 template <std::invocable<> Fn>
207 requires std::move_constructible<T>
208 constexpr std::optional<T> or_else(Fn&& fn) && {
209 if (has_value()) {
210 return std::move(*this);
211 } else {
212 return std::invoke(std::forward<Fn>(fn));
213 }
214 }
215
216 [[nodiscard]] constexpr T const&& value_or_default() const&& { return std::move(value()); }
217 [[nodiscard]] constexpr T&& value_or_default() && { return std::move(value()); }
218 [[nodiscard]] constexpr T const& value_or_default() const& { return value(); }
219 [[nodiscard]] constexpr T& value_or_default() & { return value(); }
220
221 template <typename U = std::decay_t<T>>
222 requires(std::is_constructible_v<U, T>)
223 [[nodiscard]] constexpr std::optional<U> copy_as_optional() const {
224 return has_value() ? std::optional<U>(mValue) : std::nullopt;
225 }
226};
227
228template <class T>
230 using type = T;
231};
232template <class T>
234 using type = T;
235};
236
237template <class T>
238using remove_optional_t = remove_optional<T>::type;
239
240
241template <class T>
243 static constexpr auto value = offsetof(Optional<T>, hasValue);
244};
245
246} // namespace ll::command
Definition Optional.h:17
Definition Meta.h:247
Definition Optional.h:242
Definition Optional.h:229