expected.h raw
1 // Copyright (c) The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or https://opensource.org/license/mit.
4
5 #ifndef BITCOIN_UTIL_EXPECTED_H
6 #define BITCOIN_UTIL_EXPECTED_H
7
8 #include <attributes.h>
9 #include <util/check.h>
10
11 #include <exception>
12 #include <utility>
13 #include <variant>
14
15 namespace util {
16
17 /// The util::Unexpected class represents an unexpected value stored in
18 /// util::Expected.
19 template <class E>
20 class Unexpected
21 {
22 public:
23 constexpr explicit Unexpected(E e) : m_error(std::move(e)) {}
24
25 constexpr const E& error() const& noexcept LIFETIMEBOUND { return m_error; }
26 constexpr E& error() & noexcept LIFETIMEBOUND { return m_error; }
27 constexpr E&& error() && noexcept LIFETIMEBOUND { return std::move(m_error); }
28
29 private:
30 E m_error;
31 };
32
33 struct BadExpectedAccess : std::exception {
34 const char* what() const noexcept override { return "Bad util::Expected access"; }
35 };
36
37 /// The util::Expected class provides a standard way for low-level functions to
38 /// return either error values or result values.
39 ///
40 /// It provides a smaller version of std::expected from C++23. Missing features
41 /// can be added, if needed.
42 template <class T, class E>
43 class Expected
44 {
45 private:
46 std::variant<T, E> m_data;
47
48 public:
49 constexpr Expected() : m_data{std::in_place_index<0>, T{}} {}
50 constexpr Expected(T v) : m_data{std::in_place_index<0>, std::move(v)} {}
51 template <class Err>
52 constexpr Expected(Unexpected<Err> u) : m_data{std::in_place_index<1>, std::move(u).error()}
53 {
54 }
55
56 constexpr bool has_value() const noexcept { return m_data.index() == 0; }
57 constexpr explicit operator bool() const noexcept { return has_value(); }
58
59 constexpr const T& value() const& LIFETIMEBOUND
60 {
61 if (!has_value()) {
62 throw BadExpectedAccess{};
63 }
64 return std::get<0>(m_data);
65 }
66 constexpr T& value() & LIFETIMEBOUND
67 {
68 if (!has_value()) {
69 throw BadExpectedAccess{};
70 }
71 return std::get<0>(m_data);
72 }
73 constexpr T&& value() && LIFETIMEBOUND { return std::move(value()); }
74
75 template <class U>
76 T value_or(U&& default_value) const&
77 {
78 return has_value() ? value() : std::forward<U>(default_value);
79 }
80 template <class U>
81 T value_or(U&& default_value) &&
82 {
83 return has_value() ? std::move(value()) : std::forward<U>(default_value);
84 }
85
86 constexpr const E& error() const& noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); }
87 constexpr E& error() & noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); }
88 constexpr E&& error() && noexcept LIFETIMEBOUND { return std::move(error()); }
89
90 constexpr void swap(Expected& other) noexcept { m_data.swap(other.m_data); }
91
92 constexpr T& operator*() & noexcept LIFETIMEBOUND { return value(); }
93 constexpr const T& operator*() const& noexcept LIFETIMEBOUND { return value(); }
94 constexpr T&& operator*() && noexcept LIFETIMEBOUND { return std::move(value()); }
95
96 constexpr T* operator->() noexcept LIFETIMEBOUND { return &value(); }
97 constexpr const T* operator->() const noexcept LIFETIMEBOUND { return &value(); }
98 };
99
100 template <class E>
101 class Expected<void, E>
102 {
103 private:
104 std::variant<std::monostate, E> m_data;
105
106 public:
107 constexpr Expected() : m_data{std::in_place_index<0>, std::monostate{}} {}
108 template <class Err>
109 constexpr Expected(Unexpected<Err> u) : m_data{std::in_place_index<1>, std::move(u).error()}
110 {
111 }
112
113 constexpr bool has_value() const noexcept { return m_data.index() == 0; }
114 constexpr explicit operator bool() const noexcept { return has_value(); }
115
116 constexpr void operator*() const noexcept { return value(); }
117 constexpr void value() const
118 {
119 if (!has_value()) {
120 throw BadExpectedAccess{};
121 }
122 }
123
124 constexpr const E& error() const& noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); }
125 constexpr E& error() & noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); }
126 constexpr E&& error() && noexcept LIFETIMEBOUND { return std::move(error()); }
127 };
128
129 } // namespace util
130
131 #endif // BITCOIN_UTIL_EXPECTED_H
132