ExceptionWrapper.h 27 KB

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