-
Notifications
You must be signed in to change notification settings - Fork 38.7k
util: generalize util::Result to support custom errors
#34005
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
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code Coverage & BenchmarksFor details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34005. ReviewsSee the guideline for information on the review process. 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. LLM Linter (✨ experimental)Possible typos and grammar issues:
2025-12-04 |
|
Have you looked at #25665? |
Looking at bb61eca55a563d7486df3c356d163c4af10a36c8, I think this is compatible with #25665. This PR is a minimal tweak to It might be good if this added an alias like: template<typename T, typename F>
using Expected = Result<T, F>;So it could be clearer which code should eventually use |
The `util::Result` helper currently wraps a `std::variant<bilingual_str, T> `and is only usable for high-level code that needs to propagate user-facing error strings. This change generalizes `Result` so it can also be used in low-level code paths with typed error codes instead of raw `std::variant` or `std::expected`. Instead of hard-coding `bilingual_str` in the variant, the error is now templated, so the existing uses of `Result<T>` behave as before. Call sites get an explicit value-or-error result type without having to manipulate std::variant directly. The design mirrors the shape of `std::expected<T, E>` in C++23, but remains usable on our current C++20 baseline, similar to how `Span` provided a precursor to `std::span`.
bb61eca to
d44f140
Compare
|
I wonder if this is the right approach. I am not sure how fast we'll be getting C++23. C++20 was enabled two years ago in #28349, but there are still questions around which C++20 features are supported. (e.g. jthread is only in libc++ 20, std::format is only in g++13, ...) So I think just porting a minimal So I did that in #34006 |
Good idea, added and used it in the new test cases.
I agree with @ryanofsky that this is likely compatible with the PR and as far as I understand it addresses a small chunk of what it's also trying to achieve.
I'm fine with both, let me know if I should close this to favor the other one. Not sure why we'd need two, but it's not a big deal either way. |
Not sure they are conceptually compatible. 25665 aims to reduce the error footprint by wrapping it in a unique_ptr, whereas So a scripted-diff going from an I think it is better to use std::unique_ptr explicitly in Expected/std::expected, when needed. |
Nice, that's more complicated than this PR, but not too bad. I don't have any particular preference between these two PR's. Either seem fine. |
Yeah, it is a bit more code, but, the inner guts of |
|
re: #34005 (comment)
Yes, it's true that hypothetically if this PR got merged and then #25665 got merged, performance of code using I don't see that as a concern for this PR, since the current |
Either is fine by me as well. It just seems more churn to (1) introduce the Expected alias based on Result, (2) move it a different file, (3) likely rework it to remove the implicit bilingual_string error stuff (because it is not needed and confusing). It seems easier to just add Expected in its intended form in one go. But no strong opinion, anything seems fine here. |
| } | ||
|
|
||
| template<typename T, typename F> | ||
| using Expected = Result<T, F>; |
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.
if this alias is added, one should also be added for std::unexpected.
Glad you have that alternative ready, closing in favor of #34006 |
faa2373 refactor: Enable clang-tidy bugprone-unused-return-value (MarcoFalke) fa114be Add util::Expected (std::expected) (MarcoFalke) Pull request description: Some low-level code could benefit from being able to use `std::expected` from C++23: * Currently, some code is using `std::optional<E>` to denote an optional error. This is fine, but a bit confusing, because `std::optional` is normally used for values, not errors. Using `std::expected<void, E>` is clearer. * Currently, some code is using `std::variant<V, E>` to denote either a value or an error. This is fine, but a bit verbose, because `std::variant` requires a visitor or get_if/holds_alternative instead of a simple call of the `operator bool` for `std::expected`. In theory, `util::Result` could be taught to behave similar to `std::expected` (see #34005). However, it is unclear if this is the right approach: * `util::Result` is mostly meant for higher level code, where errors come with translated error messages. * `std::expected` is mostly meant for lower level code, where errors could be an enum, or any other type. * #25665 aims to minimize the memory footprint of the error by wrapping it in a unique_ptr internally. `std::expected` requires the value and error to be "nested within it" (https://cplusplus.github.io/LWG/issue4141). So from a memory-layout perspective, the two are not compatible. * `std::expected` also comes with `std::unexpected`, which also does not map cleanly to `util::Result`. So just add a minimal drop-in port of `std::expected`. ACKs for top commit: romanz: tACK faa2373 sedited: Re-ACK faa2373 hodlinator: ACK faa2373 rkrux: light Code Review ACK faa2373 ryanofsky: Code review ACK faa2373, only adding `value_or` implementation and `CheckedReturnTypes` clang-tidy commit since last review. stickies-v: ACK faa2373 Tree-SHA512: fdbd0f6bf439738ffe6a68da5522f1051537f8df9c308eb90bef6bd2e06931d79f1c5da22d5500765e9cb1d801d5be39e11e10d47c9659fec1a8c8804cb7c872
Context
While reviewing #33657 it became clear we don’t have a good value-or-error wrapper, similar in spirit to
std::expected<T, E>in C++23.Problem
The
util::Resulthelper currently stores astd::variant<bilingual_str, T>and is effectively only usable for high-level code that needs to propagate user-facing error strings.Low-level code that wants typed error codes instead of strings still has to expose raw
std::variant(or roll custom structs).Fix
Generalize
util::Resultto take a second template parameterEfor the error type, and storestd::variant<E, T>internally.The default
Eremainsbilingual_str, so existing uses ofResult<T>andutil::Error{...}behave exactly as before andErrorString(Result<T>)continues to work.New code can now use typed errors and avoid the awkward
std::variantgetter, e.g.: