New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: Replace BResult with util::Result #25721
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
13aeab1
to
2aa408b
Compare
Updated 13aeab1 -> 2aa408b ( |
2aa408b
to
e71b858
Compare
e71b858
to
42f4f7d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK.
But maybe the changes can be split in two or more commits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good, but doesn't compile in many places
src/util/result.h
Outdated
const T* operator->() const { return &value(); } | ||
const T& operator*() const { return value(); } | ||
T* operator->() { return &value(); } | ||
T& operator*() { return value(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, this addresses my feedback from #25218 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Feel free to ignore the feedback)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
42f4f7d 🖌
Show signature
Signature:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
42f4f7d126f6729c4924b0630f67d171f4d0ac9b 🖌
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUhJAAwAq6deLBiOO2WBQPKh16i4of8/CxKg0btdMYGdzxfdExEG8Dm27whTumz/
pDjAMeCpUlVuqYeR/iOrjfmkPXMXsOTWoKMXR2+RGQGTazobq7LQH2aZj5BRDnh0
zQBBpQ2X1BNQioLb45Wik63Rg/RbPX2s9hvNxXOL2OUCBqXJr0Hm1ZgxEd9/vsMe
a4ieXXLSJ5+ecc4DpZILj+ulVo4BCTRLnGZz+p7dRvDtzM62OQcxmUWt1u0mwYVf
gqYiqbEnQYcU2uLhKv1VTBdTpzWQYVALWTvZS+2Bbibt8tVpwJ+auDy8erWHZTZh
HKWnPPX5bb2UiHEnkEnJzuef8bGGBZE7WyNhaX6v4l7+LUWxh649xz7iqET8/+Ip
PbXfBoyk1pQ9tQo5MEJPwiFAP4xSSHJCjcvrXFFOUIt6iW5U1ANpUP03yzeRnkBe
E8O97B5lb6Z8b5Cr+xWYVHFntw4HiBk4tvls9imI/9qkGQxNQMAUKOqEYktpVZ5z
ZcC2Ugbr
=TSsI
-----END PGP SIGNATURE-----
42f4f7d
to
e0289b1
Compare
Thanks for reviews! Updated 42f4f7d -> e0289b1 ( re: #25721 (review)
I'm not so sure there is a way to do this that's a clear improvement. It would be possible to split by adding the new result interface and changing the old result interface to wrap the new interface in one commit, then deleting the old interface and changing usages to call the new interface in a second commit. This would split up the PR but also make it a little more complicated. Wouldn't oppose this, just not sure it would be worth it |
e0289b1
to
3262acf
Compare
Rebased e0289b1 -> 3262acf ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 3262acf 🍕
Show signature
Signature:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
ACK 3262acf70a9fdd6b4191812f928ed374dfcf32e1 🍕
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUjeSwv/WOfsnvZzsh0ls1W5nwhWVihAvZgqSDvwLDwjhIUroZAbxicGcR4+nMH3
8j/Ni2gs4mSYL3PDMOMYj0UgNalPtD41izoTFV/oEfoM1sT/1ZV0b6GtpzmNH+pR
PyuzQlGEFZSOyjHVKzQDfi6Io+1f9s4NLEPcP6KfzmNr6NaQYUCKQ2w59WVTlk/b
ELCJnE7DHAMamOF4zY6uvuiVx9SaUiM3cXjaUAZ6a1G3ccvgccGkFJNZwrXRkwKr
D1H7czDFaF5uyEMl/MJldPqmTztDGjj+vOOmOOYhSIAsMmiCgdoG/PFudKf1ple0
cVQYBS1Z2Z/aDHbk44rFzXw0eITgb69AMTwmU1iSOw5gzl4/Ejv8b0q0FTX/l/b2
3G4SjdP5oq1gmCjfzITeU9JxgqcKhSb83sp/LInsbg6KFAI7zP0KnluoNGK3o+NY
hgjGzcScF6+pULwZjdweCVW+7JtefZMhwAFYle45+gVzaJ2otL6SZrThBRKuJWi3
kndV8T/O
=xb3E
-----END PGP SIGNATURE-----
3262acf
to
6777df6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very helpful review!
Updated 3262acf -> 6777df6 (pr/bresult-del.6
-> pr/bresult-del.7
, compare) with suggested changes
ACK 6777df6 🏏 Show signatureSignature:
|
6777df6
to
c654ec5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated 6777df6 -> c654ec5 (pr/bresult-del.7
-> pr/bresult-del.8
, compare) with more complete commit description and suggestions from last review
Updated c654ec5 -> 7b249b3 (pr/bresult-del.8
-> pr/bresult-del.9
, compare) with a few more cleanups
c654ec5
to
7b249b3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits, but please don't take them or I might leave more
//! 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 { return m_variant.index() == 1; } | ||
const T& value() const LIFETIMEBOUND |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
same?
Can throw bad_variant_access according to https://en.cppreference.com/w/cpp/utility/variant/get. The equivalent std::optional method can also throw https://en.cppreference.com/w/cpp/utility/optional/value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it can throw after our assert, can it?
return has_value() ? std::move(value()) : std::forward<U>(default_value); | ||
} | ||
explicit operator bool() const { return has_value(); } | ||
const T* operator->() const LIFETIMEBOUND { return &value(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
Same, per https://en.cppreference.com/w/cpp/utility/optional/operator*
I guess this is a difference between std::optional and std::variant. It doesn't seem like there is a way to get the address of an object inside a variant that is noexcept. it would be possible to make this noexcept in followup #25665 which removes the std::variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be possible, given our assert
, no?
Ok, will skip noexcept stuff for now unless there is another push. If I rebase #25665 on top of this PR, that would be another chance to make noexcept or constexpr or similar improvements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK, pretty cool stuff.
Will review it deeper in the coming days.
Have to say that would have loved to talk about some of this stuff on the initial PR and/or via DM. Including the thrashing to the current result class name. But well, no hard feelings.
/* 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)} {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
styling nit: what about creating an enum for the indexes?
enum { ERR=0, VAL=1 };
// then
std::in_place_index_t<VAL>{}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
enum { ERR=0, VAL=1 }; // then std::in_place_index_t<VAL>{}
#25665 should drop the std::variant entirely making this moot, but using enum for this seems more indirect and fragile since nothing keeps enum and variant declarations lined up. I'm not against adding this, but would prefer not to unless there is more demand
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 bitcoin#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 bitcoin#25665. This change makes the following improvements originally implemented in bitcoin#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.
7b249b3
to
a23cca5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased 7b249b3 -> a23cca5 (pr/bresult-del.9
-> pr/bresult-del.10
, compare) due to silent conflict with #25272
re: #25721 (review)
Have to say that would have loved to talk about some of this stuff on the initial PR and/or via DM. Including the thrashing to the current result class name. But well, no hard feelings.
Thanks. I did bring up all of the issues I saw with the result class in the original PR, including the name. But I didn't want to get bogged down, and in general I think it's more productive to iterate on designs across PRs than to have back and forth debates in PR comments. Iterating across PRs puts PR authors in a position to propose unified designs, and avoid problems of design-by-committtee, or intractable disagreements, or compromises that split things down the middle and don't leave anybody happy. Better to give priority to PR authors and just take turns making improvements. Also, iterating on designs with actual PRs instead of PR comments centers code changes over verbal descriptions, which I think leads to better decisions in the end.
/* 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)} {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
enum { ERR=0, VAL=1 }; // then std::in_place_index_t<VAL>{}
#25665 should drop the std::variant entirely making this moot, but using enum for this seems more indirect and fragile since nothing keeps enum and variant declarations lined up. I'm not against adding this, but would prefer not to unless there is more demand
//! 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 { return m_variant.index() == 1; } | ||
const T& value() const LIFETIMEBOUND |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
same?
Can throw bad_variant_access according to https://en.cppreference.com/w/cpp/utility/variant/get. The equivalent std::optional method can also throw https://en.cppreference.com/w/cpp/utility/optional/value
return has_value() ? std::move(value()) : std::forward<U>(default_value); | ||
} | ||
explicit operator bool() const { return has_value(); } | ||
const T* operator->() const LIFETIMEBOUND { return &value(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25721 (comment)
Same, per https://en.cppreference.com/w/cpp/utility/optional/operator*
I guess this is a difference between std::optional and std::variant. It doesn't seem like there is a way to get the address of an object inside a variant that is noexcept. it would be possible to make this noexcept in followup #25665 which removes the std::variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK a23cca5
If you retouch, suggest running the clang-format script on these changes for indentation fixups: git diff -U0 HEAD~1.. | ./contrib/devtools/clang-format-diff.py -p1 -i -v
} | ||
|
||
template <typename T, typename... Args> | ||
void ExpectFail(const util::Result<T>& result, const bilingual_str& str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add static
for the non-template functions in this test file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
template
should imply inline
, which again implies that this can't be used in another translation unit, but correct me if I am wrong.
CMutableTransaction mtx; | ||
mtx.vout.push_back({COIN, GetScriptForDestination(dest.GetObj())}); | ||
mtx.vout.push_back({COIN, GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, "")))}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add util/check.h
include header for Assert
@@ -88,7 +88,7 @@ class Wallet | |||
virtual std::string getWalletName() = 0; | |||
|
|||
// Get a new address. | |||
virtual BResult<CTxDestination> getNewDestination(const OutputType type, const std::string label) = 0; | |||
virtual util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string label) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While touching this line here and in the override function in src/wallet/interfaces.cpp
virtual util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string label) = 0; | |
virtual util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string& label) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Picked up in #25616.
auto op_dest = wallet.GetNewDestination(OutputType::BECH32, ""); | ||
assert(op_dest.HasRes()); | ||
tx.vout[nInput].scriptPubKey = GetScriptForDestination(op_dest.GetObj()); | ||
tx.vout[nInput].scriptPubKey = GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, ""))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add util/check.h
include header for Assert
if (fuzzed_data_provider.ConsumeBool()) { | ||
op_dest = wallet->GetNewDestination(type, ""); | ||
} else { | ||
op_dest = wallet->GetNewChangeDestination(type); | ||
} | ||
assert(op_dest.HasRes()); | ||
return GetScriptForDestination(op_dest.GetObj()); | ||
return GetScriptForDestination(*Assert(op_dest)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add util/check.h
include header for Assert
Can drop the temporary here, if desired
// Add tx to wallet
const auto& op_dest = wallet.GetNewDestination(OutputType::BECH32M, "");
BOOST_ASSERT(op_dest);
- const CTxDestination& dest = *op_dest;
CMutableTransaction mtx;
- mtx.vout.push_back({COIN, GetScriptForDestination(dest)});
+ mtx.vout.push_back({COIN, GetScriptForDestination(*op_dest)});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Picked up in #25616.
} | ||
return op_dest.HasRes(); | ||
return bool(op_dest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pico-nit
return bool(op_dest); | |
return bool{op_dest}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or just has_value (from HasRes)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Picked up in #25616.
ACK a23cca5 🏵 Show signatureSignature:
|
Rename
BResult
class toutil::Result
and update the class interface to be more compatible withstd::optional
and with a full-featured result class implemented in #25665. Motivation for this change is to update existingBResult
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. Addsutil::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 asstd::optional
.BResult
had a less familiar interface withHasRes
/GetObj
/ReleaseObj
methods. The Result/Res/Obj naming was also not internally consistent.Better code organization. Puts
src/util/
code in theutil::
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.