mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 06:58:57 +01:00
Merge bitcoin/bitcoin#25721: refactor: Replace BResult with util::Result
a23cca56c0refactor: Replace BResult with util::Result (Ryan Ofsky) Pull request description: 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 https://github.com/bitcoin/bitcoin/pull/25665. This change makes the following improvements originally implemented in https://github.com/bitcoin/bitcoin/pull/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. ACKs for top commit: MarcoFalke: ACKa23cca56c0🏵 jonatack: ACKa23cca56c0Tree-SHA512: 2769791e08cd62f21d850aa13fa7afce4fb6875a9cedc39ad5025150dbc611c2ecfd7b3aba8b980a79fde7fbda13babdfa37340633c69b501b6e89727bad5b31
This commit is contained in:
@@ -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;
|
||||
|
||||
template <typename FT>
|
||||
friend bilingual_str ErrorString(const Result<FT>& result);
|
||||
|
||||
public:
|
||||
BResult() : m_variant{Untranslated("")} {}
|
||||
BResult(T obj) : m_variant{std::move(obj)} {}
|
||||
BResult(bilingual_str error) : m_variant{std::move(error)} {}
|
||||
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)} {}
|
||||
|
||||
/* Whether the function succeeded or not */
|
||||
bool HasRes() const { return std::holds_alternative<T>(m_variant); }
|
||||
|
||||
/* In case of success, the result object */
|
||||
const T& GetObj() const {
|
||||
assert(HasRes());
|
||||
return std::get<T>(m_variant);
|
||||
}
|
||||
T ReleaseObj()
|
||||
//! 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(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);
|
||||
T& value() LIFETIMEBOUND
|
||||
{
|
||||
assert(has_value());
|
||||
return std::get<1>(m_variant);
|
||||
}
|
||||
|
||||
explicit operator bool() const { return HasRes(); }
|
||||
template <class U>
|
||||
T value_or(U&& default_value) const&
|
||||
{
|
||||
return has_value() ? value() : std::forward<U>(default_value);
|
||||
}
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user