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. |
47 | FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS |
48 | #endif |
49 | |
50 | #define FOLLY_EXCEPTION_WRAPPER_H_INCLUDED |
51 | |
52 | namespace 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 | |
59 | namespace exception_wrapper_detail { |
60 | |
61 | template <template <class> class T, class... As> |
62 | using AllOf = StrictConjunction<T<As>...>; |
63 | |
64 | template <bool If, class T> |
65 | using AddConstIf = std::conditional_t<If, const T, T>; |
66 | |
67 | template <class Fn, class A> |
68 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto fold(Fn&&, A&& a) { |
69 | return static_cast<A&&>(a); |
70 | } |
71 | |
72 | template <class Fn, class A, class B, class... Bs> |
73 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto |
74 | fold(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 |
162 | class 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 | |
618 | template <class Ex> |
619 | constexpr 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 | */ |
625 | template <class Ex, typename... As> |
626 | exception_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 | */ |
634 | template <class Ch> |
635 | std::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 | */ |
644 | inline void swap(exception_wrapper& a, exception_wrapper& b) noexcept { |
645 | a.swap(b); |
646 | } |
647 | |
648 | // For consistency with exceptionStr() functions in ExceptionString.h |
649 | fbstring exceptionStr(exception_wrapper const& ew); |
650 | |
651 | namespace detail { |
652 | template <typename F> |
653 | inline exception_wrapper try_and_catch_(F&& f) { |
654 | return (f(), exception_wrapper()); |
655 | } |
656 | |
657 | template <typename F, typename Ex, typename... Exs> |
658 | inline 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 |
702 | template <typename... Exceptions, typename F> |
703 | exception_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 | |