diff options
Diffstat (limited to 'src/util/result.h')
-rw-r--r-- | src/util/result.h | 87 |
1 files changed, 61 insertions, 26 deletions
diff --git a/src/util/result.h b/src/util/result.h index 2f586a4c9b..972b1aada0 100644 --- a/src/util/result.h +++ b/src/util/result.h @@ -5,45 +5,80 @@ #ifndef BITCOIN_UTIL_RESULT_H #define BITCOIN_UTIL_RESULT_H +#include <attributes.h> #include <util/translation.h> #include <variant> -/* - * 'BResult' is a generic class useful for wrapping a return object - * (in case of success) or propagating the error cause. -*/ -template<class T> -class BResult { +namespace util { + +struct Error { + bilingual_str message; +}; + +//! The util::Result class provides a standard way for functions to return +//! either error messages or result values. +//! +//! It is intended for high-level functions that need to report error strings to +//! end users. Lower-level functions that don't need this error-reporting and +//! only need error-handling should avoid util::Result and instead use standard +//! classes like std::optional, std::variant, and std::tuple, or custom structs +//! and enum types to return function results. +//! +//! Usage examples can be found in \example ../test/result_tests.cpp, but in +//! general code returning `util::Result<T>` values is very similar to code +//! returning `std::optional<T>` values. Existing functions returning +//! `std::optional<T>` can be updated to return `util::Result<T>` and return +//! error strings usually just replacing `return std::nullopt;` with `return +//! util::Error{error_string};`. +template <class T> +class Result +{ private: std::variant<bilingual_str, T> m_variant; -public: - BResult() : m_variant{Untranslated("")} {} - BResult(T obj) : m_variant{std::move(obj)} {} - BResult(bilingual_str error) : m_variant{std::move(error)} {} + template <typename FT> + friend bilingual_str ErrorString(const Result<FT>& result); - /* Whether the function succeeded or not */ - bool HasRes() const { return std::holds_alternative<T>(m_variant); } +public: + Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {} + Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {} - /* In case of success, the result object */ - const T& GetObj() const { - assert(HasRes()); - return std::get<T>(m_variant); + //! std::optional methods, so functions returning optional<T> can change to + //! return Result<T> with minimal changes to existing code, and vice versa. + bool has_value() const noexcept { return m_variant.index() == 1; } + const T& value() const LIFETIMEBOUND + { + assert(has_value()); + return std::get<1>(m_variant); } - T ReleaseObj() + T& value() LIFETIMEBOUND { - assert(HasRes()); - return std::move(std::get<T>(m_variant)); + assert(has_value()); + return std::get<1>(m_variant); } - - /* In case of failure, the error cause */ - const bilingual_str& GetError() const { - assert(!HasRes()); - return std::get<bilingual_str>(m_variant); + template <class U> + T value_or(U&& default_value) const& + { + return has_value() ? value() : std::forward<U>(default_value); } - - explicit operator bool() const { return HasRes(); } + template <class U> + T value_or(U&& default_value) && + { + return has_value() ? std::move(value()) : std::forward<U>(default_value); + } + explicit operator bool() const noexcept { return has_value(); } + const T* operator->() const LIFETIMEBOUND { return &value(); } + const T& operator*() const LIFETIMEBOUND { return value(); } + T* operator->() LIFETIMEBOUND { return &value(); } + T& operator*() LIFETIMEBOUND { return value(); } }; +template <typename T> +bilingual_str ErrorString(const Result<T>& result) +{ + return result ? bilingual_str{} : std::get<0>(result.m_variant); +} +} // namespace util + #endif // BITCOIN_UTIL_RESULT_H |