Skip to content

Commit 61866ff

Browse files
snarkmasterfacebook-github-bot
authored andcommitted
result_catch_all captures invocation exceptions in result<T>
Reviewed By: ispeters, ibrahimjirdeh Differential Revision: D71942890 fbshipit-source-id: 3f35c8cdd35b4a1061eae0694b0e0e5f2c5633fc
1 parent ef0d79a commit 61866ff

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

folly/result/result.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,37 @@ auto /* implicit */ operator co_await(T && t) {
903903
return detail::result_co_await_dispatcher()(std::forward<T>(t));
904904
}
905905

906+
/// Wraps the return value from the lambda `fn` in a `result`, putting any
907+
/// thrown exception into its "error" state.
908+
///
909+
/// return result_catch_all([&](){ return riskyWork(); });
910+
///
911+
/// Useful when you need a subroutine **definitely** not to throw. In contrast:
912+
/// - `result<>` coroutines catch unhandled exceptions, but can throw due to
913+
/// argument copy/move ctors, or due to `bad_alloc`.
914+
/// - Like all functions, `result<>` non-coroutines let exceptions fly.
915+
template <typename F>
916+
// Wrap the return type of `fn` with `result` unless it already is `result`.
917+
typename std::conditional_t<
918+
is_instantiation_of_v<result, std::invoke_result_t<F>>,
919+
std::invoke_result_t<F>,
920+
result<std::invoke_result_t<F>>>
921+
result_catch_all(F&& fn) noexcept {
922+
try {
923+
if constexpr (std::is_void_v<std::invoke_result_t<F>>) {
924+
static_cast<F&&>(fn)();
925+
return {};
926+
} else {
927+
return static_cast<F&&>(fn)();
928+
}
929+
} catch (...) {
930+
// We're a making `result`, so it's OK to forward all exceptions into it,
931+
// including `OperationCancelled`.
932+
return non_value_result::make_legacy_error_or_cancellation(
933+
exception_wrapper{std::current_exception()});
934+
}
935+
}
936+
906937
} // namespace folly
907938

908939
#endif // FOLLY_HAS_RESULT

folly/result/test/result_test.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,47 @@ RESULT_CO_TEST_F(ResultTest, check_TEST_F) {
876876
EXPECT_EQ(co_await std::ref(r), 42);
877877
}
878878

879+
TEST(Result, catch_all_returns_result_error) {
880+
auto res = []() -> result<uint8_t> {
881+
return result_catch_all([]() -> result<uint8_t> {
882+
return make_exception_wrapper<std::logic_error>("bop");
883+
});
884+
}();
885+
ASSERT_STREQ("bop", get_exception<std::logic_error>(res)->what());
886+
}
887+
888+
TEST(Result, catch_all_throws_error) {
889+
auto res = []() -> result<uint8_t> {
890+
return result_catch_all([]() -> uint8_t {
891+
throw std::logic_error("foobar");
892+
});
893+
}();
894+
ASSERT_STREQ("foobar", get_exception<std::logic_error>(res)->what());
895+
}
896+
897+
TEST(Result, catch_all_throws_error_returns_result) {
898+
auto res = []() -> result<uint8_t> {
899+
return result_catch_all([]() -> result<uint8_t> {
900+
throw std::logic_error("foobar");
901+
});
902+
}();
903+
ASSERT_STREQ("foobar", get_exception<std::logic_error>(res)->what());
904+
}
905+
906+
TEST(Result, catch_all_void_throws_error) {
907+
auto res = []() -> result<> {
908+
return result_catch_all([]() { throw std::logic_error("baz"); });
909+
}();
910+
ASSERT_STREQ("baz", get_exception<std::logic_error>(res)->what());
911+
}
912+
913+
TEST(Result, catch_all_returns_value) {
914+
auto fn = []() -> result<uint8_t> {
915+
return result_catch_all([]() -> uint8_t { return 129; });
916+
};
917+
ASSERT_EQ(129, fn().value_or_throw());
918+
}
919+
879920
} // namespace folly
880921

881922
#endif // FOLLY_HAS_RESULT

0 commit comments

Comments
 (0)