Commit 6ec1462b authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

invoke_cold, catch_exception

Summary: [Folly] `invoke_cold`, `catch_exception`. These may be used to facilitate preprocessor-free-at-call-site `try`-and-`catch` in library code when compiling with `-fno-exceptions`.

Reviewed By: swolchok

Differential Revision: D13020421

fbshipit-source-id: d9d7998e00eed6ced943ea73b648fa0b7cabd831
parent 95934611
......@@ -17,6 +17,8 @@
#pragma once
#include <exception>
#include <type_traits>
#include <utility>
#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
......@@ -94,6 +96,31 @@ terminate_with(Args&&... args) noexcept {
}
// clang-format on
/// invoke_cold
///
/// Invoke the provided function with the provided arguments.
///
/// Usage note:
/// Passing extra values as arguments rather than capturing them allows smaller
/// inlined native at the call-site.
///
/// Example:
///
/// if (i < 0) {
/// invoke_cold(
/// [](int j) {
/// std::string ret = doStepA();
/// doStepB(ret);
/// doStepC(ret);
/// },
/// i);
/// }
template <typename F, typename... A>
FOLLY_NOINLINE FOLLY_COLD auto invoke_cold(F&& f, A&&... a)
-> decltype(static_cast<F&&>(f)(static_cast<A&&>(a)...)) {
return static_cast<F&&>(f)(static_cast<A&&>(a)...);
}
/// invoke_noreturn_cold
///
/// Invoke the provided function with the provided arguments. If the invocation
......@@ -107,7 +134,7 @@ terminate_with(Args&&... args) noexcept {
///
/// Usage note:
/// Passing extra values as arguments rather than capturing them allows smaller
/// bytecode at the call-site.
/// inlined native code at the call-site.
///
/// Example:
///
......@@ -126,4 +153,89 @@ template <typename F, typename... A>
std::terminate();
}
/// catch_exception
///
/// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions),
/// catches a thrown exception e of type E and invokes c, forwarding e and any
/// trailing arguments.
///
/// Usage note:
/// As a general rule, pass Ex const& rather than unqualified Ex as the explicit
/// template argument E. The catch statement catches E without qualifiers so
/// if E is Ex then that translates to catch (Ex), but if E is Ex const& then
/// that translates to catch (Ex const&).
///
/// Usage note:
/// Passing extra values as arguments rather than capturing them allows smaller
/// inlined native code at the call-site.
///
/// Example:
///
/// int input = // ...
/// int def = 45;
/// auto result = catch_exception<std::runtime_error const&>(
/// [=] {
/// if (input < 0) throw std::runtime_error("foo");
/// return input;
/// },
/// [](auto&& e, int num) { return num; },
/// def);
/// assert(result == input < 0 ? def : input);
template <typename E, typename Try, typename Catch, typename... CatchA>
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto
catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> typename std::common_type<
decltype(static_cast<Try&&>(t)()),
decltype(static_cast<Catch&&>(
c)(std::declval<E>(), static_cast<CatchA&&>(a)...))>::type {
#if FOLLY_HAS_EXCEPTIONS
try {
return static_cast<Try&&>(t)();
} catch (E e) {
return invoke_cold(static_cast<Catch&&>(c), e, static_cast<CatchA&&>(a)...);
}
#else
[](auto&&...) {}(c, a...); // ignore
return static_cast<Try&&>(t)();
#endif
}
/// catch_exception
///
/// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions),
/// catches a thrown exception of any type and invokes c, forwarding any
/// trailing arguments.
//
/// Usage note:
/// Passing extra values as arguments rather than capturing them allows smaller
/// inlined native code at the call-site.
///
/// Example:
///
/// int input = // ...
/// int def = 45;
/// auto result = catch_exception(
/// [=] {
/// if (input < 0) throw 11;
/// return input;
/// },
/// [](int num) { return num; },
/// def);
/// assert(result == input < 0 ? def : input);
template <typename Try, typename Catch, typename... CatchA>
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto
catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> typename std::common_type<
decltype(static_cast<Try&&>(t)()),
decltype(static_cast<Catch&&>(c)(static_cast<CatchA&&>(a)...))>::type {
#if FOLLY_HAS_EXCEPTIONS
try {
return static_cast<Try&&>(t)();
} catch (...) {
return invoke_cold(static_cast<Catch&&>(c), static_cast<CatchA&&>(a)...);
}
#else
[](auto&&...) {}(c, a...); // ignore
return static_cast<Try&&>(t)();
#endif
}
} // namespace folly
......@@ -103,9 +103,26 @@ TEST_F(ExceptionTest, terminate_with_variadic) {
message_for_terminate_with<MyException>("world"));
}
TEST_F(ExceptionTest, invoke_cold) {
EXPECT_THROW(
folly::invoke_cold([] { throw std::runtime_error("bad"); }),
std::runtime_error);
EXPECT_EQ(7, folly::invoke_cold([] { return 7; }));
}
TEST_F(ExceptionTest, invoke_noreturn_cold) {
EXPECT_THROW(
folly::invoke_noreturn_cold([] { throw std::runtime_error("bad"); }),
std::runtime_error);
EXPECT_DEATH(folly::invoke_noreturn_cold([] {}), message_for_terminate());
}
TEST_F(ExceptionTest, catch_exception) {
auto identity = [](int i) { return i; };
auto returner = [](int i) { return [=] { return i; }; };
auto thrower = [](int i) { return [=]() -> int { throw i; }; };
EXPECT_EQ(3, folly::catch_exception(returner(3), returner(4)));
EXPECT_EQ(3, folly::catch_exception<int>(returner(3), identity));
EXPECT_EQ(4, folly::catch_exception(thrower(3), returner(4)));
EXPECT_EQ(3, folly::catch_exception<int>(thrower(3), identity));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment