mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
[Error] Add a handleExpected utility.
handleExpected is similar to handleErrors, but takes an Expected<T> as its first input value and a fallback functor as its second, followed by an arbitary list of error handlers (equivalent to the handler list of handleErrors). If the first input value is a success value then it is returned from handleErrors unmodified. Otherwise the contained error(s) are passed to handleErrors, along with the handlers. If handleErrors returns success (indicating that all errors have been handled) then handleExpected runs the fallback functor and returns its result. If handleErrors returns a failure value then the failure value is returned and the fallback functor is never run. This simplifies the process of re-trying operations that return Expected values. Without this utility such retry logic is cumbersome as the internal Error must be explicitly extracted from the Expected value, inspected to see if its handleable and then consumed: enum FooStrategy { Aggressive, Conservative }; Expected<Foo> tryFoo(FooStrategy S); Expected<Foo> Result; (void)!!Result; // "Check" Result so that it can be safely overwritten. if (auto ValOrErr = tryFoo(Aggressive)) Result = std::move(ValOrErr); else { auto Err = ValOrErr.takeError(); if (Err.isA<HandleableError>()) { consumeError(std::move(Err)); Result = tryFoo(Conservative); } else return std::move(Err); } with handleExpected, this can be re-written as: auto Result = handleExpected( tryFoo(Aggressive), []() { return tryFoo(Conservative); }, [](HandleableError&) { /* discard to handle */ }); llvm-svn: 311870
This commit is contained in:
parent
f8a3b2e94f
commit
ed191215a4
@ -878,6 +878,43 @@ inline void handleAllErrors(Error E) {
|
||||
cantFail(std::move(E));
|
||||
}
|
||||
|
||||
/// Handle any errors (if present) in an Expected<T>, then try a recovery path.
|
||||
///
|
||||
/// If the incoming value is a success value it is returned unmodified. If it
|
||||
/// is a failure value then it the contained error is passed to handleErrors.
|
||||
/// If handleErrors is able to handle the error then the RecoveryPath functor
|
||||
/// is called to supply the final result. If handleErrors is not able to
|
||||
/// handle all errors then the unhandled errors are returned.
|
||||
///
|
||||
/// This utility enables the follow pattern:
|
||||
///
|
||||
/// @code{.cpp}
|
||||
/// enum FooStrategy { Aggressive, Conservative };
|
||||
/// Expected<Foo> foo(FooStrategy S);
|
||||
///
|
||||
/// auto ResultOrErr =
|
||||
/// handleExpected(
|
||||
/// foo(Aggressive),
|
||||
/// []() { return foo(Conservative); },
|
||||
/// [](AggressiveStrategyError&) {
|
||||
/// // Implicitly conusme this - we'll recover by using a conservative
|
||||
/// // strategy.
|
||||
/// });
|
||||
///
|
||||
/// @endcode
|
||||
template <typename T, typename RecoveryFtor, typename... HandlerTs>
|
||||
Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath,
|
||||
HandlerTs &&... Handlers) {
|
||||
if (ValOrErr)
|
||||
return ValOrErr;
|
||||
|
||||
if (auto Err = handleErrors(ValOrErr.takeError(),
|
||||
std::forward<HandlerTs>(Handlers)...))
|
||||
return std::move(Err);
|
||||
|
||||
return RecoveryPath();
|
||||
}
|
||||
|
||||
/// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner
|
||||
/// will be printed before the first one is logged. A newline will be printed
|
||||
/// after each error.
|
||||
|
@ -605,6 +605,59 @@ TEST(Error, ExpectedCovariance) {
|
||||
(void)!!A2;
|
||||
}
|
||||
|
||||
// Test that handleExpected just returns success values.
|
||||
TEST(Error, HandleExpectedSuccess) {
|
||||
auto ValOrErr =
|
||||
handleExpected(Expected<int>(42),
|
||||
[]() { return Expected<int>(43); });
|
||||
EXPECT_TRUE(!!ValOrErr)
|
||||
<< "handleExpected should have returned a success value here";
|
||||
EXPECT_EQ(*ValOrErr, 42)
|
||||
<< "handleExpected should have returned the original success value here";
|
||||
}
|
||||
|
||||
enum FooStrategy { Aggressive, Conservative };
|
||||
|
||||
static Expected<int> foo(FooStrategy S) {
|
||||
if (S == Aggressive)
|
||||
return make_error<CustomError>(7);
|
||||
return 42;
|
||||
}
|
||||
|
||||
// Test that handleExpected invokes the error path if errors are not handled.
|
||||
TEST(Error, HandleExpectedUnhandledError) {
|
||||
// foo(Aggressive) should return a CustomError which should pass through as
|
||||
// there is no handler for CustomError.
|
||||
auto ValOrErr =
|
||||
handleExpected(
|
||||
foo(Aggressive),
|
||||
[]() { return foo(Conservative); });
|
||||
|
||||
EXPECT_FALSE(!!ValOrErr)
|
||||
<< "handleExpected should have returned an error here";
|
||||
auto Err = ValOrErr.takeError();
|
||||
EXPECT_TRUE(Err.isA<CustomError>())
|
||||
<< "handleExpected should have returned the CustomError generated by "
|
||||
"foo(Aggressive) here";
|
||||
consumeError(std::move(Err));
|
||||
}
|
||||
|
||||
// Test that handleExpected invokes the fallback path if errors are handled.
|
||||
TEST(Error, HandleExpectedHandledError) {
|
||||
// foo(Aggressive) should return a CustomError which should handle triggering
|
||||
// the fallback path.
|
||||
auto ValOrErr =
|
||||
handleExpected(
|
||||
foo(Aggressive),
|
||||
[]() { return foo(Conservative); },
|
||||
[](const CustomError&) { /* do nothing */ });
|
||||
|
||||
EXPECT_TRUE(!!ValOrErr)
|
||||
<< "handleExpected should have returned a success value here";
|
||||
EXPECT_EQ(*ValOrErr, 42)
|
||||
<< "handleExpected returned the wrong success value";
|
||||
}
|
||||
|
||||
TEST(Error, ErrorCodeConversions) {
|
||||
// Round-trip a success value to check that it converts correctly.
|
||||
EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())),
|
||||
|
Loading…
Reference in New Issue
Block a user