di 0.1.0
Loading...
Searching...
No Matches
optional.h
Go to the documentation of this file.
1#pragma once
2
8#include "di/meta/compare.h"
9#include "di/meta/core.h"
10#include "di/meta/language.h"
11#include "di/meta/operations.h"
12#include "di/meta/trivial.h"
13#include "di/meta/util.h"
14#include "di/meta/vocab.h"
15#include "di/types/in_place.h"
16#include "di/util/addressof.h"
17#include "di/util/clone.h"
18#include "di/util/declval.h"
20#include "di/util/move.h"
21#include "di/util/swap.h"
25
26namespace di::vocab {
27namespace detail {
28 template<typename Opt, typename From, typename To>
29 concept OptionalConvertibleToWorkaround =
30 !concepts::SameAs<Opt, meta::RemoveCVRef<From>> && concepts::ConvertibleTo<From, To>;
31}
32
33template<typename T>
34requires(!concepts::LanguageVoid<T>)
36 : public meta::EnableView<Optional<T>>
37 , public meta::EnableBorrowedContainer<Optional<T>, concepts::LValueReference<T>>
38 , public function::monad::MonadInterface<Optional<T>> {
39private:
40 static_assert(!concepts::RValueReference<T>);
41
42 using Storage = StorageFor<T>;
43 static_assert(OptionalStorage<Storage, T>);
44
45public:
46 using Value = T;
47
48 constexpr Optional() = default;
49 constexpr Optional(NullOpt) {}
50
51 // Conditionally trivial special member functions. These overloads will
52 // be selected when the special member functions defined below are deleted.
53 constexpr Optional(Optional const&) = default;
54 constexpr Optional(Optional&&) = default;
55 constexpr auto operator=(Optional const&) -> Optional& = default;
56 constexpr auto operator=(Optional&&) -> Optional& = default;
57 constexpr ~Optional() = default;
58
59 constexpr Optional(Optional const& other)
61 {
62 if (other.has_value()) {
63 emplace(other.value());
64 }
65 }
66
67 constexpr Optional(Optional&& other)
69 {
70 if (other.has_value()) {
71 emplace(util::move(other).value());
72 }
73 }
74
75 template<typename U>
79 if (other.has_value()) {
80 emplace(other.value());
81 }
82 }
83
84 template<typename U>
87 constexpr explicit(!detail::OptionalConvertibleToWorkaround<Optional, U&&, T>) Optional(Optional<U>&& other) {
88 if (other.has_value()) {
89 emplace(util::move(other).value());
90 }
91 }
92
93 template<typename... Args>
94 requires(concepts::ConstructibleFrom<T, Args...>)
95 constexpr explicit Optional(types::InPlace, Args&&... args) {
96 emplace(util::forward<Args>(args)...);
97 }
98
99 template<typename U, typename... Args>
101 constexpr explicit Optional(types::InPlace, std::initializer_list<U> list, Args&&... args) {
102 emplace(list, util::forward<Args>(args)...);
103 }
104
105 template<typename U = T>
107 constexpr explicit(!detail::OptionalConvertibleToWorkaround<Optional, U&&, T>) Optional(U&& value) {
108 emplace(util::forward<U>(value));
109 }
110
111 constexpr ~Optional()
113 {
114 reset();
115 }
116
117 constexpr auto operator=(NullOpt) -> Optional& {
118 reset();
119 return *this;
120 }
121
122 constexpr auto operator=(Optional const& other) -> Optional&
124 {
125 if (other.has_value()) {
126 emplace(other.value());
127 } else {
128 reset();
129 }
130 return *this;
131 }
132
133 constexpr auto operator=(Optional&& other) -> Optional&
135 {
136 if (other.has_value()) {
137 emplace(util::move(other).value());
138 } else {
139 reset();
140 }
141 return *this;
142 }
143
144 template<typename U = T>
147 constexpr auto operator=(U&& value) -> Optional& {
148 emplace(util::forward<U>(value));
149 return *this;
150 }
151
152 template<typename U>
154 constexpr auto operator=(Optional<U> const& other) -> Optional& {
155 if (other.has_value()) {
156 emplace(other.value());
157 } else {
158 reset();
159 }
160 return *this;
161 }
162
163 template<typename U>
165 constexpr auto operator=(Optional<U>&& other) -> Optional& {
166 if (other.has_value()) {
167 emplace(util::move(other).value());
168 } else {
169 reset();
170 }
171 return *this;
172 }
173
174 auto clone() const -> Optional
175 requires(concepts::Clonable<T>)
176 {
177 if (has_value()) {
178 return di::clone(value());
179 }
180 return {};
181 }
182
183 // Accessors
184 constexpr auto has_value() const -> bool { return !is_nullopt(m_storage); }
185 constexpr explicit operator bool() const { return has_value(); }
186
189
190 using Pointer = decltype(util::addressof(util::declval<Reference>()));
191 using ConstPointer = decltype(util::addressof(util::declval<ConstReference>()));
192
193 constexpr auto operator->() -> Pointer { return util::addressof(value()); }
194 constexpr auto operator->() const -> ConstPointer { return util::addressof(value()); }
195
196 constexpr auto operator*() & -> decltype(auto) { return value(); }
197 constexpr auto operator*() const& -> decltype(auto) { return value(); }
198 constexpr auto operator*() && -> decltype(auto) { return util::move(*this).value(); }
199 constexpr auto operator*() const&& -> decltype(auto) { return util::move(*this).value(); }
200
201 constexpr auto value() & -> decltype(auto) {
203 return get_value(m_storage);
204 }
205 constexpr auto value() const& -> decltype(auto) {
207 return get_value(m_storage);
208 }
209 constexpr auto value() && -> decltype(auto) {
211 return get_value(util::move(m_storage));
212 }
213 constexpr auto value() const&& -> decltype(auto) {
215 return get_value(util::move(m_storage));
216 }
217
218 template<concepts::ConvertibleTo<T> U = T>
219 requires(concepts::Copyable<T>)
220 constexpr auto value_or(U&& fallback) const& -> T {
221 return has_value() ? value() : static_cast<T>(util::forward<U>(fallback));
222 }
223
224 template<concepts::ConvertibleTo<T> U = T>
225 constexpr auto value_or(U&& fallback) && -> T {
226 return has_value() ? util::move(*this).value() : static_cast<T>(util::forward<U>(fallback));
227 }
228
229 // Container interface
230 constexpr auto begin() -> Pointer {
231 if (!has_value()) {
232 return nullptr;
233 }
234 return util::addressof(value());
235 }
236
237 constexpr auto begin() const -> ConstPointer {
238 if (!has_value()) {
239 return nullptr;
240 }
241 return util::addressof(value());
242 }
243
244 constexpr auto end() -> Pointer {
245 if (!has_value()) {
246 return nullptr;
247 }
248 return util::addressof(value()) + 1;
249 }
250
251 constexpr auto end() const -> ConstPointer {
252 if (!has_value()) {
253 return nullptr;
254 }
255 return util::addressof(value()) + 1;
256 }
257
258 constexpr auto empty() const -> bool { return !has_value(); }
259 constexpr auto size() const -> types::size_t { return has_value(); }
260
261 constexpr auto data() -> Pointer { return begin(); }
262 constexpr auto data() const -> ConstPointer { return begin(); }
263
264 constexpr auto front() -> Optional<Reference> {
265 if (!has_value()) {
266 return nullopt;
267 }
268 return **this;
269 }
270 constexpr auto front() const -> Optional<ConstReference> {
271 if (!has_value()) {
272 return nullopt;
273 }
274 return **this;
275 }
276
277 constexpr auto back() -> Optional<Reference> { return front(); }
278 constexpr auto back() const -> Optional<ConstReference> { return front(); }
279
280 constexpr auto operator[](types::ssize_t index) -> Reference { return *at(index); }
281 constexpr auto operator[](types::ssize_t index) const -> ConstReference { return *at(index); }
282
283 constexpr auto at(types::ssize_t index) -> Optional<Reference> {
284 if (index != 0) {
285 return nullopt;
286 }
287 return front();
288 }
289
290 constexpr auto at(types::ssize_t index) const -> Optional<ConstReference> {
291 if (index != 0) {
292 return nullopt;
293 }
294 return front();
295 }
296
297 constexpr void reset() { set_nullopt(m_storage); }
298
299 template<typename... Args>
300 constexpr auto emplace(Args&&... args) -> decltype(auto) {
301 reset();
302 set_value(m_storage, util::forward<Args>(args)...);
303 return **this;
304 }
305
306private:
307 constexpr friend void tag_invoke(types::Tag<util::swap>, Optional& a, Optional& b)
308 requires(concepts::Swappable<T>)
309 {
310 if (a.has_value() && b.has_value()) {
311 util::swap(a.m_storage, b.m_storage);
312 } else if (a.has_value()) {
313 b = util::move(a);
314 a = {};
315 } else if (b.has_value()) {
316 a = util::move(b);
317 b = {};
318 }
319 }
320
321 // Monadic interface
322 template<concepts::DecaySameAs<Optional> Self, concepts::Invocable<meta::Like<Self, Value>> F,
323 typename R = meta::InvokeResult<F, meta::Like<Self, Value>>>
324 requires(concepts::Optional<R>)
325 constexpr friend auto tag_invoke(types::Tag<function::monad::bind>, Self&& self, F&& f) -> R {
326 if (self.has_value()) {
327 return function::invoke(util::forward<F>(f), util::forward<Self>(self).value());
328 }
329 return R();
330 }
331
332 template<concepts::DecaySameAs<Optional> Self, concepts::Invocable<meta::Like<Self, Value>> F,
333 typename R = meta::UnwrapRefDecay<meta::InvokeResult<F, meta::Like<Self, Value>>>>
334 constexpr friend auto tag_invoke(types::Tag<function::monad::fmap>, Self&& self, F&& f) -> Optional<R> {
335 if (self.has_value()) {
336 if constexpr (concepts::LanguageVoid<R>) {
337 function::invoke(util::forward<F>(f), util::forward<Self>(self).value());
339 } else {
341 function::invoke(util::forward<F>(f), util::forward<Self>(self).value()));
342 }
343 } else {
344 return nullopt;
345 }
346 }
347
348 template<concepts::DecaySameAs<Optional> Self, concepts::InvocableTo<Optional> F>
350 constexpr friend auto tag_invoke(types::Tag<function::monad::fail>, Self&& self, F&& f) -> Optional {
351 return self.has_value() ? util::forward<Self>(self) : function::invoke(util::forward<F>(f));
352 }
353
354 Storage m_storage { nullopt };
355};
356
357template<typename T, concepts::EqualityComparableWith<T> U>
358constexpr auto operator==(Optional<T> const& a, Optional<U> const& b) -> bool {
359 return (!a && !b) || (a && b && *a == *b);
360}
361
362template<typename T>
363constexpr auto operator==(Optional<T> const& a, NullOpt) -> bool {
364 return !a;
365}
366
367template<typename T, typename U>
369constexpr auto operator==(Optional<T> const& a, U const& b) -> bool {
370 return a.has_value() && *a == b;
371}
372
373template<typename T, concepts::ThreeWayComparableWith<T> U>
375 if (!a && !b) {
376 return types::strong_ordering::equal;
377 }
378 if (auto result = a.has_value() <=> b.has_value(); result != 0) {
379 return result;
380 }
381 return *a <=> *b;
382}
383
384template<typename T>
385constexpr auto operator<=>(Optional<T> const& a, NullOpt) -> types::strong_ordering {
386 return a.has_value() <=> false;
387}
388
389template<typename T, typename U>
391constexpr auto operator<=>(Optional<T> const& a, U const& b) -> meta::CompareThreeWayResult<T, U> {
392 if (!a.has_value()) {
393 return types::strong_ordering::less;
394 }
395 return *a <=> b;
396}
397
398template<class T>
400}
#define DI_ASSERT(...)
Definition assert_bool.h:7
constexpr ~Optional()=default
constexpr ~Optional()
Definition optional.h:111
constexpr auto operator*() &&-> decltype(auto)
Definition optional.h:198
constexpr auto operator->() -> Pointer
Definition optional.h:193
constexpr auto value_or(U &&fallback) const &-> T
Definition optional.h:220
constexpr auto operator*() &-> decltype(auto)
Definition optional.h:196
constexpr auto value() &&-> decltype(auto)
Definition optional.h:209
constexpr auto size() const -> types::size_t
Definition optional.h:259
constexpr auto begin() const -> ConstPointer
Definition optional.h:237
constexpr auto data() const -> ConstPointer
Definition optional.h:262
constexpr auto operator*() const &&-> decltype(auto)
Definition optional.h:199
constexpr auto end() -> Pointer
Definition optional.h:244
constexpr auto value() const &&-> decltype(auto)
Definition optional.h:213
constexpr auto front() -> Optional< Reference >
Definition optional.h:264
decltype(get_value(util::declval< Storage & >())) Reference
Definition optional.h:187
constexpr auto operator=(Optional &&) -> Optional &=default
decltype(get_value(util::declval< Storage const & >())) ConstReference
Definition optional.h:188
constexpr friend auto tag_invoke(types::Tag< function::monad::fail >, Self &&self, F &&f) -> Optional
Definition optional.h:350
constexpr friend auto tag_invoke(types::Tag< function::monad::fmap >, Self &&self, F &&f) -> Optional< R >
Definition optional.h:334
constexpr auto begin() -> Pointer
Definition optional.h:230
constexpr auto operator[](types::ssize_t index) -> Reference
Definition optional.h:280
constexpr friend auto tag_invoke(types::Tag< function::monad::bind >, Self &&self, F &&f) -> R
Definition optional.h:325
constexpr Optional(Optional &&other)
Definition optional.h:67
constexpr auto operator*() const &-> decltype(auto)
Definition optional.h:197
constexpr Optional(types::InPlace, std::initializer_list< U > list, Args &&... args)
Definition optional.h:101
constexpr friend void tag_invoke(types::Tag< util::swap >, Optional &a, Optional &b)
Definition optional.h:307
constexpr auto value() const &-> decltype(auto)
Definition optional.h:205
decltype(util::addressof(util::declval< Reference >())) Pointer
Definition optional.h:190
constexpr Optional(Optional const &)=default
constexpr auto data() -> Pointer
Definition optional.h:261
decltype(util::addressof(util::declval< ConstReference >())) ConstPointer
Definition optional.h:191
constexpr auto operator=(Optional const &other) -> Optional &requires(concepts::Copyable< T > &&!concepts::TriviallyCopyAssignable< Storage >)
Definition optional.h:122
constexpr Optional(Optional const &other)
Definition optional.h:59
T Value
Definition optional.h:46
constexpr auto operator->() const -> ConstPointer
Definition optional.h:194
constexpr auto operator=(Optional &&other) -> Optional &requires(!concepts::TriviallyMoveAssignable< Storage >)
Definition optional.h:133
constexpr Optional(NullOpt)
Definition optional.h:49
constexpr auto empty() const -> bool
Definition optional.h:258
constexpr auto operator=(Optional const &) -> Optional &=default
constexpr auto back() const -> Optional< ConstReference >
Definition optional.h:278
auto clone() const -> Optional requires(concepts::Clonable< T >)
Definition optional.h:174
constexpr auto at(types::ssize_t index) -> Optional< Reference >
Definition optional.h:283
constexpr auto end() const -> ConstPointer
Definition optional.h:251
constexpr Optional(types::InPlace, Args &&... args)
Definition optional.h:95
constexpr auto operator[](types::ssize_t index) const -> ConstReference
Definition optional.h:281
constexpr auto emplace(Args &&... args) -> decltype(auto)
Definition optional.h:300
constexpr Optional()=default
constexpr auto value_or(U &&fallback) &&-> T
Definition optional.h:225
constexpr void reset()
Definition optional.h:297
constexpr Optional(Optional &&)=default
constexpr auto back() -> Optional< Reference >
Definition optional.h:277
constexpr auto value() &-> decltype(auto)
Definition optional.h:201
constexpr auto front() const -> Optional< ConstReference >
Definition optional.h:270
constexpr auto operator=(NullOpt) -> Optional &
Definition optional.h:117
constexpr auto has_value() const -> bool
Definition optional.h:184
constexpr auto at(types::ssize_t index) const -> Optional< ConstReference >
Definition optional.h:290
Definition optional_forward_declaration.h:5
Definition operations.h:11
Definition operations.h:40
Implicit conversion for this test refers to the ability to return a value of function from a type.
Definition operations.h:89
Definition core.h:128
Definition core.h:117
Definition vocab.h:77
Definition language.h:44
Definition core.h:114
Definition language.h:367
Definition swap.h:37
Definition constructible_from_cref_optional.h:8
Definition optional_storage.h:16
Definition any_storable.h:9
Definition as_bool.h:8
constexpr auto invoke
Definition invoke.h:100
Definition merge_interfaces.h:6
meta::Type< detail::CompareThreeWayResultHelper< T, U > > CompareThreeWayResult
Definition compare.h:133
constexpr auto OptionalRank
Definition vocab.h:85
Definition method.h:5
ptrdiff_t ssize_t
Definition ssize_t.h:6
constexpr auto in_place
Definition in_place.h:8
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
auto declval() -> meta::AddRValueReference< T >
Definition declval.h:8
constexpr struct di::util::SwapFunction swap
Definition lazy.h:165
constexpr struct di::vocab::SetValueFunction set_value
Optional(T) -> Optional< T >
constexpr auto operator<=>(Optional< T > const &a, Optional< U > const &b) -> meta::CompareThreeWayResult< T, U >
Definition optional.h:374
constexpr auto nullopt
Definition nullopt.h:15
meta::Conditional< OptionalStorage< meta::WrapReference< T >, T >, meta::WrapReference< T >, BasicOptionalStorage< T > > StorageFor
Definition storage_for.h:11
constexpr struct di::vocab::SetNulloptFunction set_nullopt
constexpr auto operator==(StatusCode< T > const &a, StatusCode< U > const &b) -> bool
Definition status_code_equality.h:7
constexpr struct di::vocab::IsNulloptFunction is_nullopt
constexpr auto get_value
Definition get_value.h:15
constexpr auto front
Definition access.h:58
constexpr auto at
Definition access.h:147
constexpr auto clone
Definition clone.h:39
constexpr auto begin
Definition begin.h:52
Definition in_place.h:4
Definition nullopt.h:6