1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Author: Eric Niebler <eniebler@fb.com>
19  */
20 
21 #pragma once
22 
23 #include <cassert>
24 #include <cstdint>
25 #include <exception>
26 #include <iosfwd>
27 #include <memory>
28 #include <new>
29 #include <type_traits>
30 #include <typeinfo>
31 #include <utility>
32 
33 #include <folly/CPortability.h>
34 #include <folly/CppAttributes.h>
35 #include <folly/Demangle.h>
36 #include <folly/ExceptionString.h>
37 #include <folly/FBString.h>
38 #include <folly/Portability.h>
39 #include <folly/Traits.h>
40 #include <folly/Utility.h>
41 #include <folly/lang/Assume.h>
42 #include <folly/lang/Exception.h>
43 
44 #define FOLLY_EXCEPTION_WRAPPER_H_INCLUDED
45 
46 namespace folly {
47 
48 #define FOLLY_REQUIRES_DEF(...) \
49   std::enable_if_t<static_cast<bool>(__VA_ARGS__), long>
50 
51 #define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__
52 
53 //! Throwing exceptions can be a convenient way to handle errors. Storing
54 //! exceptions in an `exception_ptr` makes it easy to handle exceptions in a
55 //! different thread or at a later time. `exception_ptr` can also be used in a
56 //! very generic result/exception wrapper.
57 //!
58 //! However, there are some issues with throwing exceptions and
59 //! `std::exception_ptr`. These issues revolve around `throw` being expensive,
60 //! particularly in a multithreaded environment (see
61 //! ExceptionWrapperBenchmark.cpp).
62 //!
63 //! Imagine we have a library that has an API which returns a result/exception
64 //! wrapper. Let's consider some approaches for implementing this wrapper.
65 //! First, we could store a `std::exception`. This approach loses the derived
66 //! exception type, which can make exception handling more difficult for users
67 //! that prefer rethrowing the exception. We could use a `folly::dynamic` for
68 //! every possible type of exception. This is not very flexible - adding new
69 //! types of exceptions requires a change to the result/exception wrapper. We
70 //! could use an `exception_ptr`. However, constructing an `exception_ptr` as
71 //! well as accessing the error requires a call to throw. That means that there
72 //! will be two calls to throw in order to process the exception. For
73 //! performance sensitive applications, this may be unacceptable.
74 //!
75 //! `exception_wrapper` is designed to handle exception management for both
76 //! convenience and high performance use cases. `make_exception_wrapper` is
77 //! templated on derived type, allowing us to rethrow the exception properly for
78 //! users that prefer convenience. These explicitly named exception types can
79 //! therefore be handled without any performance penalty. `exception_wrapper` is
80 //! also flexible enough to accept any type. If a caught exception is not of an
81 //! explicitly named type, then `std::exception_ptr` is used to preserve the
82 //! exception state. For performance sensitive applications, the accessor
83 //! methods can test or extract a pointer to a specific exception type with very
84 //! little overhead.
85 //!
86 //! \par Example usage:
87 //! \code
88 //! exception_wrapper globalExceptionWrapper;
89 //!
90 //! // Thread1
91 //! void doSomethingCrazy() {
92 //!   int rc = doSomethingCrazyWithLameReturnCodes();
93 //!   if (rc == NAILED_IT) {
94 //!     globalExceptionWrapper = exception_wrapper();
95 //!   } else if (rc == FACE_PLANT) {
96 //!     globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
97 //!   } else if (rc == FAIL_WHALE) {
98 //!     globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
99 //!   }
100 //! }
101 //!
102 //! // Thread2: Exceptions are ok!
103 //! void processResult() {
104 //!   try {
105 //!     globalExceptionWrapper.throw_exception();
106 //!   } catch (const FacePlantException& e) {
107 //!     LOG(ERROR) << "FACEPLANT!";
108 //!   } catch (const FailWhaleException& e) {
109 //!     LOG(ERROR) << "FAILWHALE!";
110 //!   }
111 //! }
112 //!
113 //! // Thread2: Exceptions are bad!
114 //! void processResult() {
115 //!   globalExceptionWrapper.handle(
116 //!       [&](FacePlantException& faceplant) {
117 //!         LOG(ERROR) << "FACEPLANT";
118 //!       },
119 //!       [&](FailWhaleException& failwhale) {
120 //!         LOG(ERROR) << "FAILWHALE!";
121 //!       },
122 //!       [](...) {
123 //!         LOG(FATAL) << "Unrecognized exception";
124 //!       });
125 //! }
126 //! \endcode
127 class exception_wrapper final {
128  private:
129   struct FOLLY_EXPORT AnyException : std::exception {
130     std::type_info const* typeinfo_;
131     template <class T>
AnyExceptionAnyException132     /* implicit */ AnyException(T&& t) noexcept : typeinfo_(&typeid(t)) {}
133   };
134 
135   template <class Fn>
136   struct arg_type_;
137   template <class Fn>
138   using arg_type = _t<arg_type_<Fn>>;
139 
140   struct with_exception_from_fn_;
141   struct with_exception_from_ex_;
142 
143   // exception_wrapper is implemented as a simple variant over four
144   // different representations:
145   //  0. Empty, no exception.
146   //  1. An small object stored in-situ.
147   //  2. A larger object stored on the heap and referenced with a
148   //     std::shared_ptr.
149   //  3. A std::exception_ptr.
150   // This is accomplished with the help of a union and a pointer to a hand-
151   // rolled virtual table. This virtual table contains pointers to functions
152   // that know which field of the union is active and do the proper action.
153   // The class invariant ensures that the vtable ptr and the union stay in sync.
154   struct VTable {
155     void (*copy_)(exception_wrapper const*, exception_wrapper*);
156     void (*move_)(exception_wrapper*, exception_wrapper*);
157     void (*delete_)(exception_wrapper*);
158     void (*throw_)(exception_wrapper const*);
159     std::type_info const* (*type_)(exception_wrapper const*);
160     std::exception const* (*get_exception_)(exception_wrapper const*);
161     exception_wrapper (*get_exception_ptr_)(exception_wrapper const*);
162   };
163 
164   [[noreturn]] static void onNoExceptionError(char const* name);
165 
166   template <class Ret, class... Args>
167   static Ret noop_(Args...);
168 
169   static std::type_info const* uninit_type_(exception_wrapper const*);
170 
171   static VTable const uninit_;
172 
173   template <class Ex>
174   using IsStdException = std::is_base_of<std::exception, std::decay_t<Ex>>;
175   template <class CatchFn>
176   using IsCatchAll =
177       std::is_same<arg_type<std::decay_t<CatchFn>>, AnyException>;
178 
179   // Sadly, with the gcc-4.9 platform, std::logic_error and std::runtime_error
180   // do not fit here. They also don't have noexcept copy-ctors, so the internal
181   // storage wouldn't be used anyway. For the gcc-5 platform, both logic_error
182   // and runtime_error can be safely stored internally.
183   struct Buffer {
184     using Storage =
185         std::aligned_storage_t<2 * sizeof(void*), alignof(std::exception)>;
186     Storage buff_;
187 
BufferBuffer188     Buffer() : buff_{} {}
189 
190     template <class Ex, typename... As>
191     Buffer(in_place_type_t<Ex>, As&&... as_);
192     template <class Ex>
193     Ex& as() noexcept;
194     template <class Ex>
195     Ex const& as() const noexcept;
196   };
197 
198   struct ThrownTag {};
199   struct InSituTag {};
200   struct OnHeapTag {};
201 
202   template <class T>
203   using PlacementOf = std::conditional_t<
204       !IsStdException<T>::value,
205       ThrownTag,
206       std::conditional_t<
207           sizeof(T) <= sizeof(Buffer::Storage) &&
208               alignof(T) <=
209                   alignof(Buffer::Storage)&& noexcept(
210                       T(std::declval<
211                           T&&>()))&& noexcept(T(std::declval<T const&>())),
212           InSituTag,
213           OnHeapTag>>;
214 
215   static std::exception const* as_exception_or_null_(std::exception const& ex);
216   static std::exception const* as_exception_or_null_(AnyException);
217 
218   struct ExceptionPtr {
219     std::exception_ptr ptr_;
220 
221     static void copy_(exception_wrapper const* from, exception_wrapper* to);
222     static void move_(exception_wrapper* from, exception_wrapper* to);
223     static void delete_(exception_wrapper* that);
224     [[noreturn]] static void throw_(exception_wrapper const* that);
225     static std::type_info const* type_(exception_wrapper const* that);
226     static std::exception const* get_exception_(exception_wrapper const* that);
227     static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
228     static VTable const ops_;
229   };
230 
231   template <class Ex>
232   struct InPlace {
233     static_assert(IsStdException<Ex>::value, "only deriving std::exception");
234     static void copy_(exception_wrapper const* from, exception_wrapper* to);
235     static void move_(exception_wrapper* from, exception_wrapper* to);
236     static void delete_(exception_wrapper* that);
237     [[noreturn]] static void throw_(exception_wrapper const* that);
238     static std::type_info const* type_(exception_wrapper const*);
239     static std::exception const* get_exception_(exception_wrapper const* that);
240     static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
241     static constexpr VTable const ops_{
242         copy_,
243         move_,
244         delete_,
245         throw_,
246         type_,
247         get_exception_,
248         get_exception_ptr_};
249   };
250 
251   struct SharedPtr {
252     struct Base {
253       std::type_info const* info_;
254       Base() = delete;
BaseSharedPtr::Base255       explicit Base(std::type_info const& info) : info_(&info) {}
~BaseSharedPtr::Base256       virtual ~Base() {}
257       virtual void throw_() const = 0;
258       virtual std::exception const* get_exception_() const noexcept = 0;
259       virtual exception_wrapper get_exception_ptr_() const noexcept = 0;
260     };
261     template <class Ex>
262     struct Impl final : public Base {
263       static_assert(IsStdException<Ex>::value, "only deriving std::exception");
264       Ex ex_;
ImplSharedPtr::final265       Impl() : Base{typeid(Ex)}, ex_() {}
266       // clang-format off
267       template <typename... As>
ImplSharedPtr::final268       explicit Impl(As&&... as)
269           : Base{typeid(Ex)}, ex_(std::forward<As>(as)...) {}
270       [[noreturn]] void throw_() const override;
271       // clang-format on
272       std::exception const* get_exception_() const noexcept override;
273       exception_wrapper get_exception_ptr_() const noexcept override;
274     };
275     std::shared_ptr<Base> ptr_;
276 
277     static void copy_(exception_wrapper const* from, exception_wrapper* to);
278     static void move_(exception_wrapper* from, exception_wrapper* to);
279     static void delete_(exception_wrapper* that);
280     [[noreturn]] static void throw_(exception_wrapper const* that);
281     static std::type_info const* type_(exception_wrapper const* that);
282     static std::exception const* get_exception_(exception_wrapper const* that);
283     static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
284     static VTable const ops_;
285   };
286 
287   union {
288     Buffer buff_{};
289     ExceptionPtr eptr_;
290     SharedPtr sptr_;
291   };
292   VTable const* vptr_{&uninit_};
293 
294   template <class Ex, typename... As>
295   exception_wrapper(ThrownTag, in_place_type_t<Ex>, As&&... as);
296 
297   template <class Ex, typename... As>
298   exception_wrapper(OnHeapTag, in_place_type_t<Ex>, As&&... as);
299 
300   template <class Ex, typename... As>
301   exception_wrapper(InSituTag, in_place_type_t<Ex>, As&&... as);
302 
303   template <class T>
304   struct IsRegularExceptionType
305       : StrictConjunction<
306             std::is_copy_constructible<T>,
307             Negation<std::is_base_of<exception_wrapper, T>>,
308             Negation<std::is_abstract<T>>> {};
309 
310   template <class This, class Fn>
311   static bool with_exception_(This& this_, Fn fn_, tag_t<AnyException>);
312 
313   template <class This, class Fn, typename Ex>
314   static bool with_exception_(This& this_, Fn fn_, tag_t<Ex>);
315 
316   template <class Ex, class This, class Fn>
317   static bool with_exception_(This& this_, Fn fn_);
318 
319   template <class This, class... CatchFns>
320   static void handle_(This& this_, char const* name, CatchFns&... fns);
321 
322  public:
323   static exception_wrapper from_exception_ptr(
324       std::exception_ptr const& eptr) noexcept;
325   static exception_wrapper from_exception_ptr(
326       std::exception_ptr&& eptr) noexcept;
327 
328   //! Default-constructs an empty `exception_wrapper`
329   //! \post `type() == none()`
exception_wrapper()330   exception_wrapper() noexcept {}
331 
332   //! Move-constructs an `exception_wrapper`
333   //! \post `*this` contains the value of `that` prior to the move
334   //! \post `that.type() == none()`
335   exception_wrapper(exception_wrapper&& that) noexcept;
336 
337   //! Copy-constructs an `exception_wrapper`
338   //! \post `*this` contains a copy of `that`, and `that` is unmodified
339   //! \post `type() == that.type()`
340   exception_wrapper(exception_wrapper const& that) noexcept;
341 
342   //! Move-assigns an `exception_wrapper`
343   //! \pre `this != &that`
344   //! \post `*this` contains the value of `that` prior to the move
345   //! \post `that.type() == none()`
346   exception_wrapper& operator=(exception_wrapper&& that) noexcept;
347 
348   //! Copy-assigns an `exception_wrapper`
349   //! \post `*this` contains a copy of `that`, and `that` is unmodified
350   //! \post `type() == that.type()`
351   exception_wrapper& operator=(exception_wrapper const& that) noexcept;
352 
353   ~exception_wrapper();
354 
355   //! \post `!ptr || bool(*this)`
356   explicit exception_wrapper(std::exception_ptr const& ptr) noexcept;
357   explicit exception_wrapper(std::exception_ptr&& ptr) noexcept;
358 
359   //! \pre `ptr` holds a reference to `ex`.
360   //! \post `bool(*this)`
361   //! \post `type() == typeid(ex)`
362   template <class Ex>
363   exception_wrapper(std::exception_ptr const& ptr, Ex& ex) noexcept;
364   template <class Ex>
365   exception_wrapper(std::exception_ptr&& ptr, Ex& ex) noexcept;
366 
367   //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)`
368   //! \post `bool(*this)`
369   //! \post `type() == typeid(ex)`
370   //! \note Exceptions of types derived from `std::exception` can be implicitly
371   //!     converted to an `exception_wrapper`.
372   template <
373       class Ex,
374       class Ex_ = std::decay_t<Ex>,
375       FOLLY_REQUIRES(
376           Conjunction<IsStdException<Ex_>, IsRegularExceptionType<Ex_>>::value)>
377   /* implicit */ exception_wrapper(Ex&& ex);
378 
379   //! \pre `typeid(ex) == typeid(typename decay<Ex>::type)`
380   //! \post `bool(*this)`
381   //! \post `type() == typeid(ex)`
382   //! \note Exceptions of types not derived from `std::exception` can still be
383   //!     used to construct an `exception_wrapper`, but you must specify
384   //!     `folly::in_place` as the first parameter.
385   template <
386       class Ex,
387       class Ex_ = std::decay_t<Ex>,
388       FOLLY_REQUIRES(IsRegularExceptionType<Ex_>::value)>
389   exception_wrapper(in_place_t, Ex&& ex);
390 
391   template <
392       class Ex,
393       typename... As,
394       FOLLY_REQUIRES(IsRegularExceptionType<Ex>::value)>
395   exception_wrapper(in_place_type_t<Ex>, As&&... as);
396 
397   //! Swaps the value of `*this` with the value of `that`
398   void swap(exception_wrapper& that) noexcept;
399 
400   //! \return `true` if `*this` is holding an exception.
401   explicit operator bool() const noexcept;
402 
403   //! \return `!bool(*this)`
404   bool operator!() const noexcept;
405 
406   //! Make this `exception_wrapper` empty
407   //! \post `!*this`
408   void reset();
409 
410   //! \return `true` if this `exception_wrapper` holds a reference to an
411   //!     exception that was thrown (i.e., if it was constructed with
412   //!     a `std::exception_ptr`, or if `to_exception_ptr()` was called on a
413   //!     (non-const) reference to `*this`).
414   bool has_exception_ptr() const noexcept;
415 
416   //! \return a pointer to the `std::exception` held by `*this`, if it holds
417   //!     one; otherwise, returns `nullptr`.
418   //! \note This function does not mutate the `exception_wrapper` object.
419   //! \note This function never causes an exception to be thrown.
420   std::exception* get_exception() noexcept;
421   //! \overload
422   std::exception const* get_exception() const noexcept;
423 
424   //! \returns a pointer to the `Ex` held by `*this`, if it holds an object
425   //!     whose type `From` permits `std::is_convertible<From*, Ex*>`;
426   //!     otherwise, returns `nullptr`.
427   //! \note This function does not mutate the `exception_wrapper` object.
428   //! \note This function may cause an exception to be thrown and immediately
429   //!     caught internally, affecting runtime performance.
430   template <typename Ex>
431   Ex* get_exception() noexcept;
432   //! \overload
433   template <typename Ex>
434   Ex const* get_exception() const noexcept;
435 
436   //! \return A `std::exception_ptr` that references either the exception held
437   //!     by `*this`, or a copy of same.
438   //! \note This function may need to throw an exception to complete the action.
439   //! \note The non-const overload of this function mutates `*this` to cache the
440   //!     computed `std::exception_ptr`; that is, this function may cause
441   //!     `has_exception_ptr()` to change from `false` to `true`.
442   std::exception_ptr to_exception_ptr() noexcept;
443   //! \overload
444   std::exception_ptr to_exception_ptr() const noexcept;
445 
446   //! \return the `typeid` of an unspecified type used by
447   //!     `exception_wrapper::type()` to denote an empty `exception_wrapper`.
448   static std::type_info const& none() noexcept;
449 
450   //! Returns the `typeid` of the wrapped exception object. If there is no
451   //!     wrapped exception object, returns `exception_wrapper::none()`.
452   std::type_info const& type() const noexcept;
453 
454   //! \return If `get_exception() != nullptr`, `class_name() + ": " +
455   //!     get_exception()->what()`; otherwise, `class_name()`.
456   folly::fbstring what() const;
457 
458   //! \return If `!*this`, the empty string; otherwise,
459   //!     the result of `type().name()` after demangling.
460   folly::fbstring class_name() const;
461 
462   //! \tparam Ex The expression type to check for compatibility with.
463   //! \return `true` if and only if `*this` wraps an exception that would be
464   //!     caught with a `catch(Ex const&)` clause.
465   //! \note If `*this` is empty, this function returns `false`.
466   template <class Ex>
467   bool is_compatible_with() const noexcept;
468 
469   //! Throws the wrapped expression.
470   //! \pre `bool(*this)`
471   [[noreturn]] void throw_exception() const;
472 
473   //! Terminates the process with the wrapped expression.
terminate_with()474   [[noreturn]] void terminate_with() const noexcept { throw_exception(); }
475 
476   //! Throws the wrapped expression nested into another exception.
477   //! \pre `bool(*this)`
478   //! \param ex Exception in *this will be thrown nested into ex;
479   //      see std::throw_with_nested() for details on this semantic.
480   template <class Ex>
481   [[noreturn]] void throw_with_nested(Ex&& ex) const;
482 
483   //! Call `fn` with the wrapped exception (if any), if `fn` can accept it.
484   //! \par Example
485   //! \code
486   //! exception_wrapper ew{std::runtime_error("goodbye cruel world")};
487   //!
488   //! assert( ew.with_exception([](std::runtime_error& e){/*...*/}) );
489   //!
490   //! assert( !ew.with_exception([](int& e){/*...*/}) );
491   //!
492   //! assert( !exception_wrapper{}.with_exception([](int& e){/*...*/}) );
493   //! \endcode
494   //! \tparam Ex Optionally, the type of the exception that `fn` accepts.
495   //! \tparam Fn The type of a monomophic function object.
496   //! \param fn A function object to call with the wrapped exception
497   //! \return `true` if and only if `fn` was called.
498   //! \note Optionally, you may explicitly specify the type of the exception
499   //!     that `fn` expects, as in
500   //! \code
501   //! ew.with_exception<std::runtime_error>([](auto&& e) { /*...*/; });
502   //! \endcode
503   //! \note The handler is not invoked with an active exception.
504   //!     **Do not try to rethrow the exception with `throw;` from within your
505   //!     handler -- that is, a throw expression with no operand.** This may
506   //!     cause your process to terminate. (It is perfectly ok to throw from
507   //!     a handler so long as you specify the exception to throw, as in
508   //!     `throw e;`.)
509   template <class Ex = void const, class Fn>
510   bool with_exception(Fn fn);
511   //! \overload
512   template <class Ex = void const, class Fn>
513   bool with_exception(Fn fn) const;
514 
515   //! Handle the wrapped expression as if with a series of `catch` clauses,
516   //!     propagating the exception if no handler matches.
517   //! \par Example
518   //! \code
519   //! exception_wrapper ew{std::runtime_error("goodbye cruel world")};
520   //!
521   //! ew.handle(
522   //!   [&](std::logic_error const& e) {
523   //!      LOG(DFATAL) << "ruh roh";
524   //!      ew.throw_exception(); // rethrow the active exception without
525   //!                           // slicing it. Will not be caught by other
526   //!                           // handlers in this call.
527   //!   },
528   //!   [&](std::exception const& e) {
529   //!      LOG(ERROR) << ew.what();
530   //!   });
531   //! \endcode
532   //! In the above example, any exception _not_ derived from `std::exception`
533   //!     will be propagated. To specify a catch-all clause, pass a lambda that
534   //!     takes a C-style ellipses, as in:
535   //! \code
536   //! ew.handle(/*...* /, [](...) { /* handle unknown exception */ } )
537   //! \endcode
538   //! \pre `!*this`
539   //! \tparam CatchFns A pack of unary monomorphic function object types.
540   //! \param fns A pack of unary monomorphic function objects to be treated as
541   //!     an ordered list of potential exception handlers.
542   //! \note The handlers are not invoked with an active exception.
543   //!     **Do not try to rethrow the exception with `throw;` from within your
544   //!     handler -- that is, a throw expression with no operand.** This may
545   //!     cause your process to terminate. (It is perfectly ok to throw from
546   //!     a handler so long as you specify the exception to throw, as in
547   //!     `throw e;`.)
548   template <class... CatchFns>
549   void handle(CatchFns... fns);
550   //! \overload
551   template <class... CatchFns>
552   void handle(CatchFns... fns) const;
553 };
554 
555 template <class Ex>
556 constexpr exception_wrapper::VTable exception_wrapper::InPlace<Ex>::ops_;
557 
558 /**
559  * \return An `exception_wrapper` that wraps an instance of type `Ex`
560  *     that has been constructed with arguments `std::forward<As>(as)...`.
561  */
562 template <class Ex, typename... As>
make_exception_wrapper(As &&...as)563 exception_wrapper make_exception_wrapper(As&&... as) {
564   return exception_wrapper{in_place_type<Ex>, std::forward<As>(as)...};
565 }
566 
567 /**
568  * Inserts `ew.what()` into the ostream `sout`.
569  * \return `sout`
570  */
571 template <class Ch>
572 std::basic_ostream<Ch>& operator<<(
573     std::basic_ostream<Ch>& sout, exception_wrapper const& ew) {
574   return sout << ew.what();
575 }
576 
577 /**
578  * Swaps the value of `a` with the value of `b`.
579  */
swap(exception_wrapper & a,exception_wrapper & b)580 inline void swap(exception_wrapper& a, exception_wrapper& b) noexcept {
581   a.swap(b);
582 }
583 
584 // For consistency with exceptionStr() functions in ExceptionString.h
585 fbstring exceptionStr(exception_wrapper const& ew);
586 
587 //! `try_and_catch` is a convenience for `try {} catch(...) {}`` that returns an
588 //! `exception_wrapper` with the thrown exception, if any.
589 template <typename F>
try_and_catch(F && fn)590 exception_wrapper try_and_catch(F&& fn) noexcept {
591   auto x = [&] { return void(static_cast<F&&>(fn)()), std::exception_ptr{}; };
592   return exception_wrapper{catch_exception(x, std::current_exception)};
593 }
594 } // namespace folly
595 
596 #include <folly/ExceptionWrapper-inl.h>
597 
598 #undef FOLLY_REQUIRES
599 #undef FOLLY_REQUIRES_DEF
600