/ 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 <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