ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
expected.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <exception>
9#include <functional>
10#include <system_error>
11#include <type_traits>
12#include <utility>
13#include <variant>
14
15#if defined(__has_include)
16# if __has_include(<version>)
17# include <version>
18# endif
19#endif
20
21#if defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
22# include <expected>
23# define THREADSCHEDULE_HAS_STD_EXPECTED 1
24#elif (defined(__cplusplus) && __cplusplus >= 202302L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L)
25# include <expected>
26# define THREADSCHEDULE_HAS_STD_EXPECTED 1
27#else
28# define THREADSCHEDULE_HAS_STD_EXPECTED 0
29#endif
30
31#ifdef __cpp_exceptions
32# define THREADSCHEDULE_EXPECTED_THROW(ex) throw ex
33#else
34# define THREADSCHEDULE_EXPECTED_THROW(ex) ::std::terminate()
35#endif
36
37namespace threadschedule
38{
39
41{
42 explicit unexpect_t() = default;
43};
44inline constexpr unexpect_t unexpect{};
45
46template <typename E>
48
50template <>
51class bad_expected_access<void> : public std::exception
52{
53public:
54 [[nodiscard]] auto
55 what() const noexcept -> char const* override
56 {
57 return "bad expected access";
58 }
59};
60
61template <typename E>
62class bad_expected_access : public bad_expected_access<void>
63{
64public:
65 explicit bad_expected_access(E error) : error_(std::move(error)) {}
66 [[nodiscard]] auto
67 error() const& noexcept -> E const&
68 {
69 return error_;
70 }
71 auto
72 error() & noexcept -> E&
73 {
74 return error_;
75 }
76 [[nodiscard]] auto
77 error() const&& noexcept -> E const&&
78 {
79 return std::move(error_);
80 }
81 auto
82 error() && noexcept -> E&&
83 {
84 return std::move(error_);
85 }
86
87private:
88 E error_;
89};
91
92template <typename E>
94{
95public:
96 constexpr explicit unexpected(E const& error) : error_(error) {}
97 constexpr explicit unexpected(E&& error) : error_(std::move(error)) {}
98 [[nodiscard]] constexpr auto
99 error() const& noexcept -> E const&
100 {
101 return error_;
102 }
103 constexpr auto
104 error() & noexcept -> E&
105 {
106 return error_;
107 }
108 [[nodiscard]] constexpr auto
109 error() const&& noexcept -> E const&&
110 {
111 return std::move(error_);
112 }
113 constexpr auto
114 error() && noexcept -> E&&
115 {
116 return std::move(error_);
117 }
118
119private:
120 E error_;
121};
122
123template <typename T, typename E = std::error_code>
124class expected;
125
126namespace detail
127{
129
130struct expected_value_tag
131{
132};
133struct expected_error_tag
134{
135};
136
137template <typename T>
138struct expected_value_holder
139{
140 template <typename... Args>
141 explicit constexpr expected_value_holder(std::in_place_t, Args&&... args) : value(std::forward<Args>(args)...)
142 {
143 }
144 T value;
145};
146
147template <typename E>
148struct expected_error_holder
149{
150 template <typename... Args>
151 explicit constexpr expected_error_holder(std::in_place_t, Args&&... args) : error(std::forward<Args>(args)...)
152 {
153 }
154 E error;
155};
156
157template <typename T, typename E>
158class expected_storage_common
159{
160public:
161 using value_holder = expected_value_holder<T>;
162 using error_holder = expected_error_holder<E>;
163
164 template <typename... Args>
165 explicit constexpr expected_storage_common(expected_value_tag, Args&&... args)
166 : data_(std::in_place_index<0>, std::in_place, std::forward<Args>(args)...)
167 {
168 }
169 template <typename... Args>
170 explicit constexpr expected_storage_common(expected_error_tag, Args&&... args)
171 : data_(std::in_place_index<1>, std::in_place, std::forward<Args>(args)...)
172 {
173 }
174
175 expected_storage_common(expected_storage_common const&) = default;
176 expected_storage_common(expected_storage_common&&) = default;
177 auto operator=(expected_storage_common const&) -> expected_storage_common& = default;
178 auto operator=(expected_storage_common&&) -> expected_storage_common& = default;
179 ~expected_storage_common() = default;
180
181 [[nodiscard]] constexpr auto
182 has_value() const noexcept -> bool
183 {
184 return data_.index() == 0;
185 }
186 constexpr auto
187 value() & noexcept -> T&
188 {
189 return std::get<0>(data_).value;
190 }
191 [[nodiscard]] constexpr auto
192 value() const& noexcept -> T const&
193 {
194 return std::get<0>(data_).value;
195 }
196 constexpr auto
197 value() && noexcept -> T&&
198 {
199 return std::move(std::get<0>(data_).value);
200 }
201 [[nodiscard]] constexpr auto
202 value() const&& noexcept -> T const&&
203 {
204 return std::move(std::get<0>(data_).value);
205 }
206 constexpr auto
207 error() & noexcept -> E&
208 {
209 return std::get<1>(data_).error;
210 }
211 [[nodiscard]] constexpr auto
212 error() const& noexcept -> E const&
213 {
214 return std::get<1>(data_).error;
215 }
216 constexpr auto
217 error() && noexcept -> E&&
218 {
219 return std::move(std::get<1>(data_).error);
220 }
221 [[nodiscard]] constexpr auto
222 error() const&& noexcept -> E const&&
223 {
224 return std::move(std::get<1>(data_).error);
225 }
226
227 template <typename U>
228 void
229 assign_value(U&& source)
230 {
231 if (has_value())
232 value() = std::forward<U>(source);
233 else
234 replace_with_value(std::forward<U>(source));
235 }
236
237 template <typename G>
238 void
239 assign_error(G&& source)
240 {
241 if (has_value())
242 replace_with_error(std::forward<G>(source));
243 else
244 error() = std::forward<G>(source);
245 }
246
247 template <typename... Args>
248 auto
249 emplace_value(Args&&... args) noexcept -> T&
250 {
251 data_.template emplace<0>(std::in_place, std::forward<Args>(args)...);
252 return value();
253 }
254
255 void
256 copy_assign(expected_storage_common const& other)
257 {
258 if (has_value() && other.has_value())
259 value() = other.value();
260 else if (!has_value() && !other.has_value())
261 error() = other.error();
262 else if (other.has_value())
263 replace_with_value(other.value());
264 else
265 replace_with_error(other.error());
266 }
267
268 void
269 move_assign(expected_storage_common&& other)
270 {
271 if (has_value() && other.has_value())
272 value() = std::move(other).value();
273 else if (!has_value() && !other.has_value())
274 error() = std::move(other).error();
275 else if (other.has_value())
276 replace_with_value(std::move(other).value());
277 else
278 replace_with_error(std::move(other).error());
279 }
280
281 void
282 swap_storage(expected_storage_common& other) noexcept(std::is_nothrow_move_constructible_v<T>
283 && std::is_nothrow_move_constructible_v<E>
284 && std::is_nothrow_swappable_v<T>
285 && std::is_nothrow_swappable_v<E>)
286 {
287 if (has_value() && other.has_value())
288 {
289 using std::swap;
290 swap(value(), other.value());
291 }
292 else if (!has_value() && !other.has_value())
293 {
294 using std::swap;
295 swap(error(), other.error());
296 }
297 else if (has_value())
298 swap_value_error(*this, other);
299 else
300 swap_value_error(other, *this);
301 }
302
303private:
304 template <typename U>
305 void
306 replace_with_value(U&& source)
307 {
308 if constexpr (std::is_nothrow_constructible_v<T, U>)
309 data_.template emplace<0>(std::in_place, std::forward<U>(source));
310 else if constexpr (std::is_nothrow_move_constructible_v<T>)
311 {
312 T replacement(std::forward<U>(source));
313 data_.template emplace<0>(std::in_place, std::move(replacement));
314 }
315 else
316 {
317 static_assert(std::is_nothrow_move_constructible_v<E>);
318 E backup(std::move(error()));
319#ifdef __cpp_exceptions
320 try
321 {
322 data_.template emplace<0>(std::in_place, std::forward<U>(source));
323 }
324 catch (...)
325 {
326 data_.template emplace<1>(std::in_place, std::move(backup));
327 throw;
328 }
329#else
330 (void)backup;
331 data_.template emplace<0>(std::in_place, std::forward<U>(source));
332#endif
333 }
334 }
335
336 template <typename G>
337 void
338 replace_with_error(G&& source)
339 {
340 if constexpr (std::is_nothrow_constructible_v<E, G>)
341 data_.template emplace<1>(std::in_place, std::forward<G>(source));
342 else if constexpr (std::is_nothrow_move_constructible_v<E>)
343 {
344 E replacement(std::forward<G>(source));
345 data_.template emplace<1>(std::in_place, std::move(replacement));
346 }
347 else
348 {
349 static_assert(std::is_nothrow_move_constructible_v<T>);
350 T backup(std::move(value()));
351#ifdef __cpp_exceptions
352 try
353 {
354 data_.template emplace<1>(std::in_place, std::forward<G>(source));
355 }
356 catch (...)
357 {
358 data_.template emplace<0>(std::in_place, std::move(backup));
359 throw;
360 }
361#else
362 (void)backup;
363 data_.template emplace<1>(std::in_place, std::forward<G>(source));
364#endif
365 }
366 }
367
368 static void
369 swap_value_error(expected_storage_common& with_value, expected_storage_common& with_error)
370 {
371 if constexpr (std::is_nothrow_move_constructible_v<T>)
372 {
373 T backup(std::move(with_value.value()));
374#ifdef __cpp_exceptions
375 try
376 {
377 with_value.data_.template emplace<1>(std::in_place, std::move(with_error.error()));
378 }
379 catch (...)
380 {
381 with_value.data_.template emplace<0>(std::in_place, std::move(backup));
382 throw;
383 }
384#else
385 with_value.data_.template emplace<1>(std::in_place, std::move(with_error.error()));
386#endif
387 with_error.data_.template emplace<0>(std::in_place, std::move(backup));
388 }
389 else
390 {
391 static_assert(std::is_nothrow_move_constructible_v<E>);
392 E backup(std::move(with_error.error()));
393#ifdef __cpp_exceptions
394 try
395 {
396 with_error.data_.template emplace<0>(std::in_place, std::move(with_value.value()));
397 }
398 catch (...)
399 {
400 with_error.data_.template emplace<1>(std::in_place, std::move(backup));
401 throw;
402 }
403#else
404 with_error.data_.template emplace<0>(std::in_place, std::move(with_value.value()));
405#endif
406 with_value.data_.template emplace<1>(std::in_place, std::move(backup));
407 }
408 }
409
410 std::variant<value_holder, error_holder> data_;
411};
412
413template <typename T, typename E>
414inline constexpr bool expected_copy_assignable_v
415 = std::is_copy_constructible_v<T> && std::is_copy_constructible_v<E> && std::is_copy_assignable_v<T>
416 && std::is_copy_assignable_v<E>
417 && (std::is_nothrow_move_constructible_v<T> || std::is_nothrow_move_constructible_v<E>);
418template <typename T, typename E>
419inline constexpr bool expected_move_assignable_v
420 = std::is_move_constructible_v<T> && std::is_move_constructible_v<E> && std::is_move_assignable_v<T>
421 && std::is_move_assignable_v<E>
422 && (std::is_nothrow_move_constructible_v<T> || std::is_nothrow_move_constructible_v<E>);
423
424template <typename T, typename E, bool Copy = expected_copy_assignable_v<T, E>,
425 bool Move = expected_move_assignable_v<T, E>>
426class expected_storage;
427
428template <typename T, typename E>
429class expected_storage<T, E, true, true> : public expected_storage_common<T, E>
430{
431 using base = expected_storage_common<T, E>;
432
433public:
434 using base::base;
435 expected_storage(expected_storage const&) = default;
436 expected_storage(expected_storage&&) = default;
437 auto
438 operator=(expected_storage const& other) -> expected_storage&
439 {
440 if (this != &other)
441 this->copy_assign(other);
442 return *this;
443 }
444 auto
445 operator=(expected_storage&& other) noexcept(std::is_nothrow_move_constructible_v<T>
446 && std::is_nothrow_move_constructible_v<E>
447 && std::is_nothrow_move_assignable_v<T>
448 && std::is_nothrow_move_assignable_v<E>) -> expected_storage&
449 {
450 if (this != &other)
451 this->move_assign(std::move(other));
452 return *this;
453 }
454};
455
456template <typename T, typename E>
457class expected_storage<T, E, true, false> : public expected_storage_common<T, E>
458{
459 using base = expected_storage_common<T, E>;
460
461public:
462 using base::base;
463 expected_storage(expected_storage const&) = default;
464 expected_storage(expected_storage&&) = default;
465 auto
466 operator=(expected_storage const& other) -> expected_storage&
467 {
468 if (this != &other)
469 this->copy_assign(other);
470 return *this;
471 }
472 auto operator=(expected_storage&&) -> expected_storage& = delete;
473};
474
475template <typename T, typename E>
476class expected_storage<T, E, false, true> : public expected_storage_common<T, E>
477{
478 using base = expected_storage_common<T, E>;
479
480public:
481 using base::base;
482 expected_storage(expected_storage const&) = default;
483 expected_storage(expected_storage&&) = default;
484 auto operator=(expected_storage const&) -> expected_storage& = delete;
485 auto
486 operator=(expected_storage&& other) noexcept(std::is_nothrow_move_constructible_v<T>
487 && std::is_nothrow_move_constructible_v<E>
488 && std::is_nothrow_move_assignable_v<T>
489 && std::is_nothrow_move_assignable_v<E>) -> expected_storage&
490 {
491 if (this != &other)
492 this->move_assign(std::move(other));
493 return *this;
494 }
495};
496
497template <typename T, typename E>
498class expected_storage<T, E, false, false> : public expected_storage_common<T, E>
499{
500 using base = expected_storage_common<T, E>;
501
502public:
503 using base::base;
504 expected_storage(expected_storage const&) = default;
505 expected_storage(expected_storage&&) = default;
506 auto operator=(expected_storage const&) -> expected_storage& = delete;
507 auto operator=(expected_storage&&) -> expected_storage& = delete;
508};
509
511} // namespace detail
512
513template <typename T, typename E>
515{
516public:
517 using value_type = T;
518 using error_type = E;
520
521 template <typename U = T, std::enable_if_t<std::is_default_constructible_v<U>, int> = 0>
522 constexpr expected() : storage_(detail::expected_value_tag{})
523 {
524 }
525 template <typename U = T, std::enable_if_t<!std::is_default_constructible_v<U>, int> = 0>
526 expected() = delete;
527
528 expected(expected const&) = default;
529 expected(expected&&) = default;
530 auto operator=(expected const&) -> expected& = default;
531 auto operator=(expected&&) -> expected& = default;
532 ~expected() = default;
533
534 template <typename U = T, std::enable_if_t<!std::is_same_v<std::decay_t<U>, expected>
535 && !std::is_same_v<std::decay_t<U>, std::in_place_t>
536 && !std::is_same_v<std::decay_t<U>, unexpected<E>>
537 && std::is_constructible_v<T, U> && std::is_convertible_v<U, T>,
538 int> = 0>
539 constexpr expected(U&& value) : storage_(detail::expected_value_tag{}, std::forward<U>(value))
540 {
541 }
542 template <typename U = T, std::enable_if_t<!std::is_same_v<std::decay_t<U>, expected>
543 && !std::is_same_v<std::decay_t<U>, std::in_place_t>
544 && !std::is_same_v<std::decay_t<U>, unexpected<E>>
545 && std::is_constructible_v<T, U> && !std::is_convertible_v<U, T>,
546 int> = 0>
547 constexpr explicit expected(U&& value) : storage_(detail::expected_value_tag{}, std::forward<U>(value))
548 {
549 }
550 template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args...>, int> = 0>
551 constexpr explicit expected(std::in_place_t, Args&&... args)
552 : storage_(detail::expected_value_tag{}, std::forward<Args>(args)...)
553 {
554 }
555 constexpr expected(unexpected<E> const& error) : storage_(detail::expected_error_tag{}, error.error()) {}
556 constexpr expected(unexpected<E>&& error) : storage_(detail::expected_error_tag{}, std::move(error).error()) {}
557 template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args...>, int> = 0>
558 constexpr explicit expected(unexpect_t, Args&&... args)
559 : storage_(detail::expected_error_tag{}, std::forward<Args>(args)...)
560 {
561 }
562
563 template <typename U = T,
564 std::enable_if_t<!std::is_same_v<std::decay_t<U>, expected> && std::is_constructible_v<T, U>
565 && std::is_assignable_v<T&, U>
566 && (std::is_nothrow_constructible_v<T, U> || std::is_nothrow_move_constructible_v<T>
567 || std::is_nothrow_move_constructible_v<E>),
568 int> = 0>
569 auto
570 operator=(U&& value) -> expected&
571 {
572 storage_.assign_value(std::forward<U>(value));
573 return *this;
574 }
575 template <typename G, std::enable_if_t<std::is_constructible_v<E, G const&> && std::is_assignable_v<E&, G const&>
576 && (std::is_nothrow_constructible_v<E, G const&>
577 || std::is_nothrow_move_constructible_v<E>
578 || std::is_nothrow_move_constructible_v<T>),
579 int> = 0>
580 auto
582 {
583 storage_.assign_error(error.error());
584 return *this;
585 }
586 template <typename G,
587 std::enable_if_t<std::is_constructible_v<E, G> && std::is_assignable_v<E&, G>
588 && (std::is_nothrow_constructible_v<E, G> || std::is_nothrow_move_constructible_v<E>
589 || std::is_nothrow_move_constructible_v<T>),
590 int> = 0>
591 auto
592 operator=(unexpected<G>&& error) -> expected&
593 {
594 storage_.assign_error(std::move(error).error());
595 return *this;
596 }
597
598 [[nodiscard]] constexpr auto
599 has_value() const noexcept -> bool
600 {
601 return storage_.has_value();
602 }
603 constexpr explicit
604 operator bool() const noexcept
605 {
606 return has_value();
607 }
608 constexpr auto
609 operator->() noexcept -> T*
610 {
611 return &storage_.value();
612 }
613 constexpr auto
614 operator->() const noexcept -> T const*
615 {
616 return &storage_.value();
617 }
618 constexpr auto
619 operator*() & noexcept -> T&
620 {
621 return storage_.value();
622 }
623 [[nodiscard]] constexpr auto
624 operator*() const& noexcept -> T const&
625 {
626 return storage_.value();
627 }
628 constexpr auto
629 operator*() && noexcept -> T&&
630 {
631 return std::move(storage_).value();
632 }
633 [[nodiscard]] constexpr auto
634 operator*() const&& noexcept -> T const&&
635 {
636 return std::move(storage_).value();
637 }
638
639 auto
640 value() & -> T&
641 {
642 if (!has_value())
644 return storage_.value();
645 }
646 [[nodiscard]] auto
647 value() const& -> T const&
648 {
649 if (!has_value())
651 return storage_.value();
652 }
653 auto
654 value() && -> T&&
655 {
656 if (!has_value())
658 return std::move(storage_).value();
659 }
660 [[nodiscard]] auto
661 value() const&& -> T const&&
662 {
663 if (!has_value())
665 return std::move(storage_).value();
666 }
667 constexpr auto
668 error() & noexcept -> E&
669 {
670 return storage_.error();
671 }
672 [[nodiscard]] constexpr auto
673 error() const& noexcept -> E const&
674 {
675 return storage_.error();
676 }
677 constexpr auto
678 error() && noexcept -> E&&
679 {
680 return std::move(storage_).error();
681 }
682 [[nodiscard]] constexpr auto
683 error() const&& noexcept -> E const&&
684 {
685 return std::move(storage_).error();
686 }
687
688 template <typename U>
689 auto
690 value_or(U&& fallback) const& -> T
691 {
692 return has_value() ? storage_.value() : static_cast<T>(std::forward<U>(fallback));
693 }
694 template <typename U>
695 auto
696 value_or(U&& fallback) && -> T
697 {
698 return has_value() ? std::move(storage_).value() : static_cast<T>(std::forward<U>(fallback));
699 }
700 template <typename... Args, std::enable_if_t<std::is_nothrow_constructible_v<T, Args...>, int> = 0>
701 auto
702 emplace(Args&&... args) noexcept -> T&
703 {
704 return storage_.emplace_value(std::forward<Args>(args)...);
705 }
706 void
707 swap(expected& other) noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_constructible_v<E>
708 && std::is_nothrow_swappable_v<T> && std::is_nothrow_swappable_v<E>)
709 {
710 storage_.swap_storage(other.storage_);
711 }
712
713 template <typename F>
714 auto
715 and_then(F&& f) &
716 {
717 return and_then_impl(*this, std::forward<F>(f));
718 }
719 template <typename F>
720 auto
721 and_then(F&& f) const&
722 {
723 return and_then_impl(*this, std::forward<F>(f));
724 }
725 template <typename F>
726 auto
727 and_then(F&& f) &&
728 {
729 return and_then_impl(std::move(*this), std::forward<F>(f));
730 }
731 template <typename F>
732 auto
733 and_then(F&& f) const&&
734 {
735 return and_then_impl(std::move(*this), std::forward<F>(f));
736 }
737 template <typename F>
738 auto
739 or_else(F&& f) &
740 {
741 return or_else_impl(*this, std::forward<F>(f));
742 }
743 template <typename F>
744 auto
745 or_else(F&& f) const&
746 {
747 return or_else_impl(*this, std::forward<F>(f));
748 }
749 template <typename F>
750 auto
751 or_else(F&& f) &&
752 {
753 return or_else_impl(std::move(*this), std::forward<F>(f));
754 }
755 template <typename F>
756 auto
757 or_else(F&& f) const&&
758 {
759 return or_else_impl(std::move(*this), std::forward<F>(f));
760 }
761 template <typename F>
762 auto
763 transform(F&& f) &
764 {
765 return transform_impl(*this, std::forward<F>(f));
766 }
767 template <typename F>
768 auto
769 transform(F&& f) const&
770 {
771 return transform_impl(*this, std::forward<F>(f));
772 }
773 template <typename F>
774 auto
775 transform(F&& f) &&
776 {
777 return transform_impl(std::move(*this), std::forward<F>(f));
778 }
779 template <typename F>
780 auto
781 transform(F&& f) const&&
782 {
783 return transform_impl(std::move(*this), std::forward<F>(f));
784 }
785 template <typename F>
786 auto
788 {
789 return transform_error_impl(*this, std::forward<F>(f));
790 }
791 template <typename F>
792 auto
793 transform_error(F&& f) const&
794 {
795 return transform_error_impl(*this, std::forward<F>(f));
796 }
797 template <typename F>
798 auto
800 {
801 return transform_error_impl(std::move(*this), std::forward<F>(f));
802 }
803 template <typename F>
804 auto
805 transform_error(F&& f) const&&
806 {
807 return transform_error_impl(std::move(*this), std::forward<F>(f));
808 }
809
810#if THREADSCHEDULE_HAS_STD_EXPECTED
811 template <typename U = T, typename G = E,
812 std::enable_if_t<std::is_copy_constructible_v<U> && std::is_copy_constructible_v<G>, int> = 0>
813 operator std::expected<T, E>() const&
814 {
815 if (has_value())
816 return std::expected<T, E>(std::in_place, storage_.value());
817 return std::expected<T, E>(std::unexpect, storage_.error());
818 }
819 template <typename U = T, typename G = E,
820 std::enable_if_t<std::is_move_constructible_v<U> && std::is_move_constructible_v<G>, int> = 0>
821 operator std::expected<T, E>() &&
822 {
823 if (has_value())
824 return std::expected<T, E>(std::in_place, std::move(storage_).value());
825 return std::expected<T, E>(std::unexpect, std::move(storage_).error());
826 }
827#endif
828
829 template <typename T2, typename E2>
830 friend auto
831 operator==(expected const& lhs, expected<T2, E2> const& rhs) -> bool
832 {
833 if (lhs.has_value() != rhs.has_value())
834 return false;
835 return lhs.has_value() ? *lhs == *rhs : lhs.error() == rhs.error();
836 }
837 template <typename T2, typename E2>
838 friend auto
839 operator!=(expected const& lhs, expected<T2, E2> const& rhs) -> bool
840 {
841 return !(lhs == rhs);
842 }
843 template <typename T2, std::enable_if_t<!std::is_same_v<expected, std::decay_t<T2>>, int> = 0>
844 friend auto
845 operator==(expected const& lhs, T2 const& rhs) -> bool
846 {
847 return lhs.has_value() && *lhs == rhs;
848 }
849 template <typename T2, std::enable_if_t<!std::is_same_v<expected, std::decay_t<T2>>, int> = 0>
850 friend auto
851 operator==(T2 const& lhs, expected const& rhs) -> bool
852 {
853 return rhs.has_value() && lhs == *rhs;
854 }
855 template <typename T2, std::enable_if_t<!std::is_same_v<expected, std::decay_t<T2>>, int> = 0>
856 friend auto
857 operator!=(expected const& lhs, T2 const& rhs) -> bool
858 {
859 return !(lhs == rhs);
860 }
861 template <typename T2, std::enable_if_t<!std::is_same_v<expected, std::decay_t<T2>>, int> = 0>
862 friend auto
863 operator!=(T2 const& lhs, expected const& rhs) -> bool
864 {
865 return !(lhs == rhs);
866 }
867 template <typename E2>
868 friend auto
869 operator==(expected const& lhs, unexpected<E2> const& rhs) -> bool
870 {
871 return !lhs.has_value() && lhs.error() == rhs.error();
872 }
873 template <typename E2>
874 friend auto
875 operator==(unexpected<E2> const& lhs, expected const& rhs) -> bool
876 {
877 return rhs == lhs;
878 }
879 template <typename E2>
880 friend auto
881 operator!=(expected const& lhs, unexpected<E2> const& rhs) -> bool
882 {
883 return !(lhs == rhs);
884 }
885 template <typename E2>
886 friend auto
887 operator!=(unexpected<E2> const& lhs, expected const& rhs) -> bool
888 {
889 return !(rhs == lhs);
890 }
891
892private:
893 template <typename Self, typename F>
894 static auto
895 and_then_impl(Self&& self, F&& f)
896 {
897 using result_type = std::invoke_result_t<F, decltype(*std::forward<Self>(self))>;
898 if (self.has_value())
899 return std::invoke(std::forward<F>(f), *std::forward<Self>(self));
900 return result_type(unexpect, std::forward<Self>(self).error());
901 }
902 template <typename Self, typename F>
903 static auto
904 or_else_impl(Self&& self, F&& f)
905 {
906 using result_type = std::invoke_result_t<F, decltype(std::forward<Self>(self).error())>;
907 if (self.has_value())
908 return result_type(*std::forward<Self>(self));
909 return std::invoke(std::forward<F>(f), std::forward<Self>(self).error());
910 }
911 template <typename Self, typename F>
912 static auto
913 transform_impl(Self&& self, F&& f)
914 {
915 using result_value = std::remove_cv_t<std::invoke_result_t<F, decltype(*std::forward<Self>(self))>>;
916 if (self.has_value())
917 {
918 if constexpr (std::is_void_v<result_value>)
919 {
920 std::invoke(std::forward<F>(f), *std::forward<Self>(self));
921 return expected<void, E>();
922 }
923 else
924 return expected<result_value, E>(std::in_place, std::invoke(std::forward<F>(f), *std::forward<Self>(self)));
925 }
926 return expected<result_value, E>(unexpect, std::forward<Self>(self).error());
927 }
928 template <typename Self, typename F>
929 static auto
930 transform_error_impl(Self&& self, F&& f)
931 {
932 using result_error = std::remove_cv_t<std::invoke_result_t<F, decltype(std::forward<Self>(self).error())>>;
933 if (self.has_value())
934 return expected<T, result_error>(*std::forward<Self>(self));
935 return expected<T, result_error>(unexpect, std::invoke(std::forward<F>(f), std::forward<Self>(self).error()));
936 }
937
938 detail::expected_storage<T, E> storage_;
939};
940
941template <typename E>
942class expected<void, E>
943{
944 using storage_type = detail::expected_storage<std::monostate, E>;
945
946public:
947 using value_type = void;
948 using error_type = E;
950
951 constexpr expected() : storage_(detail::expected_value_tag{}) {}
952 expected(expected const&) = default;
953 expected(expected&&) = default;
954 auto operator=(expected const&) -> expected& = default;
955 auto operator=(expected&&) -> expected& = default;
956 ~expected() = default;
957
958 template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args...>, int> = 0>
959 constexpr explicit expected(unexpect_t, Args&&... args)
960 : storage_(detail::expected_error_tag{}, std::forward<Args>(args)...)
961 {
962 }
963 constexpr expected(unexpected<E> const& error) : storage_(detail::expected_error_tag{}, error.error()) {}
964 constexpr expected(unexpected<E>&& error) : storage_(detail::expected_error_tag{}, std::move(error).error()) {}
965
966 template <typename G, std::enable_if_t<std::is_constructible_v<E, G const&> && std::is_assignable_v<E&, G const&>
967 && (std::is_nothrow_constructible_v<E, G const&>
968 || std::is_nothrow_move_constructible_v<E>),
969 int> = 0>
970 auto
972 {
973 storage_.assign_error(error.error());
974 return *this;
975 }
976 template <typename G,
977 std::enable_if_t<std::is_constructible_v<E, G> && std::is_assignable_v<E&, G>
978 && (std::is_nothrow_constructible_v<E, G> || std::is_nothrow_move_constructible_v<E>),
979 int> = 0>
980 auto
981 operator=(unexpected<G>&& error) -> expected&
982 {
983 storage_.assign_error(std::move(error).error());
984 return *this;
985 }
986
987 [[nodiscard]] constexpr auto
988 has_value() const noexcept -> bool
989 {
990 return storage_.has_value();
991 }
992 constexpr explicit
993 operator bool() const noexcept
994 {
995 return has_value();
996 }
997 void
998 value() const
999 {
1000 if (!has_value())
1002 }
1003 constexpr auto
1004 error() & noexcept -> E&
1005 {
1006 return storage_.error();
1007 }
1008 [[nodiscard]] constexpr auto
1009 error() const& noexcept -> E const&
1010 {
1011 return storage_.error();
1012 }
1013 constexpr auto
1014 error() && noexcept -> E&&
1015 {
1016 return std::move(storage_).error();
1017 }
1018 [[nodiscard]] constexpr auto
1019 error() const&& noexcept -> E const&&
1020 {
1021 return std::move(storage_).error();
1022 }
1023 void
1024 emplace() noexcept
1025 {
1026 storage_.emplace_value();
1027 }
1028 void
1029 swap(expected& other) noexcept(std::is_nothrow_move_constructible_v<E> && std::is_nothrow_swappable_v<E>)
1030 {
1031 storage_.swap_storage(other.storage_);
1032 }
1033
1034 template <typename F>
1035 auto
1036 and_then(F&& f) &
1037 {
1038 return and_then_impl(*this, std::forward<F>(f));
1039 }
1040 template <typename F>
1041 auto
1042 and_then(F&& f) const&
1043 {
1044 return and_then_impl(*this, std::forward<F>(f));
1045 }
1046 template <typename F>
1047 auto
1048 and_then(F&& f) &&
1049 {
1050 return and_then_impl(std::move(*this), std::forward<F>(f));
1051 }
1052 template <typename F>
1053 auto
1054 and_then(F&& f) const&&
1055 {
1056 return and_then_impl(std::move(*this), std::forward<F>(f));
1057 }
1058 template <typename F>
1059 auto
1060 or_else(F&& f) &
1061 {
1062 return or_else_impl(*this, std::forward<F>(f));
1063 }
1064 template <typename F>
1065 auto
1066 or_else(F&& f) const&
1067 {
1068 return or_else_impl(*this, std::forward<F>(f));
1069 }
1070 template <typename F>
1071 auto
1072 or_else(F&& f) &&
1073 {
1074 return or_else_impl(std::move(*this), std::forward<F>(f));
1075 }
1076 template <typename F>
1077 auto
1078 or_else(F&& f) const&&
1079 {
1080 return or_else_impl(std::move(*this), std::forward<F>(f));
1081 }
1082 template <typename F>
1083 auto
1084 transform(F&& f) &
1085 {
1086 return transform_impl(*this, std::forward<F>(f));
1087 }
1088 template <typename F>
1089 auto
1090 transform(F&& f) const&
1091 {
1092 return transform_impl(*this, std::forward<F>(f));
1093 }
1094 template <typename F>
1095 auto
1096 transform(F&& f) &&
1097 {
1098 return transform_impl(std::move(*this), std::forward<F>(f));
1099 }
1100 template <typename F>
1101 auto
1102 transform(F&& f) const&&
1103 {
1104 return transform_impl(std::move(*this), std::forward<F>(f));
1105 }
1106 template <typename F>
1107 auto
1109 {
1110 return transform_error_impl(*this, std::forward<F>(f));
1111 }
1112 template <typename F>
1113 auto
1114 transform_error(F&& f) const&
1115 {
1116 return transform_error_impl(*this, std::forward<F>(f));
1117 }
1118 template <typename F>
1119 auto
1121 {
1122 return transform_error_impl(std::move(*this), std::forward<F>(f));
1123 }
1124 template <typename F>
1125 auto
1126 transform_error(F&& f) const&&
1127 {
1128 return transform_error_impl(std::move(*this), std::forward<F>(f));
1129 }
1130
1131#if THREADSCHEDULE_HAS_STD_EXPECTED
1132 template <typename G = E, std::enable_if_t<std::is_copy_constructible_v<G>, int> = 0>
1133 operator std::expected<void, E>() const&
1134 {
1135 if (has_value())
1136 return std::expected<void, E>();
1137 return std::expected<void, E>(std::unexpect, storage_.error());
1138 }
1139 template <typename G = E, std::enable_if_t<std::is_move_constructible_v<G>, int> = 0>
1140 operator std::expected<void, E>() &&
1141 {
1142 if (has_value())
1143 return std::expected<void, E>();
1144 return std::expected<void, E>(std::unexpect, std::move(storage_).error());
1145 }
1146#endif
1147
1148 template <typename E2>
1149 friend auto
1150 operator==(expected const& lhs, expected<void, E2> const& rhs) -> bool
1151 {
1152 if (lhs.has_value() != rhs.has_value())
1153 return false;
1154 return lhs.has_value() || lhs.error() == rhs.error();
1155 }
1156 template <typename E2>
1157 friend auto
1158 operator!=(expected const& lhs, expected<void, E2> const& rhs) -> bool
1159 {
1160 return !(lhs == rhs);
1161 }
1162 template <typename E2>
1163 friend auto
1164 operator==(expected const& lhs, unexpected<E2> const& rhs) -> bool
1165 {
1166 return !lhs.has_value() && lhs.error() == rhs.error();
1167 }
1168 template <typename E2>
1169 friend auto
1170 operator==(unexpected<E2> const& lhs, expected const& rhs) -> bool
1171 {
1172 return rhs == lhs;
1173 }
1174 template <typename E2>
1175 friend auto
1176 operator!=(expected const& lhs, unexpected<E2> const& rhs) -> bool
1177 {
1178 return !(lhs == rhs);
1179 }
1180 template <typename E2>
1181 friend auto
1182 operator!=(unexpected<E2> const& lhs, expected const& rhs) -> bool
1183 {
1184 return !(rhs == lhs);
1185 }
1186
1187private:
1188 template <typename Self, typename F>
1189 static auto
1190 and_then_impl(Self&& self, F&& f)
1191 {
1192 using result_type = std::invoke_result_t<F>;
1193 if (self.has_value())
1194 return std::invoke(std::forward<F>(f));
1195 return result_type(unexpect, std::forward<Self>(self).error());
1196 }
1197 template <typename Self, typename F>
1198 static auto
1199 or_else_impl(Self&& self, F&& f)
1200 {
1201 using result_type = std::invoke_result_t<F, decltype(std::forward<Self>(self).error())>;
1202 if (self.has_value())
1203 return result_type();
1204 return std::invoke(std::forward<F>(f), std::forward<Self>(self).error());
1205 }
1206 template <typename Self, typename F>
1207 static auto
1208 transform_impl(Self&& self, F&& f)
1209 {
1210 using result_value = std::remove_cv_t<std::invoke_result_t<F>>;
1211 if (self.has_value())
1212 {
1213 if constexpr (std::is_void_v<result_value>)
1214 {
1215 std::invoke(std::forward<F>(f));
1216 return expected<void, E>();
1217 }
1218 else
1219 return expected<result_value, E>(std::in_place, std::invoke(std::forward<F>(f)));
1220 }
1221 return expected<result_value, E>(unexpect, std::forward<Self>(self).error());
1222 }
1223 template <typename Self, typename F>
1224 static auto
1225 transform_error_impl(Self&& self, F&& f)
1226 {
1227 using result_error = std::remove_cv_t<std::invoke_result_t<F, decltype(std::forward<Self>(self).error())>>;
1228 if (self.has_value())
1229 return expected<void, result_error>();
1230 return expected<void, result_error>(unexpect, std::invoke(std::forward<F>(f), std::forward<Self>(self).error()));
1231 }
1232
1233 storage_type storage_;
1234};
1235
1236template <typename T, typename E>
1237void
1238swap(expected<T, E>& lhs, expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs)))
1239{
1240 lhs.swap(rhs);
1241}
1242
1243} // namespace threadschedule
1244
1245#undef THREADSCHEDULE_EXPECTED_THROW
constexpr expected(unexpect_t, Args &&... args)
Definition expected.hpp:959
void swap(expected &other) noexcept(std::is_nothrow_move_constructible_v< E > &&std::is_nothrow_swappable_v< E >)
friend auto operator==(unexpected< E2 > const &lhs, expected const &rhs) -> bool
auto operator=(expected &&) -> expected &=default
constexpr expected(unexpected< E > const &error)
Definition expected.hpp:963
auto operator=(expected const &) -> expected &=default
constexpr auto has_value() const noexcept -> bool
Definition expected.hpp:988
friend auto operator!=(unexpected< E2 > const &lhs, expected const &rhs) -> bool
constexpr auto error() &&noexcept -> E &&
auto transform_error(F &&f) const &&
constexpr auto error() const &&noexcept -> E const &&
friend auto operator!=(expected const &lhs, expected< void, E2 > const &rhs) -> bool
auto operator=(unexpected< G > const &error) -> expected &
Definition expected.hpp:971
constexpr auto error() const &noexcept -> E const &
expected(expected const &)=default
friend auto operator==(expected const &lhs, unexpected< E2 > const &rhs) -> bool
constexpr expected(unexpected< E > &&error)
Definition expected.hpp:964
auto transform_error(F &&f) const &
friend auto operator==(expected const &lhs, expected< void, E2 > const &rhs) -> bool
friend auto operator!=(expected const &lhs, unexpected< E2 > const &rhs) -> bool
constexpr auto error() &noexcept -> E &
constexpr expected(unexpected< E > &&error)
Definition expected.hpp:556
auto and_then(F &&f) const &&
Definition expected.hpp:733
constexpr expected(unexpected< E > const &error)
Definition expected.hpp:555
friend auto operator!=(expected const &lhs, T2 const &rhs) -> bool
Definition expected.hpp:857
auto value() &&-> T &&
Definition expected.hpp:654
auto emplace(Args &&... args) noexcept -> T &
Definition expected.hpp:702
constexpr auto error() const &noexcept -> E const &
Definition expected.hpp:673
constexpr auto operator*() const &&noexcept -> T const &&
Definition expected.hpp:634
auto value() const &&-> T const &&
Definition expected.hpp:661
friend auto operator==(T2 const &lhs, expected const &rhs) -> bool
Definition expected.hpp:851
constexpr expected(unexpect_t, Args &&... args)
Definition expected.hpp:558
friend auto operator!=(expected const &lhs, expected< T2, E2 > const &rhs) -> bool
Definition expected.hpp:839
constexpr auto operator*() &noexcept -> T &
Definition expected.hpp:619
void swap(expected &other) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_constructible_v< E > &&std::is_nothrow_swappable_v< T > &&std::is_nothrow_swappable_v< E >)
Definition expected.hpp:707
friend auto operator==(unexpected< E2 > const &lhs, expected const &rhs) -> bool
Definition expected.hpp:875
auto transform(F &&f) const &&
Definition expected.hpp:781
auto or_else(F &&f) const &&
Definition expected.hpp:757
constexpr auto operator->() const noexcept -> T const *
Definition expected.hpp:614
friend auto operator!=(T2 const &lhs, expected const &rhs) -> bool
Definition expected.hpp:863
auto value() const &-> T const &
Definition expected.hpp:647
auto operator=(expected const &) -> expected &=default
friend auto operator!=(unexpected< E2 > const &lhs, expected const &rhs) -> bool
Definition expected.hpp:887
constexpr auto operator->() noexcept -> T *
Definition expected.hpp:609
auto transform(F &&f) &
Definition expected.hpp:763
auto operator=(expected &&) -> expected &=default
auto and_then(F &&f) const &
Definition expected.hpp:721
friend auto operator==(expected const &lhs, expected< T2, E2 > const &rhs) -> bool
Definition expected.hpp:831
friend auto operator==(expected const &lhs, T2 const &rhs) -> bool
Definition expected.hpp:845
expected(expected const &)=default
auto transform_error(F &&f) &&
Definition expected.hpp:799
expected(expected &&)=default
auto transform_error(F &&f) const &&
Definition expected.hpp:805
constexpr auto has_value() const noexcept -> bool
Definition expected.hpp:599
auto value_or(U &&fallback) const &-> T
Definition expected.hpp:690
auto transform(F &&f) const &
Definition expected.hpp:769
constexpr auto error() const &&noexcept -> E const &&
Definition expected.hpp:683
friend auto operator==(expected const &lhs, unexpected< E2 > const &rhs) -> bool
Definition expected.hpp:869
constexpr auto error() &&noexcept -> E &&
Definition expected.hpp:678
constexpr expected(std::in_place_t, Args &&... args)
Definition expected.hpp:551
constexpr expected(U &&value)
Definition expected.hpp:539
auto transform_error(F &&f) &
Definition expected.hpp:787
constexpr auto operator*() &&noexcept -> T &&
Definition expected.hpp:629
auto or_else(F &&f) const &
Definition expected.hpp:745
auto or_else(F &&f) &&
Definition expected.hpp:751
constexpr auto error() &noexcept -> E &
Definition expected.hpp:668
auto and_then(F &&f) &&
Definition expected.hpp:727
auto operator=(unexpected< G > const &error) -> expected &
Definition expected.hpp:581
auto transform(F &&f) &&
Definition expected.hpp:775
auto transform_error(F &&f) const &
Definition expected.hpp:793
friend auto operator!=(expected const &lhs, unexpected< E2 > const &rhs) -> bool
Definition expected.hpp:881
constexpr auto operator*() const &noexcept -> T const &
Definition expected.hpp:624
auto value_or(U &&fallback) &&-> T
Definition expected.hpp:696
constexpr auto error() &&noexcept -> E &&
Definition expected.hpp:114
constexpr auto error() const &&noexcept -> E const &&
Definition expected.hpp:109
constexpr unexpected(E &&error)
Definition expected.hpp:97
constexpr auto error() const &noexcept -> E const &
Definition expected.hpp:99
constexpr auto error() &noexcept -> E &
Definition expected.hpp:104
constexpr unexpected(E const &error)
Definition expected.hpp:96
#define THREADSCHEDULE_EXPECTED_THROW(ex)
Definition expected.hpp:34
@ other
Standard round-robin time-sharing.
constexpr unexpect_t unexpect
Definition expected.hpp:44
void swap(expected< T, E > &lhs, expected< T, E > &rhs) noexcept(noexcept(lhs.swap(rhs)))