expected.h
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