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