Commit 6fd0d1b5 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

simplify exception_wrapper::handle

Summary: By implementing it in terms of a param-pack expansion of calls to `with_exception`. No longer rethrows objects not derived from `std::exception`.

Reviewed By: luciang

Differential Revision: D28125669

fbshipit-source-id: b9eef160cc03e2d6f5524c1a5113405cc2881324
parent 98c2c6f1
...@@ -472,140 +472,16 @@ template <class Ex> ...@@ -472,140 +472,16 @@ template <class Ex>
} }
} }
template <class CatchFn, bool IsConst> template <class This, class Fn>
struct exception_wrapper::ExceptionTypeOf { inline bool exception_wrapper::with_exception_(
using type = arg_type<std::decay_t<CatchFn>>; This&, Fn fn_, tag_t<AnyException>) {
static_assert( return void(fn_()), true;
std::is_reference<type>::value, "Always catch exceptions by reference.");
static_assert(
!IsConst || std::is_const<std::remove_reference_t<type>>::value,
"handle() or with_exception() called on a const exception_wrapper "
"and asked to catch a non-const exception. Handler will never fire. "
"Catch exception by const reference to fix this.");
};
// Nests a throw in the proper try/catch blocks
template <bool IsConst>
struct exception_wrapper::HandleReduce {
bool* handled_;
template <
class ThrowFn,
class CatchFn,
FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
auto operator()(ThrowFn&& th, CatchFn& ca) const {
using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] {
try {
th();
} catch (Ex& e) {
// If we got here because a catch function threw, rethrow.
if (*handled_) {
throw;
}
*handled_ = true;
ca(e);
}
};
}
template <
class ThrowFn,
class CatchFn,
FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
auto operator()(ThrowFn&& th, CatchFn& ca) const {
return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] {
try {
th();
} catch (...) {
// If we got here because a catch function threw, rethrow.
if (*handled_) {
throw;
}
*handled_ = true;
ca();
}
};
}
};
// When all the handlers expect types derived from std::exception, we can
// sometimes invoke the handlers without throwing any exceptions.
template <bool IsConst>
struct exception_wrapper::HandleStdExceptReduce {
using StdEx = AddConstIf<IsConst, std::exception>;
template <
class ThrowFn,
class CatchFn,
FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
auto operator()(ThrowFn&& th, CatchFn& ca) const {
using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
return
[th = std::forward<ThrowFn>(th), &ca](auto&& continuation) -> StdEx* {
if (auto e = const_cast<StdEx*>(th(continuation))) {
if (auto e2 = dynamic_cast<std::add_pointer_t<Ex>>(e)) {
ca(*e2);
} else {
return e;
}
}
return nullptr;
};
}
template <
class ThrowFn,
class CatchFn,
FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
auto operator()(ThrowFn&& th, CatchFn& ca) const {
return [th = std::forward<ThrowFn>(th), &ca](auto&&) -> StdEx* {
// The following continuation causes ca() to execute if *this contains
// an exception /not/ derived from std::exception.
auto continuation = [&ca](StdEx* e) {
return e != nullptr ? e : ((void)ca(), nullptr);
};
if (th(continuation) != nullptr) {
ca();
}
return nullptr;
};
}
};
// Called when some types in the catch clauses are not derived from
// std::exception.
template <class This, class... CatchFns>
inline void exception_wrapper::handle_(
std::false_type, This& this_, CatchFns&... fns) {
bool handled = false;
auto impl = exception_wrapper_detail::fold(
HandleReduce<std::is_const<This>::value>{&handled},
[&] { this_.throw_exception(); },
fns...);
impl();
} }
// Called when all types in the catch clauses are either derived from template <class This, class Fn, typename Ex>
// std::exception or a catch-all clause. inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t<Ex>) {
template <class This, class... CatchFns> auto ptr = this_.template get_exception<remove_cvref_t<Ex>>();
inline void exception_wrapper::handle_( return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);
std::true_type, This& this_, CatchFns&... fns) {
using StdEx = exception_wrapper_detail::
AddConstIf<std::is_const<This>::value, std::exception>;
auto impl = exception_wrapper_detail::fold(
HandleStdExceptReduce<std::is_const<This>::value>{},
[&](auto&& continuation) {
return continuation(
const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
},
fns...);
// This continuation gets evaluated if CatchFns... does not include a
// catch-all handler. It is a no-op.
auto continuation = [](StdEx* ex) { return ex; };
if (nullptr != impl(continuation)) {
this_.throw_exception();
}
} }
template <class Ex, class This, class Fn> template <class Ex, class This, class Fn>
...@@ -614,8 +490,21 @@ inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { ...@@ -614,8 +490,21 @@ inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
using from_ex = with_exception_from_ex_; using from_ex = with_exception_from_ex_;
using from = conditional_t<std::is_void<Ex>::value, from_fn, from_ex>; using from = conditional_t<std::is_void<Ex>::value, from_fn, from_ex>;
using type = typename from::template apply<Ex, Fn>; using type = typename from::template apply<Ex, Fn>;
auto ptr = this_.template get_exception<remove_cvref_t<type>>(); return with_exception_(this_, fn_, tag<type>);
return ptr && (void(fn_(static_cast<type&>(*ptr))), true); }
template <class This, class... CatchFns>
inline void exception_wrapper::handle_(
This& this_, char const* name, CatchFns&... fns) {
using _ = bool[];
if (!this_) {
onNoExceptionError(name);
}
bool handled = false;
void(_{false, (handled = handled || with_exception_<void>(this_, fns))...});
if (!handled) {
this_.throw_exception();
}
} }
template <class Ex, class Fn> template <class Ex, class Fn>
...@@ -629,21 +518,11 @@ inline bool exception_wrapper::with_exception(Fn fn) const { ...@@ -629,21 +518,11 @@ inline bool exception_wrapper::with_exception(Fn fn) const {
template <class... CatchFns> template <class... CatchFns>
inline void exception_wrapper::handle(CatchFns... fns) { inline void exception_wrapper::handle(CatchFns... fns) {
using AllStdEx = handle_(*this, __func__, fns...);
exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
if (!*this) {
onNoExceptionError(__func__);
}
this->handle_(AllStdEx{}, *this, fns...);
} }
template <class... CatchFns> template <class... CatchFns>
inline void exception_wrapper::handle(CatchFns... fns) const { inline void exception_wrapper::handle(CatchFns... fns) const {
using AllStdEx = handle_(*this, __func__, fns...);
exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
if (!*this) {
onNoExceptionError(__func__);
}
this->handle_(AllStdEx{}, *this, fns...);
} }
} // namespace folly } // namespace folly
...@@ -59,33 +59,6 @@ namespace folly { ...@@ -59,33 +59,6 @@ namespace folly {
#define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__ #define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__
namespace exception_wrapper_detail {
template <template <class> class T, class... As>
using AllOf = StrictConjunction<T<As>...>;
template <bool If, class T>
using AddConstIf = std::conditional_t<If, const T, T>;
template <class Fn, class A>
FOLLY_ERASE auto fold(Fn&&, A&& a) {
return static_cast<A&&>(a);
}
template <class Fn, class A, class B, class... Bs>
FOLLY_ERASE auto fold(Fn&& fn, A&& a, B&& b, Bs&&... bs) {
return fold(
// This looks like a use of fn after a move of fn, but in reality, this is
// just a cast and not a move. That's because regardless of which fold
// overload is selected, fn gets bound to a &&. Had fold taken fn by value
// there would indeed be a problem here.
static_cast<Fn&&>(fn),
static_cast<Fn&&>(fn)(static_cast<A&&>(a), static_cast<B&&>(b)),
static_cast<Bs&&>(bs)...);
}
} // namespace exception_wrapper_detail
//! Throwing exceptions can be a convenient way to handle errors. Storing //! Throwing exceptions can be a convenient way to handle errors. Storing
//! exceptions in an `exception_ptr` makes it easy to handle exceptions in a //! exceptions in an `exception_ptr` makes it easy to handle exceptions in a
//! different thread or at a later time. `exception_ptr` can also be used in a //! different thread or at a later time. `exception_ptr` can also be used in a
...@@ -208,8 +181,6 @@ class exception_wrapper final { ...@@ -208,8 +181,6 @@ class exception_wrapper final {
template <class Ex> template <class Ex>
using IsStdException = std::is_base_of<std::exception, std::decay_t<Ex>>; using IsStdException = std::is_base_of<std::exception, std::decay_t<Ex>>;
template <bool B, class T>
using AddConstIf = exception_wrapper_detail::AddConstIf<B, T>;
template <class CatchFn> template <class CatchFn>
using IsCatchAll = using IsCatchAll =
std::is_same<arg_type<std::decay_t<CatchFn>>, AnyException>; std::is_same<arg_type<std::decay_t<CatchFn>>, AnyException>;
...@@ -345,24 +316,18 @@ class exception_wrapper final { ...@@ -345,24 +316,18 @@ class exception_wrapper final {
Negation<std::is_base_of<exception_wrapper, T>>, Negation<std::is_base_of<exception_wrapper, T>>,
Negation<std::is_abstract<T>>> {}; Negation<std::is_abstract<T>>> {};
template <class CatchFn, bool IsConst = false> template <class This, class Fn>
struct ExceptionTypeOf; static bool with_exception_(This& this_, Fn fn_, tag_t<AnyException>);
template <bool IsConst> template <class This, class Fn, typename Ex>
struct HandleReduce; static bool with_exception_(This& this_, Fn fn_, tag_t<Ex>);
template <bool IsConst>
struct HandleStdExceptReduce;
template <class This, class... CatchFns>
static void handle_(std::false_type, This& this_, CatchFns&... fns);
template <class This, class... CatchFns>
static void handle_(std::true_type, This& this_, CatchFns&... fns);
template <class Ex, class This, class Fn> template <class Ex, class This, class Fn>
static bool with_exception_(This& this_, Fn fn_); static bool with_exception_(This& this_, Fn fn_);
template <class This, class... CatchFns>
static void handle_(This& this_, char const* name, CatchFns&... fns);
public: public:
static exception_wrapper from_exception_ptr( static exception_wrapper from_exception_ptr(
std::exception_ptr const& eptr) noexcept; std::exception_ptr const& eptr) noexcept;
...@@ -580,7 +545,7 @@ class exception_wrapper final { ...@@ -580,7 +545,7 @@ class exception_wrapper final {
//! \tparam CatchFns A pack of unary monomorphic function object types. //! \tparam CatchFns A pack of unary monomorphic function object types.
//! \param fns A pack of unary monomorphic function objects to be treated as //! \param fns A pack of unary monomorphic function objects to be treated as
//! an ordered list of potential exception handlers. //! an ordered list of potential exception handlers.
//! \note The handlers may or may not be invoked with an active exception. //! \note The handlers are not invoked with an active exception.
//! **Do not try to rethrow the exception with `throw;` from within your //! **Do not try to rethrow the exception with `throw;` from within your
//! handler -- that is, a throw expression with no operand.** This may //! handler -- that is, a throw expression with no operand.** This may
//! cause your process to terminate. (It is perfectly ok to throw from //! cause your process to terminate. (It is perfectly ok to throw from
......
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