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 | * |
19 | * Author: Eric Niebler <[email protected]> |
20 | */ |
21 | |
22 | #include <folly/Portability.h> |
23 | |
24 | namespace folly { |
25 | |
26 | template <class Fn> |
27 | struct exception_wrapper::arg_type_ |
28 | : public arg_type_<decltype(&Fn::operator())> {}; |
29 | template <class Ret, class Class, class Arg> |
30 | struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> { |
31 | using type = Arg; |
32 | }; |
33 | template <class Ret, class Class, class Arg> |
34 | struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> { |
35 | using type = Arg; |
36 | }; |
37 | template <class Ret, class Arg> |
38 | struct exception_wrapper::arg_type_<Ret(Arg)> { |
39 | using type = Arg; |
40 | }; |
41 | template <class Ret, class Arg> |
42 | struct exception_wrapper::arg_type_<Ret (*)(Arg)> { |
43 | using type = Arg; |
44 | }; |
45 | template <class Ret, class Class> |
46 | struct exception_wrapper::arg_type_<Ret (Class::*)(...)> { |
47 | using type = AnyException; |
48 | }; |
49 | template <class Ret, class Class> |
50 | struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> { |
51 | using type = AnyException; |
52 | }; |
53 | template <class Ret> |
54 | struct exception_wrapper::arg_type_<Ret(...)> { |
55 | using type = AnyException; |
56 | }; |
57 | template <class Ret> |
58 | struct exception_wrapper::arg_type_<Ret (*)(...)> { |
59 | using type = AnyException; |
60 | }; |
61 | |
62 | template <class Ret, class... Args> |
63 | inline Ret exception_wrapper::noop_(Args...) { |
64 | return Ret(); |
65 | } |
66 | |
67 | inline std::type_info const* exception_wrapper::uninit_type_( |
68 | exception_wrapper const*) { |
69 | return &typeid(void); |
70 | } |
71 | |
72 | template <class Ex, typename... As> |
73 | inline exception_wrapper::Buffer::Buffer(in_place_type_t<Ex>, As&&... as_) { |
74 | ::new (static_cast<void*>(&buff_)) Ex(std::forward<As>(as_)...); |
75 | } |
76 | |
77 | template <class Ex> |
78 | inline Ex& exception_wrapper::Buffer::as() noexcept { |
79 | return *static_cast<Ex*>(static_cast<void*>(&buff_)); |
80 | } |
81 | template <class Ex> |
82 | inline Ex const& exception_wrapper::Buffer::as() const noexcept { |
83 | return *static_cast<Ex const*>(static_cast<void const*>(&buff_)); |
84 | } |
85 | |
86 | inline std::exception const* exception_wrapper::as_exception_or_null_( |
87 | std::exception const& ex) { |
88 | return &ex; |
89 | } |
90 | inline std::exception const* exception_wrapper::as_exception_or_null_( |
91 | AnyException) { |
92 | return nullptr; |
93 | } |
94 | |
95 | static_assert( |
96 | !kMicrosoftAbiVer || (kMicrosoftAbiVer >= 1900 && kMicrosoftAbiVer <= 2000), |
97 | "exception_wrapper is untested and possibly broken on your version of " |
98 | "MSVC" ); |
99 | |
100 | inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( |
101 | std::exception_ptr const& ptr, |
102 | std::exception const& e) noexcept { |
103 | if (!kMicrosoftAbiVer) { |
104 | return reinterpret_cast<std::uintptr_t>(&e); |
105 | } else { |
106 | // On Windows, as of MSVC2017, all thrown exceptions are copied to the stack |
107 | // first. Thus, we cannot depend on exception references associated with an |
108 | // exception_ptr to be live for the duration of the exception_ptr. We need |
109 | // to directly access the heap allocated memory inside the exception_ptr. |
110 | // |
111 | // std::exception_ptr is an opaque reinterpret_cast of |
112 | // std::shared_ptr<__ExceptionPtr> |
113 | // __ExceptionPtr is a non-virtual class with two members, a union and a |
114 | // bool. The union contains the now-undocumented EHExceptionRecord, which |
115 | // contains a struct which contains a void* which points to the heap |
116 | // allocated exception. |
117 | // We derive the offset to pExceptionObject via manual means. |
118 | FOLLY_PACK_PUSH |
119 | struct Win32ExceptionPtr { |
120 | char offset[8 + 4 * sizeof(void*)]; |
121 | void* exceptionObject; |
122 | } FOLLY_PACK_ATTR; |
123 | FOLLY_PACK_POP |
124 | |
125 | auto* win32ExceptionPtr = |
126 | reinterpret_cast<std::shared_ptr<Win32ExceptionPtr> const*>(&ptr) |
127 | ->get(); |
128 | return reinterpret_cast<std::uintptr_t>(win32ExceptionPtr->exceptionObject); |
129 | } |
130 | } |
131 | inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( |
132 | std::exception_ptr const&, |
133 | AnyException e) noexcept { |
134 | return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1; |
135 | } |
136 | inline bool exception_wrapper::ExceptionPtr::has_exception_() const { |
137 | return 0 == exception_or_type_ % 2; |
138 | } |
139 | inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_() |
140 | const { |
141 | return reinterpret_cast<std::exception const*>(exception_or_type_); |
142 | } |
143 | inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const { |
144 | return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1); |
145 | } |
146 | |
147 | inline void exception_wrapper::ExceptionPtr::copy_( |
148 | exception_wrapper const* from, |
149 | exception_wrapper* to) { |
150 | ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_); |
151 | } |
152 | inline void exception_wrapper::ExceptionPtr::move_( |
153 | exception_wrapper* from, |
154 | exception_wrapper* to) { |
155 | ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(std::move(from->eptr_)); |
156 | delete_(from); |
157 | } |
158 | inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) { |
159 | that->eptr_.~ExceptionPtr(); |
160 | that->vptr_ = &uninit_; |
161 | } |
162 | [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_( |
163 | exception_wrapper const* that) { |
164 | std::rethrow_exception(that->eptr_.ptr_); |
165 | } |
166 | inline std::type_info const* exception_wrapper::ExceptionPtr::type_( |
167 | exception_wrapper const* that) { |
168 | if (auto e = get_exception_(that)) { |
169 | return &typeid(*e); |
170 | } |
171 | return that->eptr_.as_type_(); |
172 | } |
173 | inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_( |
174 | exception_wrapper const* that) { |
175 | return that->eptr_.has_exception_() ? that->eptr_.as_exception_() : nullptr; |
176 | } |
177 | inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_( |
178 | exception_wrapper const* that) { |
179 | return *that; |
180 | } |
181 | |
182 | template <class Ex> |
183 | inline void exception_wrapper::InPlace<Ex>::copy_( |
184 | exception_wrapper const* from, |
185 | exception_wrapper* to) { |
186 | ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) |
187 | Ex(from->buff_.as<Ex>()); |
188 | } |
189 | template <class Ex> |
190 | inline void exception_wrapper::InPlace<Ex>::move_( |
191 | exception_wrapper* from, |
192 | exception_wrapper* to) { |
193 | ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) |
194 | Ex(std::move(from->buff_.as<Ex>())); |
195 | delete_(from); |
196 | } |
197 | template <class Ex> |
198 | inline void exception_wrapper::InPlace<Ex>::delete_(exception_wrapper* that) { |
199 | that->buff_.as<Ex>().~Ex(); |
200 | that->vptr_ = &uninit_; |
201 | } |
202 | template <class Ex> |
203 | [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_( |
204 | exception_wrapper const* that) { |
205 | throw that->buff_.as<Ex>(); |
206 | } |
207 | template <class Ex> |
208 | inline std::type_info const* exception_wrapper::InPlace<Ex>::type_( |
209 | exception_wrapper const*) { |
210 | return &typeid(Ex); |
211 | } |
212 | template <class Ex> |
213 | inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_( |
214 | exception_wrapper const* that) { |
215 | return as_exception_or_null_(that->buff_.as<Ex>()); |
216 | } |
217 | template <class Ex> |
218 | inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_( |
219 | exception_wrapper const* that) { |
220 | try { |
221 | throw_(that); |
222 | } catch (Ex const& ex) { |
223 | return exception_wrapper{std::current_exception(), ex}; |
224 | } |
225 | } |
226 | |
227 | template <class Ex> |
228 | [[noreturn]] inline void exception_wrapper::SharedPtr::Impl<Ex>::throw_() |
229 | const { |
230 | throw ex_; |
231 | } |
232 | template <class Ex> |
233 | inline std::exception const* |
234 | exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept { |
235 | return as_exception_or_null_(ex_); |
236 | } |
237 | template <class Ex> |
238 | inline exception_wrapper |
239 | exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept { |
240 | try { |
241 | throw_(); |
242 | } catch (Ex& ex) { |
243 | return exception_wrapper{std::current_exception(), ex}; |
244 | } |
245 | } |
246 | inline void exception_wrapper::SharedPtr::copy_( |
247 | exception_wrapper const* from, |
248 | exception_wrapper* to) { |
249 | ::new (static_cast<void*>(std::addressof(to->sptr_))) SharedPtr(from->sptr_); |
250 | } |
251 | inline void exception_wrapper::SharedPtr::move_( |
252 | exception_wrapper* from, |
253 | exception_wrapper* to) { |
254 | ::new (static_cast<void*>(std::addressof(to->sptr_))) |
255 | SharedPtr(std::move(from->sptr_)); |
256 | delete_(from); |
257 | } |
258 | inline void exception_wrapper::SharedPtr::delete_(exception_wrapper* that) { |
259 | that->sptr_.~SharedPtr(); |
260 | that->vptr_ = &uninit_; |
261 | } |
262 | [[noreturn]] inline void exception_wrapper::SharedPtr::throw_( |
263 | exception_wrapper const* that) { |
264 | that->sptr_.ptr_->throw_(); |
265 | folly::assume_unreachable(); |
266 | } |
267 | inline std::type_info const* exception_wrapper::SharedPtr::type_( |
268 | exception_wrapper const* that) { |
269 | return that->sptr_.ptr_->info_; |
270 | } |
271 | inline std::exception const* exception_wrapper::SharedPtr::get_exception_( |
272 | exception_wrapper const* that) { |
273 | return that->sptr_.ptr_->get_exception_(); |
274 | } |
275 | inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_( |
276 | exception_wrapper const* that) { |
277 | return that->sptr_.ptr_->get_exception_ptr_(); |
278 | } |
279 | |
280 | template <class Ex, typename... As> |
281 | inline exception_wrapper::exception_wrapper( |
282 | ThrownTag, |
283 | in_place_type_t<Ex>, |
284 | As&&... as) |
285 | : eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...)), |
286 | reinterpret_cast<std::uintptr_t>(std::addressof(typeid(Ex))) + 1u}, |
287 | vptr_(&ExceptionPtr::ops_) {} |
288 | |
289 | template <class Ex, typename... As> |
290 | inline exception_wrapper::exception_wrapper( |
291 | OnHeapTag, |
292 | in_place_type_t<Ex>, |
293 | As&&... as) |
294 | : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)}, |
295 | vptr_(&SharedPtr::ops_) {} |
296 | |
297 | template <class Ex, typename... As> |
298 | inline exception_wrapper::exception_wrapper( |
299 | InSituTag, |
300 | in_place_type_t<Ex>, |
301 | As&&... as) |
302 | : buff_{in_place_type<Ex>, std::forward<As>(as)...}, |
303 | vptr_(&InPlace<Ex>::ops_) {} |
304 | |
305 | inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept |
306 | : exception_wrapper{} { |
307 | (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw |
308 | } |
309 | |
310 | inline exception_wrapper::exception_wrapper( |
311 | exception_wrapper const& that) noexcept |
312 | : exception_wrapper{} { |
313 | that.vptr_->copy_(&that, this); // Copy into *this, won't throw |
314 | vptr_ = that.vptr_; |
315 | } |
316 | |
317 | // If `this == &that`, this move assignment operator leaves the object in a |
318 | // valid but unspecified state. |
319 | inline exception_wrapper& exception_wrapper::operator=( |
320 | exception_wrapper&& that) noexcept { |
321 | vptr_->delete_(this); // Free the current exception |
322 | (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw |
323 | return *this; |
324 | } |
325 | |
326 | inline exception_wrapper& exception_wrapper::operator=( |
327 | exception_wrapper const& that) noexcept { |
328 | exception_wrapper(that).swap(*this); |
329 | return *this; |
330 | } |
331 | |
332 | inline exception_wrapper::~exception_wrapper() { |
333 | reset(); |
334 | } |
335 | |
336 | template <class Ex> |
337 | inline exception_wrapper::exception_wrapper( |
338 | std::exception_ptr ptr, |
339 | Ex& ex) noexcept |
340 | : eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)}, vptr_(&ExceptionPtr::ops_) { |
341 | assert(eptr_.ptr_); |
342 | } |
343 | |
344 | namespace exception_wrapper_detail { |
345 | template <class Ex> |
346 | Ex&& dont_slice(Ex&& ex) { |
347 | assert(typeid(ex) == typeid(std::decay_t<Ex>) || |
348 | !"Dynamic and static exception types don't match. Exception would " |
349 | "be sliced when storing in exception_wrapper." ); |
350 | return std::forward<Ex>(ex); |
351 | } |
352 | } // namespace exception_wrapper_detail |
353 | |
354 | template < |
355 | class Ex, |
356 | class Ex_, |
357 | FOLLY_REQUIRES_DEF(Conjunction< |
358 | exception_wrapper::IsStdException<Ex_>, |
359 | exception_wrapper::IsRegularExceptionType<Ex_>>::value)> |
360 | inline exception_wrapper::exception_wrapper(Ex&& ex) |
361 | : exception_wrapper{ |
362 | PlacementOf<Ex_>{}, |
363 | in_place_type<Ex_>, |
364 | exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} |
365 | |
366 | template < |
367 | class Ex, |
368 | class Ex_, |
369 | FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex_>::value)> |
370 | inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex) |
371 | : exception_wrapper{ |
372 | PlacementOf<Ex_>{}, |
373 | in_place_type<Ex_>, |
374 | exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} |
375 | |
376 | template < |
377 | class Ex, |
378 | typename... As, |
379 | FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex>::value)> |
380 | inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as) |
381 | : exception_wrapper{PlacementOf<Ex>{}, |
382 | in_place_type<Ex>, |
383 | std::forward<As>(as)...} {} |
384 | |
385 | inline void exception_wrapper::swap(exception_wrapper& that) noexcept { |
386 | exception_wrapper tmp(std::move(that)); |
387 | that = std::move(*this); |
388 | *this = std::move(tmp); |
389 | } |
390 | |
391 | inline exception_wrapper::operator bool() const noexcept { |
392 | return vptr_ != &uninit_; |
393 | } |
394 | |
395 | inline bool exception_wrapper::operator!() const noexcept { |
396 | return !static_cast<bool>(*this); |
397 | } |
398 | |
399 | inline void exception_wrapper::reset() { |
400 | vptr_->delete_(this); |
401 | } |
402 | |
403 | inline bool exception_wrapper::has_exception_ptr() const noexcept { |
404 | return vptr_ == &ExceptionPtr::ops_; |
405 | } |
406 | |
407 | inline std::exception* exception_wrapper::get_exception() noexcept { |
408 | return const_cast<std::exception*>(vptr_->get_exception_(this)); |
409 | } |
410 | inline std::exception const* exception_wrapper::get_exception() const noexcept { |
411 | return vptr_->get_exception_(this); |
412 | } |
413 | |
414 | template <typename Ex> |
415 | inline Ex* exception_wrapper::get_exception() noexcept { |
416 | Ex* object{nullptr}; |
417 | with_exception([&](Ex& ex) { object = &ex; }); |
418 | return object; |
419 | } |
420 | |
421 | template <typename Ex> |
422 | inline Ex const* exception_wrapper::get_exception() const noexcept { |
423 | Ex const* object{nullptr}; |
424 | with_exception([&](Ex const& ex) { object = &ex; }); |
425 | return object; |
426 | } |
427 | |
428 | inline std::exception_ptr exception_wrapper::to_exception_ptr() noexcept { |
429 | if (*this) { |
430 | // Computing an exception_ptr is expensive so cache the result. |
431 | return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_; |
432 | } |
433 | return {}; |
434 | } |
435 | inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept { |
436 | return vptr_->get_exception_ptr_(this).eptr_.ptr_; |
437 | } |
438 | |
439 | inline std::type_info const& exception_wrapper::none() noexcept { |
440 | return typeid(void); |
441 | } |
442 | inline std::type_info const& exception_wrapper::unknown() noexcept { |
443 | return typeid(Unknown); |
444 | } |
445 | |
446 | inline std::type_info const& exception_wrapper::type() const noexcept { |
447 | return *vptr_->type_(this); |
448 | } |
449 | |
450 | inline folly::fbstring exception_wrapper::what() const { |
451 | if (auto e = get_exception()) { |
452 | return class_name() + ": " + e->what(); |
453 | } |
454 | return class_name(); |
455 | } |
456 | |
457 | inline folly::fbstring exception_wrapper::class_name() const { |
458 | auto& ti = type(); |
459 | return ti == none() |
460 | ? "" |
461 | : ti == unknown() ? "<unknown exception>" : folly::demangle(ti); |
462 | } |
463 | |
464 | template <class Ex> |
465 | inline bool exception_wrapper::is_compatible_with() const noexcept { |
466 | return with_exception([](Ex const&) {}); |
467 | } |
468 | |
469 | [[noreturn]] inline void exception_wrapper::throw_exception() const { |
470 | vptr_->throw_(this); |
471 | onNoExceptionError(__func__); |
472 | } |
473 | |
474 | template <class Ex> |
475 | [[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { |
476 | try { |
477 | throw_exception(); |
478 | } catch (...) { |
479 | std::throw_with_nested(std::forward<Ex>(ex)); |
480 | } |
481 | } |
482 | |
483 | template <class CatchFn, bool IsConst> |
484 | struct exception_wrapper::ExceptionTypeOf { |
485 | using type = arg_type<std::decay_t<CatchFn>>; |
486 | static_assert( |
487 | std::is_reference<type>::value, |
488 | "Always catch exceptions by reference." ); |
489 | static_assert( |
490 | !IsConst || std::is_const<std::remove_reference_t<type>>::value, |
491 | "handle() or with_exception() called on a const exception_wrapper " |
492 | "and asked to catch a non-const exception. Handler will never fire. " |
493 | "Catch exception by const reference to fix this." ); |
494 | }; |
495 | |
496 | // Nests a throw in the proper try/catch blocks |
497 | template <bool IsConst> |
498 | struct exception_wrapper::HandleReduce { |
499 | bool* handled_; |
500 | |
501 | template < |
502 | class ThrowFn, |
503 | class CatchFn, |
504 | FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)> |
505 | auto operator()(ThrowFn&& th, CatchFn& ca) const { |
506 | using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>; |
507 | return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] { |
508 | try { |
509 | th(); |
510 | } catch (Ex& e) { |
511 | // If we got here because a catch function threw, rethrow. |
512 | if (*handled_) { |
513 | throw; |
514 | } |
515 | *handled_ = true; |
516 | ca(e); |
517 | } |
518 | }; |
519 | } |
520 | |
521 | template < |
522 | class ThrowFn, |
523 | class CatchFn, |
524 | FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)> |
525 | auto operator()(ThrowFn&& th, CatchFn& ca) const { |
526 | return [th = std::forward<ThrowFn>(th), &ca, handled_ = handled_] { |
527 | try { |
528 | th(); |
529 | } catch (...) { |
530 | // If we got here because a catch function threw, rethrow. |
531 | if (*handled_) { |
532 | throw; |
533 | } |
534 | *handled_ = true; |
535 | ca(); |
536 | } |
537 | }; |
538 | } |
539 | }; |
540 | |
541 | // When all the handlers expect types derived from std::exception, we can |
542 | // sometimes invoke the handlers without throwing any exceptions. |
543 | template <bool IsConst> |
544 | struct exception_wrapper::HandleStdExceptReduce { |
545 | using StdEx = AddConstIf<IsConst, std::exception>; |
546 | |
547 | template < |
548 | class ThrowFn, |
549 | class CatchFn, |
550 | FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)> |
551 | auto operator()(ThrowFn&& th, CatchFn& ca) const { |
552 | using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>; |
553 | return |
554 | [th = std::forward<ThrowFn>(th), &ca](auto&& continuation) -> StdEx* { |
555 | if (auto e = const_cast<StdEx*>(th(continuation))) { |
556 | if (auto e2 = dynamic_cast<std::add_pointer_t<Ex>>(e)) { |
557 | ca(*e2); |
558 | } else { |
559 | return e; |
560 | } |
561 | } |
562 | return nullptr; |
563 | }; |
564 | } |
565 | |
566 | template < |
567 | class ThrowFn, |
568 | class CatchFn, |
569 | FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)> |
570 | auto operator()(ThrowFn&& th, CatchFn& ca) const { |
571 | return [th = std::forward<ThrowFn>(th), &ca](auto &&) -> StdEx* { |
572 | // The following continuation causes ca() to execute if *this contains |
573 | // an exception /not/ derived from std::exception. |
574 | auto continuation = [&ca](StdEx* e) { |
575 | return e != nullptr ? e : ((void)ca(), nullptr); |
576 | }; |
577 | if (th(continuation) != nullptr) { |
578 | ca(); |
579 | } |
580 | return nullptr; |
581 | }; |
582 | } |
583 | }; |
584 | |
585 | // Called when some types in the catch clauses are not derived from |
586 | // std::exception. |
587 | template <class This, class... CatchFns> |
588 | inline void |
589 | exception_wrapper::handle_(std::false_type, This& this_, CatchFns&... fns) { |
590 | bool handled = false; |
591 | auto impl = exception_wrapper_detail::fold( |
592 | HandleReduce<std::is_const<This>::value>{&handled}, |
593 | [&] { this_.throw_exception(); }, |
594 | fns...); |
595 | impl(); |
596 | } |
597 | |
598 | // Called when all types in the catch clauses are either derived from |
599 | // std::exception or a catch-all clause. |
600 | template <class This, class... CatchFns> |
601 | inline void |
602 | exception_wrapper::handle_(std::true_type, This& this_, CatchFns&... fns) { |
603 | using StdEx = exception_wrapper_detail:: |
604 | AddConstIf<std::is_const<This>::value, std::exception>; |
605 | auto impl = exception_wrapper_detail::fold( |
606 | HandleStdExceptReduce<std::is_const<This>::value>{}, |
607 | [&](auto&& continuation) { |
608 | return continuation( |
609 | const_cast<StdEx*>(this_.vptr_->get_exception_(&this_))); |
610 | }, |
611 | fns...); |
612 | // This continuation gets evaluated if CatchFns... does not include a |
613 | // catch-all handler. It is a no-op. |
614 | auto continuation = [](StdEx* ex) { return ex; }; |
615 | if (nullptr != impl(continuation)) { |
616 | this_.throw_exception(); |
617 | } |
618 | } |
619 | |
620 | namespace exception_wrapper_detail { |
621 | template <class Ex, class Fn> |
622 | struct catch_fn { |
623 | Fn fn_; |
624 | auto operator()(Ex& ex) { |
625 | return fn_(ex); |
626 | } |
627 | }; |
628 | |
629 | template <class Ex, class Fn> |
630 | inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) { |
631 | return {std::move(fn)}; |
632 | } |
633 | template <class Fn> |
634 | inline Fn catch_(void const*, Fn fn) { |
635 | return fn; |
636 | } |
637 | } // namespace exception_wrapper_detail |
638 | |
639 | template <class Ex, class This, class Fn> |
640 | inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { |
641 | if (!this_) { |
642 | return false; |
643 | } |
644 | bool handled = true; |
645 | auto fn = exception_wrapper_detail::catch_( |
646 | static_cast<Ex*>(nullptr), std::move(fn_)); |
647 | auto&& all = [&](...) { handled = false; }; |
648 | handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all); |
649 | return handled; |
650 | } |
651 | |
652 | template <class Ex, class Fn> |
653 | inline bool exception_wrapper::with_exception(Fn fn) { |
654 | return with_exception_<Ex>(*this, std::move(fn)); |
655 | } |
656 | template <class Ex, class Fn> |
657 | inline bool exception_wrapper::with_exception(Fn fn) const { |
658 | return with_exception_<Ex const>(*this, std::move(fn)); |
659 | } |
660 | |
661 | template <class... CatchFns> |
662 | inline void exception_wrapper::handle(CatchFns... fns) { |
663 | using AllStdEx = |
664 | exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>; |
665 | if (!*this) { |
666 | onNoExceptionError(__func__); |
667 | } |
668 | this->handle_(AllStdEx{}, *this, fns...); |
669 | } |
670 | template <class... CatchFns> |
671 | inline void exception_wrapper::handle(CatchFns... fns) const { |
672 | using AllStdEx = |
673 | exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>; |
674 | if (!*this) { |
675 | onNoExceptionError(__func__); |
676 | } |
677 | this->handle_(AllStdEx{}, *this, fns...); |
678 | } |
679 | |
680 | } // namespace folly |
681 | |