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