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