+#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();
+
+ 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, class DEx = _t<std::decay<Ex>>>
+ Buffer(in_place_t, Ex&& ex);
+ 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 const& e);
+ static std::uintptr_t as_int_(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;
+ explicit Impl(Ex const& ex) : Base{typeid(ex)}, ex_(ex) {}
+ explicit Impl(Ex&& ex)
+ : Base{typeid(ex)},
+ ex_(std::move(ex)){}[[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, class DEx = _t<std::decay<Ex>>>
+ exception_wrapper(Ex&& ex, OnHeapTag);
+
+ template <class Ex, class DEx = _t<std::decay<Ex>>>
+ exception_wrapper(Ex&& ex, InSituTag);
+
+ 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_);
+