diff options
author | Ryan Ofsky <ryan@ofsky.org> | 2022-07-21 08:30:39 -0400 |
---|---|---|
committer | Ryan Ofsky <ryan@ofsky.org> | 2022-08-03 07:33:01 -0400 |
commit | a23cca56c0a7f4a267915b4beba3af3454c51603 (patch) | |
tree | 5d85193889f5a4552a568cfd2b472670c2c6d513 /src/util | |
parent | 4a4289e2c98cfbc51b05716f21065838afed80f6 (diff) | |
download | bitcoin-a23cca56c0a7f4a267915b4beba3af3454c51603.tar.xz |
refactor: Replace BResult with util::Result
Rename `BResult` class to `util::Result` and update the class interface to be
more compatible with `std::optional` and with a full-featured result class
implemented in https://github.com/bitcoin/bitcoin/pull/25665. Motivation for
this change is to update existing `BResult` usages now so they don't have to
change later when more features are added in #25665.
This change makes the following improvements originally implemented in #25665:
- More explicit API. Drops potentially misleading `BResult` constructor that
treats any bilingual string argument as an error. Adds `util::Error`
constructor so it is never ambiguous when a result is being assigned an error
or non-error value.
- Better type compatibility. Supports `util::Result<bilingual_str>` return
values to hold translated messages which are not errors.
- More standard and consistent API. `util::Result` supports most of the same
operators and methods as `std::optional`. `BResult` had a less familiar
interface with `HasRes`/`GetObj`/`ReleaseObj` methods. The Result/Res/Obj
naming was also not internally consistent.
- Better code organization. Puts `src/util/` code in the `util::` namespace so
naming reflects code organization and it is obvious where the class is coming
from. Drops "B" from name because it is undocumented what it stands for
(bilingual?)
- Has unit tests.
Diffstat (limited to 'src/util')
-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 |