Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <ostream>
7 : #include <utility>
8 :
9 : #include "Utilities/ErrorHandling/Error.hpp"
10 : #include "Utilities/Kokkos/KokkosMacros.hpp"
11 :
12 : /*!
13 : * \ingroup UtilitiesGroup
14 : * \brief Wraps an error to construct an `Expected` that holds the error.
15 : */
16 : template <typename E>
17 1 : struct Unexpected {
18 0 : E error;
19 : };
20 :
21 : template <typename E>
22 0 : Unexpected(E) -> Unexpected<E>;
23 :
24 : namespace Expected_detail {
25 : template <typename E>
26 : concept streamable =
27 : requires(std::ostream& os, const E& error) { os << error; };
28 : } // namespace Expected_detail
29 :
30 : /*!
31 : * \ingroup UtilitiesGroup
32 : * \brief Holds either a value of type `T` or an error of type `E`, like a
33 : * minimal version of C++23's `std::expected`.
34 : *
35 : * Construct an `Expected` from a `T` to hold a value, or from an
36 : * `Unexpected<E>` to hold an error. Return an `Expected` from functions that
37 : * can fail to let the caller decide how to handle the failure.
38 : *
39 : * \snippet Test_Expected.cpp expected_example
40 : *
41 : * Calling `value()` when there is an error, or `error()` when there is a
42 : * value, is an error (also in release builds).
43 : *
44 : * Unlike `std::expected`, this class stores both a `T` and an `E`, so both must
45 : * be default-constructible and the one that isn't held is default-constructed.
46 : * This keeps the class simple.
47 : */
48 : template <typename T, typename E>
49 1 : class Expected {
50 : public:
51 : // NOLINTNEXTLINE(google-explicit-constructor): converts like std::expected
52 0 : constexpr Expected(T value) : value_(std::move(value)), has_value_(true) {}
53 : // NOLINTNEXTLINE(google-explicit-constructor): converts like std::expected
54 0 : constexpr Expected(Unexpected<E> unexpected)
55 : : error_(std::move(unexpected.error)), has_value_(false) {}
56 :
57 0 : constexpr bool has_value() const { return has_value_; }
58 0 : constexpr explicit operator bool() const { return has_value_; }
59 :
60 : /// @{
61 : /// The value. It is an error to call this if `has_value()` is false.
62 1 : constexpr const T& value() const& {
63 : check_has_value();
64 : return value_;
65 : }
66 1 : constexpr T& value() & {
67 : check_has_value();
68 : return value_;
69 : }
70 1 : constexpr T&& value() && {
71 : check_has_value();
72 : return std::move(value_);
73 : }
74 : /// @}
75 :
76 : /// The error. It is an error to call this if `has_value()` is true.
77 1 : constexpr const E& error() const {
78 : if (has_value_) {
79 : KOKKOS_IF_ON_HOST((ERROR("Expected holds a value, not an error.");))
80 : KOKKOS_IF_ON_DEVICE(
81 : (Kokkos::abort("Expected holds a value, not an error.");))
82 : }
83 : return error_;
84 : }
85 :
86 : private:
87 0 : constexpr void check_has_value() const {
88 : if (not has_value_) {
89 : KOKKOS_IF_ON_HOST((
90 : if constexpr (Expected_detail::streamable<E>) {
91 : ERROR("Expected holds an error, not a value. The error is:\n"
92 : << error_);
93 : } else { ERROR("Expected holds an error, not a value."); }))
94 : KOKKOS_IF_ON_DEVICE(
95 : (Kokkos::abort("Expected holds an error, not a value.");))
96 : }
97 : }
98 :
99 0 : T value_{};
100 0 : E error_{};
101 0 : bool has_value_;
102 : };
|