llvm.org GIT mirror llvm / 68a7b2d
[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 */ }); git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@311870 91177308-0d34-0410-b5e6-96231b3b80d8 Lang Hames 2 years ago
2 changed file(s) with 90 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
877877 cantFail(std::move(E));
878878 }
879879
880 /// Handle any errors (if present) in an Expected, then try a recovery path.
881 ///
882 /// If the incoming value is a success value it is returned unmodified. If it
883 /// is a failure value then it the contained error is passed to handleErrors.
884 /// If handleErrors is able to handle the error then the RecoveryPath functor
885 /// is called to supply the final result. If handleErrors is not able to
886 /// handle all errors then the unhandled errors are returned.
887 ///
888 /// This utility enables the follow pattern:
889 ///
890 /// @code{.cpp}
891 /// enum FooStrategy { Aggressive, Conservative };
892 /// Expected foo(FooStrategy S);
893 ///
894 /// auto ResultOrErr =
895 /// handleExpected(
896 /// foo(Aggressive),
897 /// []() { return foo(Conservative); },
898 /// [](AggressiveStrategyError&) {
899 /// // Implicitly conusme this - we'll recover by using a conservative
900 /// // strategy.
901 /// });
902 ///
903 /// @endcode
904 template
905 Expected handleExpected(Expected ValOrErr, RecoveryFtor &&RecoveryPath,
906 HandlerTs &&... Handlers) {
907 if (ValOrErr)
908 return ValOrErr;
909
910 if (auto Err = handleErrors(ValOrErr.takeError(),
911 std::forward(Handlers)...))
912 return std::move(Err);
913
914 return RecoveryPath();
915 }
916
880917 /// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner
881918 /// will be printed before the first one is logged. A newline will be printed
882919 /// after each error.
604604 (void)!!A2;
605605 }
606606
607 // Test that handleExpected just returns success values.
608 TEST(Error, HandleExpectedSuccess) {
609 auto ValOrErr =
610 handleExpected(Expected(42),
611 []() { return Expected(43); });
612 EXPECT_TRUE(!!ValOrErr)
613 << "handleExpected should have returned a success value here";
614 EXPECT_EQ(*ValOrErr, 42)
615 << "handleExpected should have returned the original success value here";
616 }
617
618 enum FooStrategy { Aggressive, Conservative };
619
620 static Expected foo(FooStrategy S) {
621 if (S == Aggressive)
622 return make_error(7);
623 return 42;
624 }
625
626 // Test that handleExpected invokes the error path if errors are not handled.
627 TEST(Error, HandleExpectedUnhandledError) {
628 // foo(Aggressive) should return a CustomError which should pass through as
629 // there is no handler for CustomError.
630 auto ValOrErr =
631 handleExpected(
632 foo(Aggressive),
633 []() { return foo(Conservative); });
634
635 EXPECT_FALSE(!!ValOrErr)
636 << "handleExpected should have returned an error here";
637 auto Err = ValOrErr.takeError();
638 EXPECT_TRUE(Err.isA())
639 << "handleExpected should have returned the CustomError generated by "
640 "foo(Aggressive) here";
641 consumeError(std::move(Err));
642 }
643
644 // Test that handleExpected invokes the fallback path if errors are handled.
645 TEST(Error, HandleExpectedHandledError) {
646 // foo(Aggressive) should return a CustomError which should handle triggering
647 // the fallback path.
648 auto ValOrErr =
649 handleExpected(
650 foo(Aggressive),
651 []() { return foo(Conservative); },
652 [](const CustomError&) { /* do nothing */ });
653
654 EXPECT_TRUE(!!ValOrErr)
655 << "handleExpected should have returned a success value here";
656 EXPECT_EQ(*ValOrErr, 42)
657 << "handleExpected returned the wrong success value";
658 }
659
607660 TEST(Error, ErrorCodeConversions) {
608661 // Round-trip a success value to check that it converts correctly.
609662 EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())),