Result<T> currently has two problems,
1) A failing Result<T> cannot be easily constructed from a Result<U>'s
error.
2) errno is lost when passing .error() through multiple Result<T>'s
This change fixes both problems having Result<T>::error() return a
ResultError class that contains the std::string error message and int
errno.
It additionally has ostream operators to continue to allow printing
the error string directly to an ostream and also to pass the errno
through to another Result<T> class via Error() creation.
Lastly, it provides a new constructor for Result<T> for ResultError,
such that a Result<T> can be constructed from Result<U>::error().
Test: boot bullhead, init unit tests
Change-Id: Id9614b727cdabd2f5498b0da0e598e9aff7d9ae0
init tries to propagate error information up to build context before
logging errors. This is a good thing, however too often init has the
overly verbose paradigm for error handling, below:
bool CalculateResult(const T& input, U* output, std::string* err)
bool CalculateAndUseResult(const T& input, std::string* err) {
U output;
std::string calculate_result_err;
if (!CalculateResult(input, &output, &calculate_result_err)) {
*err = "CalculateResult " + input + " failed: " +
calculate_result_err;
return false;
}
UseResult(output);
return true;
}
Even more common are functions that return only true/false but also
require passing a std::string* err in order to see the error message.
This change introduces a Result<T> that is use to either hold a
successful return value of type T or to hold an error message as a
std::string. If the functional only returns success or a failure with
an error message, Result<Success> may be used. The classes Error and
ErrnoError are used to indicate a failed Result<T>.
A successful Result<T> is constructed implicitly from any type that
can be implicitly converted to T or from the constructor arguments for
T. This allows you to return a type T directly from a function that
returns Result<T>.
Error and ErrnoError are used to construct a Result<T> has
failed. Each of these classes take an ostream as an input and are
implicitly cast to a Result<T> containing that failure. ErrnoError()
additionally appends ": " + strerror(errno) to the end of the failure
string to aid in interacting with C APIs.
The end result is that the above code snippet is turned into the much
clearer example below:
Result<U> CalculateResult(const T& input);
Result<Success> CalculateAndUseResult(const T& input) {
auto output = CalculateResult(input);
if (!output) {
return Error() << "CalculateResult " << input << " failed: "
<< output.error();
}
UseResult(*output);
return Success();
}
This change also makes this conversion for some of the util.cpp
functions that used the old paradigm.
Test: boot bullhead, init unit tests
Merged-In: I1e7d3a8820a79362245041251057fbeed2f7979b
Change-Id: I1e7d3a8820a79362245041251057fbeed2f7979b