result.h raw

   1  // Copyright (c) 2022-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or https://www.opensource.org/licenses/mit-license.php.
   4  
   5  #ifndef BITCOIN_UTIL_RESULT_H
   6  #define BITCOIN_UTIL_RESULT_H
   7  
   8  #include <attributes.h>
   9  #include <util/translation.h>
  10  
  11  #include <variant>
  12  
  13  namespace util {
  14  
  15  struct Error {
  16      bilingual_str message;
  17  };
  18  
  19  //! The util::Result class provides a standard way for functions to return
  20  //! either error messages or result values.
  21  //!
  22  //! It is intended for high-level functions that need to report error strings to
  23  //! end users. Lower-level functions that don't need this error-reporting and
  24  //! that only need error-handling should avoid util::Result and instead use
  25  //! util::Expected, std::optional, std::variant, or custom structs
  26  //! and enum types to return function results.
  27  //!
  28  //! Usage examples can be found in \example ../test/result_tests.cpp, but in
  29  //! general code returning `util::Result<T>` values is very similar to code
  30  //! returning `std::optional<T>` values. Existing functions returning
  31  //! `std::optional<T>` can be updated to return `util::Result<T>` and return
  32  //! error strings usually just replacing `return std::nullopt;` with `return
  33  //! util::Error{error_string};`.
  34  template <class M>
  35  class Result
  36  {
  37  private:
  38      using T = std::conditional_t<std::is_same_v<M, void>, std::monostate, M>;
  39  
  40      std::variant<bilingual_str, T> m_variant;
  41  
  42      //! Disallow copy constructor, require Result to be moved for efficiency.
  43      Result(const Result&) = delete;
  44  
  45      //! Disallow operator= to avoid confusion in the future when the Result
  46      //! class gains support for richer error reporting, and callers should have
  47      //! ability to set a new result value without clearing existing error
  48      //! messages.
  49      Result& operator=(const Result&) = delete;
  50      Result& operator=(Result&&) = delete;
  51  
  52      template <typename FT>
  53      friend bilingual_str ErrorString(const Result<FT>& result);
  54  
  55  public:
  56      Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {}  // constructor for void
  57      Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
  58      Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
  59      Result(Result&&) = default;
  60      ~Result() = default;
  61  
  62      //! std::optional methods, so functions returning optional<T> can change to
  63      //! return Result<T> with minimal changes to existing code, and vice versa.
  64      bool has_value() const noexcept { return m_variant.index() == 1; }
  65      const T& value() const LIFETIMEBOUND
  66      {
  67          assert(has_value());
  68          return std::get<1>(m_variant);
  69      }
  70      T& value() LIFETIMEBOUND
  71      {
  72          assert(has_value());
  73          return std::get<1>(m_variant);
  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      explicit operator bool() const noexcept { return has_value(); }
  86      const T* operator->() const LIFETIMEBOUND { return &value(); }
  87      const T& operator*() const LIFETIMEBOUND { return value(); }
  88      T* operator->() LIFETIMEBOUND { return &value(); }
  89      T& operator*() LIFETIMEBOUND { return value(); }
  90  };
  91  
  92  template <typename T>
  93  bilingual_str ErrorString(const Result<T>& result)
  94  {
  95      return result ? bilingual_str{} : std::get<0>(result.m_variant);
  96  }
  97  } // namespace util
  98  
  99  #endif // BITCOIN_UTIL_RESULT_H
 100