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