/ src / util / expected.h
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