header_utils
Loading...
Searching...
No Matches
soptional.h
1
4
5#pragma once
6
7#include <optional>
8#include <bit>
9#include "min-cpp-version/cpp20.h"
10
11namespace ghassanpl
12{
16
17 // TODO:
18 // ```c++
19 // struct sentinel_struct {
20 // static bool operator()(T const& v) const { return v == sentinel_value; } // check func
21 // static void operator()(T& v) const { v = sentinel_value; } // reset func
22 // };
23 // template <typename T, typename SENTINEL_STRUCT>
24 // struct sentinel_optional;
25 // ```
26 // for types that cannot be template arguments.
27
28 namespace detail
29 {
30 struct better_equal_to
31 {
32 template <class T, class U>
33 [[nodiscard]] constexpr auto operator()(T&& t, U&& u) const
34 {
36 if constexpr (std::is_floating_point_v<std::remove_cvref_t<T>> && std::is_floating_point_v<std::remove_cvref_t<U>>)
37 return std::bit_cast<uint64_t>(double(t)) == std::bit_cast<uint64_t>(double(u));
38 else
39 return std::forward<T>(t) == std::forward<U>(u);
40 }
41
42 using is_transparent = int;
43 };
44 }
45
54 template <typename T, auto SENTINEL = T{}, typename EQ = detail::better_equal_to>
55 struct sentinel_optional
56 {
57 using sentinel_type = decltype(SENTINEL);
58
59 static_assert(EQ{}(SENTINEL, SENTINEL), "Sentinel value must equal itself under the equal operation given in the template parameter EQ");
60
61 constexpr sentinel_optional() noexcept {}
62 constexpr sentinel_optional(std::nullopt_t) noexcept {}
63
64 template <class... TYPES, std::enable_if_t<std::is_constructible_v<T, TYPES...>, int> = 0>
65 constexpr explicit sentinel_optional(std::in_place_t, TYPES&&... args) noexcept(std::is_nothrow_constructible_v<T, TYPES...>)
66 : m_value(std::forward<TYPES>(args)...)
67 {
68 }
69
70 template <class U = T, std::enable_if_t<
71 !std::is_same_v<remove_cvref_t<U>, sentinel_optional> &&
72 !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
73 !std::is_same_v<std::remove_cv_t<U>, bool> &&
74 std::is_constructible_v<T, U>
75 , int> = 0>
76 constexpr sentinel_optional(U&& right) noexcept(std::is_nothrow_constructible_v<T, U>)
77 : m_value(std::forward<U>(right))
78 {
79 }
80
81 template <class U, std::enable_if_t<std::is_constructible_v<T, const U&>, int> = 0>
82 constexpr explicit sentinel_optional(const sentinel_optional<U>& right) noexcept(std::is_nothrow_constructible_v<T, const U&>)
83 {
84 if (right)
85 this->m_value = *right;
86 }
87
88 template <class U, std::enable_if_t<std::is_constructible_v<T, U>, int> = 0>
89 constexpr explicit sentinel_optional(sentinel_optional<U>&& right) noexcept(std::is_nothrow_constructible_v<T, U>)
90 {
91 if (right)
92 this->m_value = std::move(*right);
93 }
94
95 constexpr sentinel_optional& operator=(std::nullopt_t) noexcept {
96 reset();
97 return *this;
98 }
99
100 template <class U = T, std::enable_if_t<
101 !std::is_same_v<sentinel_optional, remove_cvref_t<U>> &&
102 std::is_constructible_v<T, U> &&
103 std::is_assignable_v<T&, U>,
104 int> = 0>
105 constexpr sentinel_optional& operator=(U&& right) noexcept(std::is_nothrow_assignable_v<T&, U> && std::is_nothrow_constructible_v<T, U>)
106 {
107 this->m_value = std::forward<U>(right);
108 return *this;
109 }
110
111 template <class U, std::enable_if_t<
112 !(
113 std::is_same_v<T, U> ||
114 std::is_assignable_v<T&, sentinel_optional<U>&> ||
115 std::is_assignable_v<T&, const sentinel_optional<U>&> ||
116 std::is_assignable_v<T&, const sentinel_optional<U>> ||
117 std::is_assignable_v<T&, sentinel_optional<U>>
118 ) &&
119 std::is_constructible_v<T, const U&> &&
120 std::is_assignable_v<T&, const U&>,
121 int> = 0>
122 constexpr sentinel_optional& operator=(const sentinel_optional<U>& right) noexcept(std::is_nothrow_assignable_v<T&, const U&> && std::is_nothrow_constructible_v<T, const U&>)
123 {
124 if (right)
125 this->m_value = *right;
126 else
127 reset();
128
129 return *this;
130 }
131
132 template <class U, std::enable_if_t<
133 !(
134 std::is_same_v<T, U> ||
135 std::is_assignable_v<T&, sentinel_optional<U>&> ||
136 std::is_assignable_v<T&, const sentinel_optional<U>&> ||
137 std::is_assignable_v<T&, const sentinel_optional<U>> ||
138 std::is_assignable_v<T&, sentinel_optional<U>>
139 ) &&
140 std::is_constructible_v<T, U> &&
141 std::is_assignable_v<T&, U>,
142 int> = 0>
143 constexpr sentinel_optional& operator=(sentinel_optional<U>&& right) noexcept(std::is_nothrow_assignable_v<T&, U>&& std::is_nothrow_constructible_v<T, U>)
144 {
145 if (right)
146 this->m_value = std::move(*right);
147 else
148 reset();
149
150 return *this;
151 }
152
153 template <class... TYPES>
154 constexpr T& emplace(TYPES&&... args) noexcept(std::is_nothrow_constructible_v<T, TYPES...>)
155 {
156 reset();
157 return this->m_value = T(std::forward<TYPES>(args)...);
158 }
159
160 inline constexpr void assert_value() const { if (!has_value()) throw std::bad_optional_access(); }
161
162 [[nodiscard]] constexpr const T* operator->() const
163 {
164 this->assert_value();
165 return std::addressof(this->m_value);
166 }
167 [[nodiscard]] constexpr T* operator->()
168 {
169 this->assert_value();
170 return std::addressof(this->m_value);
171 }
172
173 [[nodiscard]] constexpr T& operator*() &
174 {
175 this->assert_value();
176 return this->m_value;
177 }
178
179 [[nodiscard]] constexpr const T& operator*() const&
180 {
181 this->assert_value();
182 return this->m_value;
183 }
184
185 [[nodiscard]] constexpr T&& operator*() &&
186 {
187 this->assert_value();
188 return std::move(this->m_value);
189 }
190
191 [[nodiscard]] constexpr const T&& operator*() const&&
192 {
193 this->assert_value();
194 return std::move(this->m_value);
195 }
196
197 [[nodiscard]] constexpr bool has_value() const noexcept { return !EQ{}(this->m_value, SENTINEL); }
198 constexpr explicit operator bool() const noexcept { return has_value(); }
199
200 [[nodiscard]] constexpr const T& value() const& {
201 this->assert_value();
202 return this->m_value;
203 }
204 [[nodiscard]] constexpr T& value()& {
205 this->assert_value();
206 return this->m_value;
207 }
208 [[nodiscard]] constexpr T&& value()&& {
209 this->assert_value();
210 return std::move(this->m_value);
211 }
212 [[nodiscard]] constexpr const T&& value() const&& {
213 this->assert_value();
214 return std::move(this->m_value);
215 }
216
218 [[nodiscard]] constexpr const T& raw_value() const & {
219 return this->m_value;
220 }
221
222 [[nodiscard]] constexpr T& raw_value() & {
223 return this->m_value;
224 }
225
226 [[nodiscard]] constexpr T&& raw_value() && {
227 return std::move(this->m_value);
228 }
229
230 [[nodiscard]] constexpr const T&& raw_value() const && {
231 return std::move(this->m_value);
232 }
233
234 /*
235 template <class _Ty2>
236 [[nodiscard]] constexpr remove_cv_t<T> value_or(_Ty2&& _Right) const& {
237 static_assert(is_convertible_v<const T&, remove_cv_t<T>>,
238 "The const overload of optional<T>::value_or requires const T& to be convertible to remove_cv_t<T> "
239 "(N4950 [optional.observe]/15 as modified by LWG-3424).");
240 static_assert(is_convertible_v<_Ty2, T>,
241 "optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/15).");
242
243 if (this->has_value()) {
244 return static_cast<const T&>(this->m_value);
245 }
246
247 return static_cast<remove_cv_t<T>>(std::forward<_Ty2>(_Right));
248 }
249 template <class _Ty2>
250 [[nodiscard]] constexpr remove_cv_t<T> value_or(_Ty2&& _Right)&& {
251 static_assert(is_convertible_v<T, remove_cv_t<T>>,
252 "The rvalue overload of optional<T>::value_or requires T to be convertible to remove_cv_t<T> "
253 "(N4950 [optional.observe]/17 as modified by LWG-3424).");
254 static_assert(is_convertible_v<_Ty2, T>,
255 "optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/17).");
256
257 if (this->has_value()) {
258 return static_cast<T&&>(this->m_value);
259 }
260
261 return static_cast<remove_cv_t<T>>(std::forward<_Ty2>(_Right));
262 }
263 */
264
265 constexpr void reset() noexcept { this->m_value = SENTINEL; }
266
267 [[nodiscard]] friend constexpr bool operator==(const sentinel_optional& lhs, const sentinel_optional& rhs)
268 {
269 return lhs.has_value() != rhs.has_value() ? false : (lhs.has_value() == false ? true : lhs.m_value == rhs.m_value);
270 }
271 [[nodiscard]] friend constexpr bool operator!=(const sentinel_optional& lhs, const sentinel_optional& rhs)
272 {
273 return lhs.has_value() != rhs.has_value() ? true : (lhs.has_value() == false ? false : lhs.m_value != rhs.m_value);
274 }
275 [[nodiscard]] friend constexpr bool operator<(const sentinel_optional& lhs, const sentinel_optional& rhs)
276 {
277 return !rhs ? false : (!lhs ? true : *lhs < *rhs);
278 }
279 [[nodiscard]] friend constexpr bool operator<=(const sentinel_optional & lhs, const sentinel_optional & rhs)
280 {
281 return !lhs ? true : (!rhs ? false : *lhs <= *rhs);
282 }
283 [[nodiscard]] friend constexpr bool operator>(const sentinel_optional& lhs, const sentinel_optional & rhs)
284 {
285 return !lhs ? false : (!rhs ? true : *lhs > *rhs);
286 }
287 [[nodiscard]] friend constexpr bool operator>=(const sentinel_optional& lhs, const sentinel_optional& rhs)
288 {
289 return !rhs ? true : (!lhs ? false : *lhs >= *rhs);
290 }
291 [[nodiscard]] friend constexpr auto operator<=>(const sentinel_optional& lhs, const sentinel_optional& rhs)
292 {
293 return lhs && rhs ? *lhs <=> *rhs : lhs.has_value() <=> rhs.has_value();
294 }
295
296 [[nodiscard]] friend constexpr bool operator==(const sentinel_optional& opt, std::nullopt_t) { return !opt; }
297 [[nodiscard]] friend constexpr auto operator<=>(const sentinel_optional& opt, std::nullopt_t) { return opt.has_value() <=> false; }
298
299 private:
300
301 T m_value = SENTINEL;
302 };
303
304 template <class T, std::enable_if_t<std::is_constructible_v<std::decay_t<T>, T>, int> = 0>
305 [[nodiscard]] constexpr sentinel_optional<std::decay_t<T>> make_sentinel_optional(T&& value) noexcept(noexcept(sentinel_optional<std::decay_t<T>>{std::forward<T>(value)}))
306 {
307 return sentinel_optional<std::decay_t<T>>{std::forward<T>(value)};
308 }
309 template <class T, class... TYPES, std::enable_if_t<std::is_constructible_v<T, TYPES...>, int> = 0>
310 [[nodiscard]] constexpr sentinel_optional<T> make_optional(TYPES&&... args) noexcept(noexcept(sentinel_optional<T>{std::in_place, std::forward<TYPES>(args)...}))
311 {
312 return sentinel_optional<T>{std::in_place, std::forward<TYPES>(args)...};
313 }
314
316}
Primary namespace for everything in this library.
Definition align+rec2.h:10
An equivalent to std::optional<T> except that instead of keeping an additional bool to indicate value...
Definition soptional.h:56
constexpr const T && raw_value() const &&
Returns the value without asserting has_value().
Definition soptional.h:230
constexpr T && raw_value() &&
Returns the value without asserting has_value().
Definition soptional.h:226
constexpr const T & raw_value() const &
Returns the value without asserting has_value().
Definition soptional.h:218
constexpr T & raw_value() &
Returns the value without asserting has_value().
Definition soptional.h:222