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>
24 struct exception_wrapper::arg_type_
25 : public arg_type_<decltype(&Fn::operator())> {
27 template <class Ret, class Class, class Arg>
28 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> {
31 template <class Ret, class Class, class Arg>
32 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> {
35 template <class Ret, class Arg>
36 struct exception_wrapper::arg_type_<Ret (Arg)> {
39 template <class Ret, class Arg>
40 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
43 template <class Ret, class Class>
44 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> {
45 using type = AnyException;
47 template <class Ret, class Class>
48 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> {
49 using type = AnyException;
52 struct exception_wrapper::arg_type_<Ret (...)> {
53 using type = AnyException;
56 struct exception_wrapper::arg_type_<Ret (*)(...)> {
57 using type = AnyException;
60 template <class Ret, class... Args>
61 inline Ret exception_wrapper::noop_(Args...) {
65 inline std::type_info const* exception_wrapper::uninit_type_(
66 exception_wrapper const*) {
70 template <class Ex, class DEx>
71 inline exception_wrapper::Buffer::Buffer(in_place_t, Ex&& ex) {
72 ::new (static_cast<void*>(&buff_)) DEx(std::forward<Ex>(ex));
76 inline Ex& exception_wrapper::Buffer::as() noexcept {
77 return *static_cast<Ex*>(static_cast<void*>(&buff_));
80 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
81 return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
84 inline std::exception const* exception_wrapper::as_exception_or_null_(
85 std::exception const& ex) {
88 inline std::exception const* exception_wrapper::as_exception_or_null_(
93 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
94 std::exception const& e) {
95 return reinterpret_cast<std::uintptr_t>(&e);
97 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(AnyException e) {
98 return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
100 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
101 return 0 == exception_or_type_ % 2;
103 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
105 return reinterpret_cast<std::exception const*>(exception_or_type_);
107 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
108 return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
111 inline void exception_wrapper::ExceptionPtr::copy_(
112 exception_wrapper const* from, exception_wrapper* to) {
113 ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
115 inline void exception_wrapper::ExceptionPtr::move_(
116 exception_wrapper* from, exception_wrapper* to) {
117 ::new (static_cast<void*>(&to->eptr_))
118 ExceptionPtr(std::move(from->eptr_));
121 inline void exception_wrapper::ExceptionPtr::delete_(
122 exception_wrapper* that) {
123 that->eptr_.~ExceptionPtr();
124 that->vptr_ = &uninit_;
126 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
127 exception_wrapper const* that) {
128 std::rethrow_exception(that->eptr_.ptr_);
130 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
131 exception_wrapper const* that) {
132 if (auto e = get_exception_(that)) {
135 return that->eptr_.as_type_();
137 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
138 exception_wrapper const* that) {
139 return that->eptr_.has_exception_() ? that->eptr_.as_exception_()
142 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
143 exception_wrapper const* that) {
148 inline void exception_wrapper::InPlace<Ex>::copy_(
149 exception_wrapper const* from, exception_wrapper* to) {
150 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
151 Ex(from->buff_.as<Ex>());
154 inline void exception_wrapper::InPlace<Ex>::move_(
155 exception_wrapper* from, exception_wrapper* to) {
156 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
157 Ex(std::move(from->buff_.as<Ex>()));
161 inline void exception_wrapper::InPlace<Ex>::delete_(
162 exception_wrapper* that) {
163 that->buff_.as<Ex>().~Ex();
164 that->vptr_ = &uninit_;
167 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
168 exception_wrapper const* that) {
169 throw that->buff_.as<Ex>(); // @nolint
172 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
173 exception_wrapper const*) {
177 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
178 exception_wrapper const* that) {
179 return as_exception_or_null_(that->buff_.as<Ex>());
182 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
183 exception_wrapper const* that) {
186 } catch (Ex const& ex) {
187 return exception_wrapper{std::current_exception(), ex};
192 [[noreturn]] inline void
193 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
194 throw ex_; // @nolint
197 inline std::exception const*
198 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
199 return as_exception_or_null_(ex_);
202 inline exception_wrapper
203 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
207 return exception_wrapper{std::current_exception(), ex};
210 inline void exception_wrapper::SharedPtr::copy_(
211 exception_wrapper const* from, exception_wrapper* to) {
212 ::new (static_cast<void*>(std::addressof(to->sptr_)))
213 SharedPtr(from->sptr_);
215 inline void exception_wrapper::SharedPtr::move_(
216 exception_wrapper* from, exception_wrapper* to) {
217 ::new (static_cast<void*>(std::addressof(to->sptr_)))
218 SharedPtr(std::move(from->sptr_));
221 inline void exception_wrapper::SharedPtr::delete_(
222 exception_wrapper* that) {
223 that->sptr_.~SharedPtr();
224 that->vptr_ = &uninit_;
226 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
227 exception_wrapper const* that) {
228 that->sptr_.ptr_->throw_();
229 folly::assume_unreachable();
231 inline std::type_info const* exception_wrapper::SharedPtr::type_(
232 exception_wrapper const* that) {
233 return that->sptr_.ptr_->info_;
235 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
236 exception_wrapper const* that) {
237 return that->sptr_.ptr_->get_exception_();
239 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
240 exception_wrapper const* that) {
241 return that->sptr_.ptr_->get_exception_ptr_();
244 template <class Ex, class DEx>
245 inline exception_wrapper::exception_wrapper(Ex&& ex, OnHeapTag)
246 : sptr_{std::make_shared<SharedPtr::Impl<DEx>>(std::forward<Ex>(ex))},
247 vptr_(&SharedPtr::ops_) {}
249 template <class Ex, class DEx>
250 inline exception_wrapper::exception_wrapper(Ex&& ex, InSituTag)
251 : buff_{in_place, std::forward<Ex>(ex)}, vptr_(&InPlace<DEx>::ops_) {}
253 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
254 : exception_wrapper{} {
255 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
258 inline exception_wrapper::exception_wrapper(
259 exception_wrapper const& that) : exception_wrapper{} {
260 that.vptr_->copy_(&that, this); // could throw
264 // If `this == &that`, this move assignment operator leaves the object in a
265 // valid but unspecified state.
266 inline exception_wrapper& exception_wrapper::operator=(
267 exception_wrapper&& that) noexcept {
268 vptr_->delete_(this); // Free the current exception
269 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
273 inline exception_wrapper& exception_wrapper::operator=(
274 exception_wrapper const& that) {
275 exception_wrapper(that).swap(*this);
279 inline exception_wrapper::~exception_wrapper() {
284 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
285 : eptr_{std::move(ptr), ExceptionPtr::as_int_(ex)},
286 vptr_(&ExceptionPtr::ops_) {
295 exception_wrapper::IsStdException<Ex_>,
296 exception_wrapper::IsRegularExceptionType<Ex_>>())>
297 inline exception_wrapper::exception_wrapper(Ex&& ex)
298 : exception_wrapper{std::forward<Ex>(ex), PlacementOf<Ex_>{}} {
300 assert(typeid(ex) == typeid(Ex_) ||
301 !"Dynamic and static exception types don't match. Exception would "
302 "be sliced when storing in exception_wrapper.");
309 exception_wrapper::IsRegularExceptionType<Ex_>())>
310 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
311 : exception_wrapper{std::forward<Ex>(ex), PlacementOf<Ex_>{}} {
313 assert(typeid(ex) == typeid(Ex_) ||
314 !"Dynamic and static exception types don't match. Exception would "
315 "be sliced when storing in exception_wrapper.");
318 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
319 exception_wrapper tmp(std::move(that));
320 that = std::move(*this);
321 *this = std::move(tmp);
324 inline exception_wrapper::operator bool() const noexcept {
325 return vptr_ != &uninit_;
328 inline bool exception_wrapper::operator!() const noexcept {
329 return !static_cast<bool>(*this);
332 inline void exception_wrapper::reset() {
333 vptr_->delete_(this);
336 inline bool exception_wrapper::has_exception_ptr() const noexcept {
337 return vptr_ == &ExceptionPtr::ops_;
340 inline std::exception* exception_wrapper::get_exception() noexcept {
341 return const_cast<std::exception*>(vptr_->get_exception_(this));
343 inline std::exception const* exception_wrapper::get_exception() const noexcept {
344 return vptr_->get_exception_(this);
347 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
349 // Computing an exception_ptr is expensive so cache the result.
350 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
352 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
353 return vptr_->get_exception_ptr_(this).eptr_.ptr_;
356 inline std::type_info const& exception_wrapper::none() noexcept {
359 inline std::type_info const& exception_wrapper::unknown() noexcept {
360 return typeid(Unknown);
363 inline std::type_info const& exception_wrapper::type() const noexcept {
364 return *vptr_->type_(this);
367 inline folly::fbstring exception_wrapper::what() const {
368 if (auto e = get_exception()) {
369 return class_name() + ": " + e->what();
374 inline folly::fbstring exception_wrapper::class_name() const {
378 : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
382 inline bool exception_wrapper::is_compatible_with() const noexcept {
383 return with_exception([](Ex const&) {});
386 [[noreturn]] inline void exception_wrapper::throwException() const {
388 onNoExceptionError();
391 template <class CatchFn, bool IsConst>
392 struct exception_wrapper::ExceptionTypeOf {
393 using type = arg_type<_t<std::decay<CatchFn>>>;
395 std::is_reference<type>::value,
396 "Always catch exceptions by reference.");
398 !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
399 "handle() or with_exception() called on a const exception_wrapper "
400 "and asked to catch a non-const exception. Handler will never fire. "
401 "Catch exception by const reference to fix this.");
404 // Nests a throw in the proper try/catch blocks
405 template <bool IsConst>
406 struct exception_wrapper::HandleReduce {
412 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
413 auto operator()(ThrowFn&& th, CatchFn& ca) const {
414 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
415 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
419 // If we got here because a catch function threw, rethrow.
432 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
433 auto operator()(ThrowFn&& th, CatchFn& ca) const {
434 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
438 // If we got here because a catch function threw, rethrow.
449 // When all the handlers expect types derived from std::exception, we can
450 // sometimes invoke the handlers without throwing any exceptions.
451 template <bool IsConst>
452 struct exception_wrapper::HandleStdExceptReduce {
453 using StdEx = AddConstIf<IsConst, std::exception>;
458 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
459 auto operator()(ThrowFn&& th, CatchFn& ca) const {
460 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
461 return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
463 if (auto e = const_cast<StdEx*>(th(continuation))) {
464 if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
477 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
478 auto operator()(ThrowFn&& th, CatchFn& ca) const {
479 return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
480 // The following continuation causes ca() to execute if *this contains
481 // an exception /not/ derived from std::exception.
482 auto continuation = [&ca](StdEx* e) {
483 return e != nullptr ? e : ((void)ca(), nullptr);
485 if (th(continuation) != nullptr) {
493 // Called when some types in the catch clauses are not derived from
495 template <class This, class... CatchFns>
496 inline void exception_wrapper::handle_(
497 std::false_type, This& this_, CatchFns&... fns) {
498 bool handled = false;
499 auto impl = exception_wrapper_detail::fold(
500 HandleReduce<std::is_const<This>::value>{&handled},
501 [&] { this_.throwException(); },
506 // Called when all types in the catch clauses are either derived from
507 // std::exception or a catch-all clause.
508 template <class This, class... CatchFns>
509 inline void exception_wrapper::handle_(
510 std::true_type, This& this_, CatchFns&... fns) {
511 using StdEx = exception_wrapper_detail::
512 AddConstIf<std::is_const<This>::value, std::exception>;
513 auto impl = exception_wrapper_detail::fold(
514 HandleStdExceptReduce<std::is_const<This>::value>{},
515 [&](auto&& continuation) {
517 const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
520 // This continuation gets evaluated if CatchFns... does not include a
521 // catch-all handler. It is a no-op.
522 auto continuation = [](StdEx* ex) { return ex; };
523 if (StdEx* e = impl(continuation)) {
524 throw *e; // Not handled. Throw.
528 namespace exception_wrapper_detail {
529 template <class Ex, class Fn>
532 auto operator()(Ex& ex) {
537 template <class Ex, class Fn>
538 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
539 return {std::move(fn)};
542 inline Fn catch_(void const*, Fn fn) {
545 } // namespace exception_wrapper_detail
547 template <class Ex, class This, class Fn>
548 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
553 auto fn = exception_wrapper_detail::catch_(
554 static_cast<Ex*>(nullptr), std::move(fn_));
555 auto&& all = [&](...) { handled = false; };
556 handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
560 template <class Ex, class Fn>
561 inline bool exception_wrapper::with_exception(Fn fn) {
562 return with_exception_<Ex>(*this, std::move(fn));
564 template <class Ex, class Fn>
565 inline bool exception_wrapper::with_exception(Fn fn) const {
566 return with_exception_<Ex const>(*this, std::move(fn));
569 template <class... CatchFns>
570 inline void exception_wrapper::handle(CatchFns... fns) {
572 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
574 onNoExceptionError();
576 this->handle_(AllStdEx{}, *this, fns...);
578 template <class... CatchFns>
579 inline void exception_wrapper::handle(CatchFns... fns) const {
581 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
583 onNoExceptionError();
585 this->handle_(AllStdEx{}, *this, fns...);