I have a std::expected use case like this:
class MyClass {
public:
enum class error {
e1 // ...
};
using index_return_type = std::expected<unsigned, error>;
index_return_type add_data(data d);
index_return_type update_data(data d);
private:
std::expected<void, error> expand_to_fit(data) {
// Imagine i fail here
return std::unexpected{error::e1};
}
};
In add_data and update_data i would always immediately call expand_to_fit which can fail. If it does, essentially the error is already there, so i simply want to push it forward to outside. The best i have come up with is somehting like:
index_return_type add_data(data d) {
if (auto it_fit = expand_to_fit(d); !it_fit) {
return std::unexpected{it_fit.error()};
}
// The rest of the function
}
Live example.
But i don't love it.
it_fit is arbitrary, what if it could fit, but the expand_to_fit function failed for other reasons? It makes this code hard to read (and a little deceptive).Is there a better way to forward a failed std::expected up through your calling functions?
To extend this, imagine that i have a series of short checks. If i was using exceptions i might have something like:
void expand_to_fit(data) {
if (!connected_to_database()) {
throw std::runtime_error("Lost connection");
}
if (has_data(data)) {
throw std::runtime_error("Duplicate");
}
if (!is_input_in_max_range(data)) {
throw std::runtime_error("Cannot fit");
}
// ... Rest of function
}
How might we handle this with expected?
The idiomatic way would be to use one of the monadic operations:
and_then - returns the result of the given function on the expected value if it exists; otherwise, returns the expected itself.
transform - returns an expected containing the transformed expected value if it exists; otherwise, returns the expected itself
or_else - returns the expected itself if it contains an expected value; otherwise, returns the result of the given function on the unexpected value
transform_error - returns the expected itself if it contains an expected value; otherwise, returns an expected containing the transformed unexpected value
Since the expected type returned from expand_to_fit is void and the std::expected will contain the error if it occured inside expand_to_fit, you could use transform to return the expected unsigned if expand_to_fit does not contain error:
index_return_type add_data(data d) {
return expand_to_fit(d).transform([&]{
// the rest of the function wrapped inside a lambda
return 0u; // return the expected unsigned
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With