cmake: build as many source files as possible
[folly.git] / folly / ExceptionWrapper-inl.h
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 };
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     !kIsWindows || sizeof(void*) == 8,
97     "exception_wrapper is untested on 32 bit Windows.");
98 static_assert(
99     !kIsWindows || (kMscVer >= 1900 && kMscVer <= 2000),
100     "exception_wrapper is untested and possibly broken on your version of "
101     "MSVC");
102
103 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
104     std::exception_ptr const& ptr,
105     std::exception const& e) {
106   if (!kIsWindows) {
107     return reinterpret_cast<std::uintptr_t>(&e);
108   } else {
109     // On Windows, as of MSVC2017, all thrown exceptions are copied to the stack
110     // first. Thus, we cannot depend on exception references associated with an
111     // exception_ptr to be live for the duration of the exception_ptr. We need
112     // to directly access the heap allocated memory inside the exception_ptr.
113     //
114     // std::exception_ptr is an opaque reinterpret_cast of
115     // std::shared_ptr<__ExceptionPtr>
116     // __ExceptionPtr is a non-virtual class with two members, a union and a
117     // bool. The union contains the now-undocumented EHExceptionRecord, which
118     // contains a struct which contains a void* which points to the heap
119     // allocated exception.
120     // We derive the offset to pExceptionObject via manual means.
121     FOLLY_PACK_PUSH
122     struct Win32ExceptionPtr {
123       char offset[40];
124       void* exceptionObject;
125     } FOLLY_PACK_ATTR;
126     FOLLY_PACK_POP
127
128     auto* win32ExceptionPtr =
129         reinterpret_cast<std::shared_ptr<Win32ExceptionPtr> const*>(
130             &ptr)->get();
131     return reinterpret_cast<std::uintptr_t>(win32ExceptionPtr->exceptionObject);
132   }
133 }
134 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
135     std::exception_ptr const&,
136     AnyException e) {
137   return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
138 }
139 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
140   return 0 == exception_or_type_ % 2;
141 }
142 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
143     const {
144   return reinterpret_cast<std::exception const*>(exception_or_type_);
145 }
146 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
147   return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
148 }
149
150 inline void exception_wrapper::ExceptionPtr::copy_(
151     exception_wrapper const* from, exception_wrapper* to) {
152   ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
153 }
154 inline void exception_wrapper::ExceptionPtr::move_(
155     exception_wrapper* from, exception_wrapper* to) {
156   ::new (static_cast<void*>(&to->eptr_))
157       ExceptionPtr(std::move(from->eptr_));
158   delete_(from);
159 }
160 inline void exception_wrapper::ExceptionPtr::delete_(
161     exception_wrapper* that) {
162   that->eptr_.~ExceptionPtr();
163   that->vptr_ = &uninit_;
164 }
165 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
166     exception_wrapper const* that) {
167   std::rethrow_exception(that->eptr_.ptr_);
168 }
169 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
170     exception_wrapper const* that) {
171   if (auto e = get_exception_(that)) {
172     return &typeid(*e);
173   }
174   return that->eptr_.as_type_();
175 }
176 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
177     exception_wrapper const* that) {
178   return that->eptr_.has_exception_() ? that->eptr_.as_exception_()
179                                       : nullptr;
180 }
181 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
182     exception_wrapper const* that) {
183   return *that;
184 }
185
186 template <class Ex>
187 inline void exception_wrapper::InPlace<Ex>::copy_(
188     exception_wrapper const* from, exception_wrapper* to) {
189   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
190       Ex(from->buff_.as<Ex>());
191 }
192 template <class Ex>
193 inline void exception_wrapper::InPlace<Ex>::move_(
194     exception_wrapper* from, exception_wrapper* to) {
195   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
196       Ex(std::move(from->buff_.as<Ex>()));
197   delete_(from);
198 }
199 template <class Ex>
200 inline void exception_wrapper::InPlace<Ex>::delete_(
201     exception_wrapper* that) {
202   that->buff_.as<Ex>().~Ex();
203   that->vptr_ = &uninit_;
204 }
205 template <class Ex>
206 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
207     exception_wrapper const* that) {
208   throw that->buff_.as<Ex>(); // @nolint
209 }
210 template <class Ex>
211 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
212     exception_wrapper const*) {
213   return &typeid(Ex);
214 }
215 template <class Ex>
216 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
217     exception_wrapper const* that) {
218   return as_exception_or_null_(that->buff_.as<Ex>());
219 }
220 template <class Ex>
221 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
222     exception_wrapper const* that) {
223   try {
224     throw_(that);
225   } catch (Ex const& ex) {
226     return exception_wrapper{std::current_exception(), ex};
227   }
228 }
229
230 template <class Ex>
231 [[noreturn]] inline void
232 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
233   throw ex_; // @nolint
234 }
235 template <class Ex>
236 inline std::exception const*
237 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
238   return as_exception_or_null_(ex_);
239 }
240 template <class Ex>
241 inline exception_wrapper
242 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
243   try {
244     throw_();
245   } catch (Ex& ex) {
246     return exception_wrapper{std::current_exception(), ex};
247   }
248 }
249 inline void exception_wrapper::SharedPtr::copy_(
250     exception_wrapper const* from, exception_wrapper* to) {
251   ::new (static_cast<void*>(std::addressof(to->sptr_)))
252       SharedPtr(from->sptr_);
253 }
254 inline void exception_wrapper::SharedPtr::move_(
255     exception_wrapper* from, exception_wrapper* to) {
256   ::new (static_cast<void*>(std::addressof(to->sptr_)))
257       SharedPtr(std::move(from->sptr_));
258   delete_(from);
259 }
260 inline void exception_wrapper::SharedPtr::delete_(
261     exception_wrapper* that) {
262   that->sptr_.~SharedPtr();
263   that->vptr_ = &uninit_;
264 }
265 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
266     exception_wrapper const* that) {
267   that->sptr_.ptr_->throw_();
268   folly::assume_unreachable();
269 }
270 inline std::type_info const* exception_wrapper::SharedPtr::type_(
271     exception_wrapper const* that) {
272   return that->sptr_.ptr_->info_;
273 }
274 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
275     exception_wrapper const* that) {
276   return that->sptr_.ptr_->get_exception_();
277 }
278 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
279     exception_wrapper const* that) {
280   return that->sptr_.ptr_->get_exception_ptr_();
281 }
282
283 template <class Ex, typename... As>
284 inline exception_wrapper::exception_wrapper(
285     ThrownTag,
286     in_place_type_t<Ex>,
287     As&&... as)
288     : eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...)),
289             reinterpret_cast<std::uintptr_t>(std::addressof(typeid(Ex))) + 1u},
290       vptr_(&ExceptionPtr::ops_) {}
291
292 template <class Ex, typename... As>
293 inline exception_wrapper::exception_wrapper(
294     OnHeapTag,
295     in_place_type_t<Ex>,
296     As&&... as)
297     : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)},
298       vptr_(&SharedPtr::ops_) {}
299
300 template <class Ex, typename... As>
301 inline exception_wrapper::exception_wrapper(
302     InSituTag,
303     in_place_type_t<Ex>,
304     As&&... as)
305     : buff_{in_place_type<Ex>, std::forward<As>(as)...},
306       vptr_(&InPlace<Ex>::ops_) {}
307
308 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
309     : exception_wrapper{} {
310   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
311 }
312
313 inline exception_wrapper::exception_wrapper(
314     exception_wrapper const& that) : exception_wrapper{} {
315   that.vptr_->copy_(&that, this); // could throw
316   vptr_ = that.vptr_;
317 }
318
319 // If `this == &that`, this move assignment operator leaves the object in a
320 // valid but unspecified state.
321 inline exception_wrapper& exception_wrapper::operator=(
322     exception_wrapper&& that) noexcept {
323   vptr_->delete_(this); // Free the current exception
324   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
325   return *this;
326 }
327
328 inline exception_wrapper& exception_wrapper::operator=(
329     exception_wrapper const& that) {
330   exception_wrapper(that).swap(*this);
331   return *this;
332 }
333
334 inline exception_wrapper::~exception_wrapper() {
335   reset();
336 }
337
338 template <class Ex>
339 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
340     : eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)},
341       vptr_(&ExceptionPtr::ops_) {
342   assert(eptr_.ptr_);
343 }
344
345 namespace exception_wrapper_detail {
346 template <class Ex>
347 Ex&& dont_slice(Ex&& ex) {
348   assert(typeid(ex) == typeid(_t<std::decay<Ex>>) ||
349        !"Dynamic and static exception types don't match. Exception would "
350         "be sliced when storing in exception_wrapper.");
351   return std::forward<Ex>(ex);
352 }
353 } // namespace exception_wrapper_detail
354
355 template <
356     class Ex,
357     class Ex_,
358     FOLLY_REQUIRES_DEF(
359         Conjunction<
360             exception_wrapper::IsStdException<Ex_>,
361             exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
362 inline exception_wrapper::exception_wrapper(Ex&& ex)
363     : exception_wrapper{
364         PlacementOf<Ex_>{},
365         in_place_type<Ex_>,
366         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {
367 }
368
369 template <
370     class Ex,
371     class Ex_,
372     FOLLY_REQUIRES_DEF(
373         exception_wrapper::IsRegularExceptionType<Ex_>::value)>
374 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
375     : exception_wrapper{
376         PlacementOf<Ex_>{},
377         in_place_type<Ex_>,
378         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {
379 }
380
381 template <
382     class Ex,
383     typename... As,
384     FOLLY_REQUIRES_DEF(
385         exception_wrapper::IsRegularExceptionType<Ex>::value)>
386 inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as)
387     : exception_wrapper{
388         PlacementOf<Ex>{},
389         in_place_type<Ex>,
390         std::forward<As>(as)...} {
391 }
392
393 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
394   exception_wrapper tmp(std::move(that));
395   that = std::move(*this);
396   *this = std::move(tmp);
397 }
398
399 inline exception_wrapper::operator bool() const noexcept {
400   return vptr_ != &uninit_;
401 }
402
403 inline bool exception_wrapper::operator!() const noexcept {
404   return !static_cast<bool>(*this);
405 }
406
407 inline void exception_wrapper::reset() {
408   vptr_->delete_(this);
409 }
410
411 inline bool exception_wrapper::has_exception_ptr() const noexcept {
412   return vptr_ == &ExceptionPtr::ops_;
413 }
414
415 inline std::exception* exception_wrapper::get_exception() noexcept {
416   return const_cast<std::exception*>(vptr_->get_exception_(this));
417 }
418 inline std::exception const* exception_wrapper::get_exception() const noexcept {
419   return vptr_->get_exception_(this);
420 }
421
422 template <typename Ex>
423 inline Ex* exception_wrapper::get_exception() noexcept {
424   Ex* object{nullptr};
425   with_exception([&](Ex& ex) { object = &ex; });
426   return object;
427 }
428
429 template <typename Ex>
430 inline Ex const* exception_wrapper::get_exception() const noexcept {
431   Ex const* object{nullptr};
432   with_exception([&](Ex const& ex) { object = &ex; });
433   return object;
434 }
435
436 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
437     noexcept {
438   // Computing an exception_ptr is expensive so cache the result.
439   return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
440 }
441 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
442   return vptr_->get_exception_ptr_(this).eptr_.ptr_;
443 }
444
445 inline std::type_info const& exception_wrapper::none() noexcept {
446   return typeid(void);
447 }
448 inline std::type_info const& exception_wrapper::unknown() noexcept {
449   return typeid(Unknown);
450 }
451
452 inline std::type_info const& exception_wrapper::type() const noexcept {
453   return *vptr_->type_(this);
454 }
455
456 inline folly::fbstring exception_wrapper::what() const {
457   if (auto e = get_exception()) {
458     return class_name() + ": " + e->what();
459   }
460   return class_name();
461 }
462
463 inline folly::fbstring exception_wrapper::class_name() const {
464   auto& ti = type();
465   return ti == none()
466       ? ""
467       : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
468 }
469
470 template <class Ex>
471 inline bool exception_wrapper::is_compatible_with() const noexcept {
472   return with_exception([](Ex const&) {});
473 }
474
475 [[noreturn]] inline void exception_wrapper::throw_exception() const {
476   vptr_->throw_(this);
477   onNoExceptionError(__func__);
478 }
479
480 template <class CatchFn, bool IsConst>
481 struct exception_wrapper::ExceptionTypeOf {
482   using type = arg_type<_t<std::decay<CatchFn>>>;
483   static_assert(
484       std::is_reference<type>::value,
485       "Always catch exceptions by reference.");
486   static_assert(
487       !IsConst || std::is_const<_t<std::remove_reference<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 [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
551         -> StdEx* {
552       if (auto e = const_cast<StdEx*>(th(continuation))) {
553         if (auto e2 = dynamic_cast<_t<std::add_pointer<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 exception_wrapper::handle_(
586     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 exception_wrapper::handle_(
599     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 (StdEx* e = impl(continuation)) {
613     throw *e; // Not handled. Throw.
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