2 * Copyright 2017-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Author: Eric Niebler <eniebler@fb.com>
21 #include <folly/Portability.h>
26 struct exception_wrapper::arg_type_
27 : public arg_type_<decltype(&Fn::operator())> {
29 template <class Ret, class Class, class Arg>
30 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> {
33 template <class Ret, class Class, class Arg>
34 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> {
37 template <class Ret, class Arg>
38 struct exception_wrapper::arg_type_<Ret (Arg)> {
41 template <class Ret, class Arg>
42 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
45 template <class Ret, class Class>
46 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> {
47 using type = AnyException;
49 template <class Ret, class Class>
50 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> {
51 using type = AnyException;
54 struct exception_wrapper::arg_type_<Ret (...)> {
55 using type = AnyException;
58 struct exception_wrapper::arg_type_<Ret (*)(...)> {
59 using type = AnyException;
62 template <class Ret, class... Args>
63 inline Ret exception_wrapper::noop_(Args...) {
67 inline std::type_info const* exception_wrapper::uninit_type_(
68 exception_wrapper const*) {
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_)...);
78 inline Ex& exception_wrapper::Buffer::as() noexcept {
79 return *static_cast<Ex*>(static_cast<void*>(&buff_));
82 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
83 return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
86 inline std::exception const* exception_wrapper::as_exception_or_null_(
87 std::exception const& ex) {
90 inline std::exception const* exception_wrapper::as_exception_or_null_(
96 !kIsWindows || sizeof(void*) == 8,
97 "exception_wrapper is untested on 32 bit Windows.");
99 !kIsWindows || (kMscVer >= 1900 && kMscVer <= 2000),
100 "exception_wrapper is untested and possibly broken on your version of "
103 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
104 std::exception_ptr const& ptr,
105 std::exception const& e) {
107 return reinterpret_cast<std::uintptr_t>(&e);
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.
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.
122 struct Win32ExceptionPtr {
124 void* exceptionObject;
128 auto* win32ExceptionPtr =
129 reinterpret_cast<std::shared_ptr<Win32ExceptionPtr> const*>(
131 return reinterpret_cast<std::uintptr_t>(win32ExceptionPtr->exceptionObject);
134 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
135 std::exception_ptr const&,
137 return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
139 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
140 return 0 == exception_or_type_ % 2;
142 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
144 return reinterpret_cast<std::exception const*>(exception_or_type_);
146 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
147 return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
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_);
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_));
160 inline void exception_wrapper::ExceptionPtr::delete_(
161 exception_wrapper* that) {
162 that->eptr_.~ExceptionPtr();
163 that->vptr_ = &uninit_;
165 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
166 exception_wrapper const* that) {
167 std::rethrow_exception(that->eptr_.ptr_);
169 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
170 exception_wrapper const* that) {
171 if (auto e = get_exception_(that)) {
174 return that->eptr_.as_type_();
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_()
181 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
182 exception_wrapper const* that) {
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>());
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>()));
200 inline void exception_wrapper::InPlace<Ex>::delete_(
201 exception_wrapper* that) {
202 that->buff_.as<Ex>().~Ex();
203 that->vptr_ = &uninit_;
206 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
207 exception_wrapper const* that) {
208 throw that->buff_.as<Ex>(); // @nolint
211 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
212 exception_wrapper const*) {
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>());
221 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
222 exception_wrapper const* that) {
225 } catch (Ex const& ex) {
226 return exception_wrapper{std::current_exception(), ex};
231 [[noreturn]] inline void
232 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
233 throw ex_; // @nolint
236 inline std::exception const*
237 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
238 return as_exception_or_null_(ex_);
241 inline exception_wrapper
242 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
246 return exception_wrapper{std::current_exception(), ex};
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_);
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_));
260 inline void exception_wrapper::SharedPtr::delete_(
261 exception_wrapper* that) {
262 that->sptr_.~SharedPtr();
263 that->vptr_ = &uninit_;
265 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
266 exception_wrapper const* that) {
267 that->sptr_.ptr_->throw_();
268 folly::assume_unreachable();
270 inline std::type_info const* exception_wrapper::SharedPtr::type_(
271 exception_wrapper const* that) {
272 return that->sptr_.ptr_->info_;
274 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
275 exception_wrapper const* that) {
276 return that->sptr_.ptr_->get_exception_();
278 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
279 exception_wrapper const* that) {
280 return that->sptr_.ptr_->get_exception_ptr_();
283 template <class Ex, typename... As>
284 inline exception_wrapper::exception_wrapper(OnHeapTag, in_place_type_t<Ex>, As&&... as)
285 : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)},
286 vptr_(&SharedPtr::ops_) {}
288 template <class Ex, typename... As>
289 inline exception_wrapper::exception_wrapper(InSituTag, in_place_type_t<Ex>, As&&... as)
290 : buff_{in_place_type<Ex>, std::forward<As>(as)...},
291 vptr_(&InPlace<Ex>::ops_) {}
293 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
294 : exception_wrapper{} {
295 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
298 inline exception_wrapper::exception_wrapper(
299 exception_wrapper const& that) : exception_wrapper{} {
300 that.vptr_->copy_(&that, this); // could throw
304 // If `this == &that`, this move assignment operator leaves the object in a
305 // valid but unspecified state.
306 inline exception_wrapper& exception_wrapper::operator=(
307 exception_wrapper&& that) noexcept {
308 vptr_->delete_(this); // Free the current exception
309 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
313 inline exception_wrapper& exception_wrapper::operator=(
314 exception_wrapper const& that) {
315 exception_wrapper(that).swap(*this);
319 inline exception_wrapper::~exception_wrapper() {
324 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
325 : eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)},
326 vptr_(&ExceptionPtr::ops_) {
330 namespace exception_wrapper_detail {
332 Ex&& dont_slice(Ex&& ex) {
333 assert(typeid(ex) == typeid(_t<std::decay<Ex>>) ||
334 !"Dynamic and static exception types don't match. Exception would "
335 "be sliced when storing in exception_wrapper.");
336 return std::forward<Ex>(ex);
345 exception_wrapper::IsStdException<Ex_>,
346 exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
347 inline exception_wrapper::exception_wrapper(Ex&& ex)
351 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {
358 exception_wrapper::IsRegularExceptionType<Ex_>::value)>
359 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
363 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {
370 exception_wrapper::IsRegularExceptionType<Ex>::value)>
371 inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as)
375 std::forward<As>(as)...} {
378 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
379 exception_wrapper tmp(std::move(that));
380 that = std::move(*this);
381 *this = std::move(tmp);
384 inline exception_wrapper::operator bool() const noexcept {
385 return vptr_ != &uninit_;
388 inline bool exception_wrapper::operator!() const noexcept {
389 return !static_cast<bool>(*this);
392 inline void exception_wrapper::reset() {
393 vptr_->delete_(this);
396 inline bool exception_wrapper::has_exception_ptr() const noexcept {
397 return vptr_ == &ExceptionPtr::ops_;
400 inline std::exception* exception_wrapper::get_exception() noexcept {
401 return const_cast<std::exception*>(vptr_->get_exception_(this));
403 inline std::exception const* exception_wrapper::get_exception() const noexcept {
404 return vptr_->get_exception_(this);
407 template <typename Ex>
408 inline Ex* exception_wrapper::get_exception() noexcept {
410 with_exception([&](Ex& ex) { object = &ex; });
414 template <typename Ex>
415 inline Ex const* exception_wrapper::get_exception() const noexcept {
416 Ex const* object{nullptr};
417 with_exception([&](Ex const& ex) { object = &ex; });
421 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
423 // Computing an exception_ptr is expensive so cache the result.
424 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
426 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
427 return vptr_->get_exception_ptr_(this).eptr_.ptr_;
430 inline std::type_info const& exception_wrapper::none() noexcept {
433 inline std::type_info const& exception_wrapper::unknown() noexcept {
434 return typeid(Unknown);
437 inline std::type_info const& exception_wrapper::type() const noexcept {
438 return *vptr_->type_(this);
441 inline folly::fbstring exception_wrapper::what() const {
442 if (auto e = get_exception()) {
443 return class_name() + ": " + e->what();
448 inline folly::fbstring exception_wrapper::class_name() const {
452 : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
456 inline bool exception_wrapper::is_compatible_with() const noexcept {
457 return with_exception([](Ex const&) {});
460 [[noreturn]] inline void exception_wrapper::throw_exception() const {
462 onNoExceptionError(__func__);
465 template <class CatchFn, bool IsConst>
466 struct exception_wrapper::ExceptionTypeOf {
467 using type = arg_type<_t<std::decay<CatchFn>>>;
469 std::is_reference<type>::value,
470 "Always catch exceptions by reference.");
472 !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
473 "handle() or with_exception() called on a const exception_wrapper "
474 "and asked to catch a non-const exception. Handler will never fire. "
475 "Catch exception by const reference to fix this.");
478 // Nests a throw in the proper try/catch blocks
479 template <bool IsConst>
480 struct exception_wrapper::HandleReduce {
486 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
487 auto operator()(ThrowFn&& th, CatchFn& ca) const {
488 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
489 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
493 // If we got here because a catch function threw, rethrow.
506 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
507 auto operator()(ThrowFn&& th, CatchFn& ca) const {
508 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
512 // If we got here because a catch function threw, rethrow.
523 // When all the handlers expect types derived from std::exception, we can
524 // sometimes invoke the handlers without throwing any exceptions.
525 template <bool IsConst>
526 struct exception_wrapper::HandleStdExceptReduce {
527 using StdEx = AddConstIf<IsConst, std::exception>;
532 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
533 auto operator()(ThrowFn&& th, CatchFn& ca) const {
534 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
535 return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
537 if (auto e = const_cast<StdEx*>(th(continuation))) {
538 if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
551 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
552 auto operator()(ThrowFn&& th, CatchFn& ca) const {
553 return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
554 // The following continuation causes ca() to execute if *this contains
555 // an exception /not/ derived from std::exception.
556 auto continuation = [&ca](StdEx* e) {
557 return e != nullptr ? e : ((void)ca(), nullptr);
559 if (th(continuation) != nullptr) {
567 // Called when some types in the catch clauses are not derived from
569 template <class This, class... CatchFns>
570 inline void exception_wrapper::handle_(
571 std::false_type, This& this_, CatchFns&... fns) {
572 bool handled = false;
573 auto impl = exception_wrapper_detail::fold(
574 HandleReduce<std::is_const<This>::value>{&handled},
575 [&] { this_.throw_exception(); },
580 // Called when all types in the catch clauses are either derived from
581 // std::exception or a catch-all clause.
582 template <class This, class... CatchFns>
583 inline void exception_wrapper::handle_(
584 std::true_type, This& this_, CatchFns&... fns) {
585 using StdEx = exception_wrapper_detail::
586 AddConstIf<std::is_const<This>::value, std::exception>;
587 auto impl = exception_wrapper_detail::fold(
588 HandleStdExceptReduce<std::is_const<This>::value>{},
589 [&](auto&& continuation) {
591 const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
594 // This continuation gets evaluated if CatchFns... does not include a
595 // catch-all handler. It is a no-op.
596 auto continuation = [](StdEx* ex) { return ex; };
597 if (StdEx* e = impl(continuation)) {
598 throw *e; // Not handled. Throw.
602 namespace exception_wrapper_detail {
603 template <class Ex, class Fn>
606 auto operator()(Ex& ex) {
611 template <class Ex, class Fn>
612 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
613 return {std::move(fn)};
616 inline Fn catch_(void const*, Fn fn) {
619 } // namespace exception_wrapper_detail
621 template <class Ex, class This, class Fn>
622 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
627 auto fn = exception_wrapper_detail::catch_(
628 static_cast<Ex*>(nullptr), std::move(fn_));
629 auto&& all = [&](...) { handled = false; };
630 handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
634 template <class Ex, class Fn>
635 inline bool exception_wrapper::with_exception(Fn fn) {
636 return with_exception_<Ex>(*this, std::move(fn));
638 template <class Ex, class Fn>
639 inline bool exception_wrapper::with_exception(Fn fn) const {
640 return with_exception_<Ex const>(*this, std::move(fn));
643 template <class... CatchFns>
644 inline void exception_wrapper::handle(CatchFns... fns) {
646 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
648 onNoExceptionError(__func__);
650 this->handle_(AllStdEx{}, *this, fns...);
652 template <class... CatchFns>
653 inline void exception_wrapper::handle(CatchFns... fns) const {
655 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
657 onNoExceptionError(__func__);
659 this->handle_(AllStdEx{}, *this, fns...);