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.
(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())),