| /* |
| * Copyright 2017-present Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * Author: Eric Niebler <eniebler@fb.com> |
| */ |
| |
| #pragma once |
| |
| #include <cassert> |
| #include <cstdint> |
| #include <exception> |
| #include <iosfwd> |
| #include <memory> |
| #include <new> |
| #include <type_traits> |
| #include <typeinfo> |
| #include <utility> |
| |
| #include <folly/Assume.h> |
| #include <folly/CPortability.h> |
| #include <folly/Demangle.h> |
| #include <folly/ExceptionString.h> |
| #include <folly/FBString.h> |
| #include <folly/Portability.h> |
| #include <folly/Traits.h> |
| #include <folly/Utility.h> |
| |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wpragmas" |
| #pragma GCC diagnostic ignored "-Wpotentially-evaluated-expression" |
| // GCC gets confused about lambda scopes and issues shadow-local warnings for |
| // parameters in totally different functions. |
| FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS |
| #endif |
| |
| #define FOLLY_EXCEPTION_WRAPPER_H_INCLUDED |
| |
| namespace folly { |
| |
| #define FOLLY_REQUIRES_DEF(...) \ |
| _t<std::enable_if<static_cast<bool>(__VA_ARGS__), long>> |
| |
| #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 = _t<std::conditional<If, const T, T>>; |
| |
| template <class Fn, class A> |
| FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN |
| auto fold(Fn&&, A&& a) { |
| return static_cast<A&&>(a); |
| } |
| |
| template <class Fn, class A, class B, class... Bs> |
| FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN |
| 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 |
| //! 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 |
| //! very generic result/exception wrapper. |
| //! |
| //! However, there are some issues with throwing exceptions and |
| //! `std::exception_ptr`. These issues revolve around `throw` being expensive, |
| //! particularly in a multithreaded environment (see |
| //! ExceptionWrapperBenchmark.cpp). |
| //! |
| //! Imagine we have a library that has an API which returns a result/exception |
| //! wrapper. Let's consider some approaches for implementing this wrapper. |
| //! First, we could store a `std::exception`. This approach loses the derived |
| //! exception type, which can make exception handling more difficult for users |
| //! that prefer rethrowing the exception. We could use a `folly::dynamic` for |
| //! every possible type of exception. This is not very flexible - adding new |
| //! types of exceptions requires a change to the result/exception wrapper. We |
| //! could use an `exception_ptr`. However, constructing an `exception_ptr` as |
| //! well as accessing the error requires a call to throw. That means that there |
| //! will be two calls to throw in order to process the exception. For |
| //! performance sensitive applications, this may be unacceptable. |
| //! |
| //! `exception_wrapper` is designed to handle exception management for both |
| //! convenience and high performance use cases. `make_exception_wrapper` is |
| //! templated on derived type, allowing us to rethrow the exception properly for |
| //! users that prefer convenience. These explicitly named exception types can |
| //! therefore be handled without any peformance penalty. `exception_wrapper` is |
| //! also flexible enough to accept any type. If a caught exception is not of an |
| //! explicitly named type, then `std::exception_ptr` is used to preserve the |
| //! exception state. For performance sensitive applications, the accessor |
| //! methods can test or extract a pointer to a specific exception type with very |
| //! little overhead. |
| //! |
| //! \par Example usage: |
| //! \par |
| //! \code |
| //! exception_wrapper globalExceptionWrapper; |
| //! |
| //! // Thread1 |
| //! void doSomethingCrazy() { |
| //! int rc = doSomethingCrazyWithLameReturnCodes(); |
| //! if (rc == NAILED_IT) { |
| //! globalExceptionWrapper = exception_wrapper(); |
| //! } else if (rc == FACE_PLANT) { |
| //! globalExceptionWrapper = make_exception_wrapper<FacePlantException>(); |
| //! } else if (rc == FAIL_WHALE) { |
| //! globalExceptionWrapper = make_exception_wrapper<FailWhaleException>(); |
| //! } |
| //! } |
| //! |
| //! // Thread2: Exceptions are ok! |
| //! void processResult() { |
| //! try { |
| //! globalExceptionWrapper.throw_exception(); |
| //! } catch (const FacePlantException& e) { |
| //! LOG(ERROR) << "FACEPLANT!"; |
| //! } catch (const FailWhaleException& e) { |
| //! LOG(ERROR) << "FAILWHALE!"; |
| //! } |
| //! } |
| //! |
| //! // Thread2: Exceptions are bad! |
| //! void processResult() { |
| //! globalExceptionWrapper.handle( |
| //! [&](FacePlantException& faceplant) { |
| //! LOG(ERROR) << "FACEPLANT"; |
| //! }, |
| //! [&](FailWhaleException& failwhale) { |
| //! LOG(ERROR) << "FAILWHALE!"; |
| //! }, |
| //! [](...) { |
| //! LOG(FATAL) << "Unrecognized exception"; |
| //! }); |
| //! } |
| //! \endcode |
| class exception_wrapper final { |
| private: |
| struct AnyException : std::exception { |
| std::type_info const* typeinfo_; |
| template <class T> |
| /* implicit */ AnyException(T&& t) noexcept : typeinfo_(&typeid(t)) {} |
| }; |
| |
| template <class Fn> |
| struct arg_type_; |
| template <class Fn> |
| using arg_type = _t<arg_type_<Fn>>; |
| |
| // exception_wrapper is implemented as a simple variant over four |
| // different representations: |
| // 0. Empty, no exception. |
| // 1. An small object stored in-situ. |
| // 2. A larger object stored on the heap and referenced with a |
| // std::shared_ptr. |
| // 3. A std::exception_ptr, together with either: |
| // a. A pointer to the referenced std::exception object, or |
| // b. A pointer to a std::type_info object for the referenced exception, |
| // or for an unspecified type if the type is unknown. |
| // This is accomplished with the help of a union and a pointer to a hand- |
| // rolled virtual table. This virtual table contains pointers to functions |
| // that know which field of the union is active and do the proper action. |
| // The class invariant ensures that the vtable ptr and the union stay in sync. |
| struct VTable { |
| void (*copy_)(exception_wrapper const*, exception_wrapper*); |
| void (*move_)(exception_wrapper*, exception_wrapper*); |
| void (*delete_)(exception_wrapper*); |
| void (*throw_)(exception_wrapper const*); |
| std::type_info const* (*type_)(exception_wrapper const*); |
| std::exception const* (*get_exception_)(exception_wrapper const*); |
| exception_wrapper (*get_exception_ptr_)(exception_wrapper const*); |
| }; |
| |
| [[noreturn]] static void onNoExceptionError(char const* name); |
| |
| template <class Ret, class... Args> |
| static Ret noop_(Args...); |
| |
| static std::type_info const* uninit_type_(exception_wrapper const*); |
| |
| static VTable const uninit_; |
| |
| template <class Ex> |
| using IsStdException = std::is_base_of<std::exception, _t<std::decay<Ex>>>; |
| template <bool B, class T> |
| using AddConstIf = exception_wrapper_detail::AddConstIf<B, T>; |
| template <class CatchFn> |
| using IsCatchAll = |
| std::is_same<arg_type<_t<std::decay<CatchFn>>>, AnyException>; |
| |
| struct Unknown {}; |
| |
| // Sadly, with the gcc-4.9 platform, std::logic_error and std::runtime_error |
| // do not fit here. They also don't have noexcept copy-ctors, so the internal |
| // storage wouldn't be used anyway. For the gcc-5 platform, both logic_error |
| // and runtime_error can be safely stored internally. |
| struct Buffer { |
| using Storage = |
| _t<std::aligned_storage<2 * sizeof(void*), alignof(std::exception)>>; |
| Storage buff_; |
| |
| Buffer() : buff_{} {} |
| |
| template <class Ex, typename... As> |
| Buffer(in_place_type_t<Ex>, As&&... as_); |
| template <class Ex> |
| Ex& as() noexcept; |
| template <class Ex> |
| Ex const& as() const noexcept; |
| }; |
| |
| enum class Placement { kInSitu, kOnHeap }; |
| template <class T> |
| using PlacementOf = std::integral_constant< |
| Placement, |
| sizeof(T) <= sizeof(Buffer::Storage) && |
| alignof(T) <= alignof(Buffer::Storage) && |
| noexcept(T(std::declval<T&&>())) |
| ? Placement::kInSitu |
| : Placement::kOnHeap>; |
| |
| using InSituTag = std::integral_constant<Placement, Placement::kInSitu>; |
| using OnHeapTag = std::integral_constant<Placement, Placement::kOnHeap>; |
| |
| static std::exception const* as_exception_or_null_(std::exception const& ex); |
| static std::exception const* as_exception_or_null_(AnyException); |
| |
| struct ExceptionPtr { |
| std::exception_ptr ptr_; |
| std::uintptr_t exception_or_type_; // odd for type_info |
| static_assert( |
| 1 < alignof(std::exception) && 1 < alignof(std::type_info), |
| "Surprise! std::exception and std::type_info don't have alignment " |
| "greater than one. as_int_ below will not work!"); |
| |
| static std::uintptr_t as_int_( |
| std::exception_ptr const& ptr, |
| std::exception const& e); |
| static std::uintptr_t as_int_( |
| std::exception_ptr const& ptr, |
| AnyException e); |
| bool has_exception_() const; |
| std::exception const* as_exception_() const; |
| std::type_info const* as_type_() const; |
| static void copy_(exception_wrapper const* from, exception_wrapper* to); |
| static void move_(exception_wrapper* from, exception_wrapper* to); |
| static void delete_(exception_wrapper* that); |
| [[noreturn]] static void throw_(exception_wrapper const* that); |
| static std::type_info const* type_(exception_wrapper const* that); |
| static std::exception const* get_exception_(exception_wrapper const* that); |
| static exception_wrapper get_exception_ptr_(exception_wrapper const* that); |
| static VTable const ops_; |
| }; |
| |
| template <class Ex> |
| struct InPlace { |
| static void copy_(exception_wrapper const* from, exception_wrapper* to); |
| static void move_(exception_wrapper* from, exception_wrapper* to); |
| static void delete_(exception_wrapper* that); |
| [[noreturn]] static void throw_(exception_wrapper const* that); |
| static std::type_info const* type_(exception_wrapper const*); |
| static std::exception const* get_exception_(exception_wrapper const* that); |
| static exception_wrapper get_exception_ptr_(exception_wrapper const* that); |
| static constexpr VTable const ops_{copy_, |
| move_, |
| delete_, |
| throw_, |
| type_, |
| get_exception_, |
| get_exception_ptr_}; |
| }; |
| |
| struct SharedPtr { |
| struct Base { |
| std::type_info const* info_; |
| Base() = default; |
| explicit Base(std::type_info const& info) : info_(&info) {} |
| virtual ~Base() {} |
| virtual void throw_() const = 0; |
| virtual std::exception const* get_exception_() const noexcept = 0; |
| virtual exception_wrapper get_exception_ptr_() const noexcept = 0; |
| }; |
| template <class Ex> |
| struct Impl final : public Base { |
| Ex ex_; |
| Impl() = default; |
| template <typename... As> |
| explicit Impl(As&&... as) |
| : Base{typeid(Ex)}, ex_(std::forward<As>(as)...) {} |
| [[noreturn]] void throw_() const override; |
| std::exception const* get_exception_() const noexcept override; |
| exception_wrapper get_exception_ptr_() const noexcept override; |
| }; |
| std::shared_ptr<Base> ptr_; |
| |
| static void copy_(exception_wrapper const* from, exception_wrapper* to); |
| static void move_(exception_wrapper* from, exception_wrapper* to); |
| static void delete_(exception_wrapper* that); |
| [[noreturn]] static void throw_(exception_wrapper const* that); |
| static std::type_info const* type_(exception_wrapper const* that); |
| static std::exception const* get_exception_(exception_wrapper const* that); |
| static exception_wrapper get_exception_ptr_(exception_wrapper const* that); |
| static VTable const ops_; |
| }; |
| |
| union { |
| Buffer buff_{}; |
| ExceptionPtr eptr_; |
| SharedPtr sptr_; |
| }; |
| VTable const* vptr_{&uninit_}; |
| |
| template <class Ex, typename... As> |
| exception_wrapper(OnHeapTag, in_place_type_t<Ex>, As&&... as); |
| |
| template <class Ex, typename... As> |
| exception_wrapper(InSituTag, in_place_type_t<Ex>, As&&... as); |
| |
| template <class T> |
| struct IsRegularExceptionType |
| : StrictConjunction< |
| std::is_copy_constructible<T>, |
| Negation<std::is_base_of<exception_wrapper, T>>, |
| Negation<std::is_abstract<T>>> {}; |
| |
| template <class CatchFn, bool IsConst = false> |
| struct ExceptionTypeOf; |
| |
| template <bool IsConst> |
| struct HandleReduce; |
| |
| 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> |
| static bool with_exception_(This& this_, Fn fn_); |
| |
| public: |
| //! Default-constructs an empty `exception_wrapper` |
| //! \post `type() == none()` |
| exception_wrapper() noexcept {} |
| |
| //! Move-constructs an `exception_wrapper` |
| //! \post `*this` contains the value of `that` prior to the move |
| //! \post `that.type() == none()` |
| exception_wrapper(exception_wrapper&& that) noexcept; |
| |
| //! Copy-constructs an `exception_wrapper` |
| //! \post `*this` contains a copy of `that`, and `that` is unmodified |
| //! \post `type() == that.type()` |
| exception_wrapper(exception_wrapper const& that); |
| |
| //! Move-assigns an `exception_wrapper` |
| //! \pre `this != &that` |
| //! \post `*this` contains the value of `that` prior to the move |
| //! \post `that.type() == none()` |
| exception_wrapper& operator=(exception_wrapper&& that) noexcept; |
| |
| //! Copy-assigns an `exception_wrapper` |
| //! \post `*this` contains a copy of `that`, and `that` is unmodified |
| //! \post `type() == that.type()` |
| exception_wrapper& operator=(exception_wrapper const& that); |
| |
| ~exception_wrapper(); |
| |
| //! \pre `ptr` is empty, or it holds a reference to an exception that is not |
| //! derived from `std::exception`. |
| //! \post `!ptr || bool(*this)` |
| //! \post `hasThrownException() == true` |
| //! \post `type() == unknown()` |
| explicit exception_wrapper(std::exception_ptr ptr) noexcept; |
| |
| //! \pre `ptr` holds a reference to `ex`. |
| //! \post `hasThrownException() == true` |
| //! \post `bool(*this)` |
| //! \post `type() == typeid(ex)` |
| template <class Ex> |
| exception_wrapper(std::exception_ptr ptr, Ex& ex); |
| |
| //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)` |
| //! \post `bool(*this)` |
| //! \post `hasThrownException() == false` |
| //! \post `type() == typeid(ex)` |
| //! \note Exceptions of types derived from `std::exception` can be implicitly |
| //! converted to an `exception_wrapper`. |
| template < |
| class Ex, |
| class Ex_ = _t<std::decay<Ex>>, |
| FOLLY_REQUIRES( |
| Conjunction<IsStdException<Ex_>, IsRegularExceptionType<Ex_>>::value)> |
| /* implicit */ exception_wrapper(Ex&& ex); |
| |
| //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)` |
| //! \post `bool(*this)` |
| //! \post `hasThrownException() == false` |
| //! \post `type() == typeid(ex)` |
| //! \note Exceptions of types not derived from `std::exception` can still be |
| //! used to construct an `exception_wrapper`, but you must specify |
| //! `folly::in_place` as the first parameter. |
| template < |
| class Ex, |
| class Ex_ = _t<std::decay<Ex>>, |
| FOLLY_REQUIRES(IsRegularExceptionType<Ex_>::value)> |
| exception_wrapper(in_place_t, Ex&& ex); |
| |
| template < |
| class Ex, |
| typename... As, |
| FOLLY_REQUIRES(IsRegularExceptionType<Ex>::value)> |
| exception_wrapper(in_place_type_t<Ex>, As&&... as); |
| |
| //! Swaps the value of `*this` with the value of `that` |
| void swap(exception_wrapper& that) noexcept; |
| |
| //! \return `true` if `*this` is holding an exception. |
| explicit operator bool() const noexcept; |
| |
| //! \return `!bool(*this)` |
| bool operator!() const noexcept; |
| |
| //! Make this `exception_wrapper` empty |
| //! \post `!*this` |
| void reset(); |
| |
| //! \return `true` if this `exception_wrapper` holds a reference to an |
| //! exception that was thrown (i.e., if it was constructed with |
| //! a `std::exception_ptr`, or if `to_exception_ptr()` was called on a |
| //! (non-const) reference to `*this`). |
| bool has_exception_ptr() const noexcept; |
| |
| //! \return a pointer to the `std::exception` held by `*this`, if it holds |
| //! one; otherwise, returns `nullptr`. |
| //! \note This function does not mutate the `exception_wrapper` object. |
| //! \note This function never causes an exception to be thrown. |
| std::exception* get_exception() noexcept; |
| //! \overload |
| std::exception const* get_exception() const noexcept; |
| |
| //! \returns a pointer to the `Ex` held by `*this`, if it holds an object |
| //! whose type `From` permits `std::is_convertible<From*, Ex*>`; |
| //! otherwise, returns `nullptr`. |
| //! \note This function does not mutate the `exception_wrapper` object. |
| //! \note This function may cause an exception to be thrown and immediately |
| //! caught internally, affecting runtime performance. |
| template <typename Ex> |
| Ex* get_exception() noexcept; |
| //! \overload |
| template <typename Ex> |
| Ex const* get_exception() const noexcept; |
| |
| //! \return A `std::exception_ptr` that references either the exception held |
| //! by `*this`, or a copy of same. |
| //! \note This function may need to throw an exception to complete the action. |
| //! \note The non-const overload of this function mutates `*this` to cache the |
| //! computed `std::exception_ptr`; that is, this function may cause |
| //! `has_exception_ptr()` to change from `false` to `true`. |
| std::exception_ptr const& to_exception_ptr() noexcept; |
| //! \overload |
| std::exception_ptr to_exception_ptr() const noexcept; |
| |
| //! \return the `typeid` of an unspecified type used by |
| //! `exception_wrapper::type()` to denote an empty `exception_wrapper`. |
| static std::type_info const& none() noexcept; |
| //! \return the `typeid` of an unspecified type used by |
| //! `exception_wrapper::type()` to denote an `exception_wrapper` that |
| //! holds an exception of unknown type. |
| static std::type_info const& unknown() noexcept; |
| |
| //! Returns the `typeid` of the wrapped exception object. If there is no |
| //! wrapped exception object, returns `exception_wrapper::none()`. If |
| //! this instance wraps an exception of unknown type not derived from |
| //! `std::exception`, returns `exception_wrapper::unknown()`. |
| std::type_info const& type() const noexcept; |
| |
| //! \return If `get_exception() != nullptr`, `class_name() + ": " + |
| //! get_exception()->what()`; otherwise, `class_name()`. |
| folly::fbstring what() const; |
| |
| //! \return If `!*this`, the empty string; otherwise, if |
| //! `type() == unknown()`, the string `"<unknown exception>"`; otherwise, |
| //! the result of `type().name()` after demangling. |
| folly::fbstring class_name() const; |
| |
| //! \tparam Ex The expression type to check for compatibility with. |
| //! \return `true` if and only if `*this` wraps an exception that would be |
| //! caught with a `catch(Ex const&)` clause. |
| //! \note If `*this` is empty, this function returns `false`. |
| template <class Ex> |
| bool is_compatible_with() const noexcept; |
| |
| //! \pre `bool(*this)` |
| //! Throws the wrapped expression. |
| [[noreturn]] void throw_exception() const; |
| |
| //! Call `fn` with the wrapped exception (if any), if `fn` can accept it. |
| //! \par Example |
| //! \code |
| //! exception_wrapper ew{std::runtime_error("goodbye cruel world")}; |
| //! |
| //! assert( ew.with_exception([](std::runtime_error& e){/*...*/}) ); |
| //! |
| //! assert( !ew.with_exception([](int& e){/*...*/}) ); |
| //! |
| //! assert( !exception_wrapper{}.with_exception([](int& e){/*...*/}) ); |
| //! \endcode |
| //! \tparam Ex Optionally, the type of the exception that `fn` accepts. |
| //! \tparam Fn The type of a monomophic function object. |
| //! \param fn A function object to call with the wrapped exception |
| //! \return `true` if and only if `fn` was called. |
| //! \note Optionally, you may explicitly specify the type of the exception |
| //! that `fn` expects, as in |
| //! \code |
| //! ew.with_exception<std::runtime_error>([](auto&& e) { /*...*/; }); |
| //! \endcode |
| //! \note The handler may or may not be invoked with an active exception. |
| //! **Do not try to rethrow the exception with `throw;` from within your |
| //! handler -- that is, a throw expression with no operand.** This may |
| //! cause your process to terminate. (It is perfectly ok to throw from |
| //! a handler so long as you specify the exception to throw, as in |
| //! `throw e;`.) |
| template <class Ex = void const, class Fn> |
| bool with_exception(Fn fn); |
| //! \overload |
| template <class Ex = void const, class Fn> |
| bool with_exception(Fn fn) const; |
| |
| //! Handle the wrapped expression as if with a series of `catch` clauses, |
| //! propagating the exception if no handler matches. |
| //! \par Example |
| //! \code |
| //! exception_wrapper ew{std::runtime_error("goodbye cruel world")}; |
| //! |
| //! ew.handle( |
| //! [&](std::logic_error const& e) { |
| //! LOG(DFATAL) << "ruh roh"; |
| //! ew.throw_exception(); // rethrow the active exception without |
| //! // slicing it. Will not be caught by other |
| //! // handlers in this call. |
| //! }, |
| //! [&](std::exception const& e) { |
| //! LOG(ERROR) << ew.what(); |
| //! }); |
| //! \endcode |
| //! In the above example, any exception _not_ derived from `std::exception` |
| //! will be propagated. To specify a catch-all clause, pass a lambda that |
| //! takes a C-style elipses, as in: |
| //! \code |
| //! ew.handle(/*...* /, [](...) { /* handle unknown exception */ } ) |
| //! \endcode |
| //! \pre `!*this` |
| //! \tparam CatchFns... A pack of unary monomorphic function object types. |
| //! \param fns A pack of unary monomorphic function objects to be treated as |
| //! an ordered list of potential exception handlers. |
| //! \note The handlers may or may not be invoked with an active exception. |
| //! **Do not try to rethrow the exception with `throw;` from within your |
| //! handler -- that is, a throw expression with no operand.** This may |
| //! cause your process to terminate. (It is perfectly ok to throw from |
| //! a handler so long as you specify the exception to throw, as in |
| //! `throw e;`.) |
| template <class... CatchFns> |
| void handle(CatchFns... fns); |
| //! \overload |
| template <class... CatchFns> |
| void handle(CatchFns... fns) const; |
| }; |
| |
| template <class Ex> |
| constexpr exception_wrapper::VTable exception_wrapper::InPlace<Ex>::ops_; |
| |
| /** |
| * \return An `exception_wrapper` that wraps an instance of type `Ex` |
| * that has been constructed with arguments `std::forward<As>(as)...`. |
| */ |
| template <class Ex, typename... As> |
| exception_wrapper make_exception_wrapper(As&&... as) { |
| return exception_wrapper{in_place_type<Ex>, std::forward<As>(as)...}; |
| } |
| |
| /** |
| * Inserts `ew.what()` into the ostream `sout`. |
| * \return `sout` |
| */ |
| template <class Ch> |
| std::basic_ostream<Ch>& operator<<( |
| std::basic_ostream<Ch>& sout, |
| exception_wrapper const& ew) { |
| return sout << ew.what(); |
| } |
| |
| /** |
| * Swaps the value of `a` with the value of `b`. |
| */ |
| inline void swap(exception_wrapper& a, exception_wrapper& b) noexcept { |
| a.swap(b); |
| } |
| |
| // For consistency with exceptionStr() functions in ExceptionString.h |
| fbstring exceptionStr(exception_wrapper const& ew); |
| |
| namespace detail { |
| template <typename F> |
| inline exception_wrapper try_and_catch_(F&& f) { |
| return (f(), exception_wrapper()); |
| } |
| |
| template <typename F, typename Ex, typename... Exs> |
| inline exception_wrapper try_and_catch_(F&& f) { |
| try { |
| return try_and_catch_<F, Exs...>(std::forward<F>(f)); |
| } catch (Ex& ex) { |
| return exception_wrapper(std::current_exception(), ex); |
| } |
| } |
| } // detail |
| |
| //! `try_and_catch` is a simple replacement for `try {} catch(){}`` that allows |
| //! you to specify which derived exceptions you would like to catch and store in |
| //! an `exception_wrapper`. |
| //! |
| //! Because we cannot build an equivalent of `std::current_exception()`, we need |
| //! to catch every derived exception that we are interested in catching. |
| //! |
| //! Exceptions should be listed in the reverse order that you would write your |
| //! catch statements (that is, `std::exception&` should be first). |
| //! |
| //! \par Example Usage: |
| //! \code |
| //! // This catches my runtime_error and if I call throw_exception() on ew, it |
| //! // will throw a runtime_error |
| //! auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() { |
| //! if (badThingHappens()) { |
| //! throw std::runtime_error("ZOMG!"); |
| //! } |
| //! }); |
| //! |
| //! // This will catch the exception and if I call throw_exception() on ew, it |
| //! // will throw a std::exception |
| //! auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() { |
| //! if (badThingHappens()) { |
| //! throw std::exception(); |
| //! } |
| //! }); |
| //! |
| //! // This will not catch the exception and it will be thrown. |
| //! auto ew = folly::try_and_catch<std::runtime_error>([=]() { |
| //! if (badThingHappens()) { |
| //! throw std::exception(); |
| //! } |
| //! }); |
| //! \endcode |
| template <typename... Exceptions, typename F> |
| exception_wrapper try_and_catch(F&& fn) { |
| return detail::try_and_catch_<F, Exceptions...>(std::forward<F>(fn)); |
| } |
| } // folly |
| |
| #include <folly/ExceptionWrapper-inl.h> |
| |
| #undef FOLLY_REQUIRES |
| #undef FOLLY_REQUIRES_DEF |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic pop |
| #endif |