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_) {
290 namespace exception_wrapper_detail {
292 Ex&& dont_slice(Ex&& ex) {
293 assert(typeid(ex) == typeid(_t<std::decay<Ex>>) ||
294 !"Dynamic and static exception types don't match. Exception would "
295 "be sliced when storing in exception_wrapper.");
296 return std::forward<Ex>(ex);
305 exception_wrapper::IsStdException<Ex_>,
306 exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
307 inline exception_wrapper::exception_wrapper(Ex&& ex)
309 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
310 PlacementOf<Ex_>{}} {
317 exception_wrapper::IsRegularExceptionType<Ex_>::value)>
318 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
320 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
321 PlacementOf<Ex_>{}} {
324 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
325 exception_wrapper tmp(std::move(that));
326 that = std::move(*this);
327 *this = std::move(tmp);
330 inline exception_wrapper::operator bool() const noexcept {
331 return vptr_ != &uninit_;
334 inline bool exception_wrapper::operator!() const noexcept {
335 return !static_cast<bool>(*this);
338 inline void exception_wrapper::reset() {
339 vptr_->delete_(this);
342 inline bool exception_wrapper::has_exception_ptr() const noexcept {
343 return vptr_ == &ExceptionPtr::ops_;
346 inline std::exception* exception_wrapper::get_exception() noexcept {
347 return const_cast<std::exception*>(vptr_->get_exception_(this));
349 inline std::exception const* exception_wrapper::get_exception() const noexcept {
350 return vptr_->get_exception_(this);
353 template <typename Ex>
354 inline Ex* exception_wrapper::get_object() noexcept {
356 with_exception([&](Ex& ex) { object = &ex; });
360 template <typename Ex>
361 inline Ex const* exception_wrapper::get_object() const noexcept {
362 Ex const* object{nullptr};
363 with_exception([&](Ex const& ex) { object = &ex; });
367 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
369 // Computing an exception_ptr is expensive so cache the result.
370 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
372 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
373 return vptr_->get_exception_ptr_(this).eptr_.ptr_;
376 inline std::type_info const& exception_wrapper::none() noexcept {
379 inline std::type_info const& exception_wrapper::unknown() noexcept {
380 return typeid(Unknown);
383 inline std::type_info const& exception_wrapper::type() const noexcept {
384 return *vptr_->type_(this);
387 inline folly::fbstring exception_wrapper::what() const {
388 if (auto e = get_exception()) {
389 return class_name() + ": " + e->what();
394 inline folly::fbstring exception_wrapper::class_name() const {
398 : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
402 inline bool exception_wrapper::is_compatible_with() const noexcept {
403 return with_exception([](Ex const&) {});
406 [[noreturn]] inline void exception_wrapper::throw_exception() const {
408 onNoExceptionError();
411 template <class CatchFn, bool IsConst>
412 struct exception_wrapper::ExceptionTypeOf {
413 using type = arg_type<_t<std::decay<CatchFn>>>;
415 std::is_reference<type>::value,
416 "Always catch exceptions by reference.");
418 !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
419 "handle() or with_exception() called on a const exception_wrapper "
420 "and asked to catch a non-const exception. Handler will never fire. "
421 "Catch exception by const reference to fix this.");
424 // Nests a throw in the proper try/catch blocks
425 template <bool IsConst>
426 struct exception_wrapper::HandleReduce {
432 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
433 auto operator()(ThrowFn&& th, CatchFn& ca) const {
434 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
435 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
439 // If we got here because a catch function threw, rethrow.
452 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
453 auto operator()(ThrowFn&& th, CatchFn& ca) const {
454 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
458 // If we got here because a catch function threw, rethrow.
469 // When all the handlers expect types derived from std::exception, we can
470 // sometimes invoke the handlers without throwing any exceptions.
471 template <bool IsConst>
472 struct exception_wrapper::HandleStdExceptReduce {
473 using StdEx = AddConstIf<IsConst, std::exception>;
478 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
479 auto operator()(ThrowFn&& th, CatchFn& ca) const {
480 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
481 return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
483 if (auto e = const_cast<StdEx*>(th(continuation))) {
484 if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
497 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
498 auto operator()(ThrowFn&& th, CatchFn& ca) const {
499 return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
500 // The following continuation causes ca() to execute if *this contains
501 // an exception /not/ derived from std::exception.
502 auto continuation = [&ca](StdEx* e) {
503 return e != nullptr ? e : ((void)ca(), nullptr);
505 if (th(continuation) != nullptr) {
513 // Called when some types in the catch clauses are not derived from
515 template <class This, class... CatchFns>
516 inline void exception_wrapper::handle_(
517 std::false_type, This& this_, CatchFns&... fns) {
518 bool handled = false;
519 auto impl = exception_wrapper_detail::fold(
520 HandleReduce<std::is_const<This>::value>{&handled},
521 [&] { this_.throw_exception(); },
526 // Called when all types in the catch clauses are either derived from
527 // std::exception or a catch-all clause.
528 template <class This, class... CatchFns>
529 inline void exception_wrapper::handle_(
530 std::true_type, This& this_, CatchFns&... fns) {
531 using StdEx = exception_wrapper_detail::
532 AddConstIf<std::is_const<This>::value, std::exception>;
533 auto impl = exception_wrapper_detail::fold(
534 HandleStdExceptReduce<std::is_const<This>::value>{},
535 [&](auto&& continuation) {
537 const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
540 // This continuation gets evaluated if CatchFns... does not include a
541 // catch-all handler. It is a no-op.
542 auto continuation = [](StdEx* ex) { return ex; };
543 if (StdEx* e = impl(continuation)) {
544 throw *e; // Not handled. Throw.
548 namespace exception_wrapper_detail {
549 template <class Ex, class Fn>
552 auto operator()(Ex& ex) {
557 template <class Ex, class Fn>
558 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
559 return {std::move(fn)};
562 inline Fn catch_(void const*, Fn fn) {
565 } // namespace exception_wrapper_detail
567 template <class Ex, class This, class Fn>
568 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
573 auto fn = exception_wrapper_detail::catch_(
574 static_cast<Ex*>(nullptr), std::move(fn_));
575 auto&& all = [&](...) { handled = false; };
576 handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
580 template <class Ex, class Fn>
581 inline bool exception_wrapper::with_exception(Fn fn) {
582 return with_exception_<Ex>(*this, std::move(fn));
584 template <class Ex, class Fn>
585 inline bool exception_wrapper::with_exception(Fn fn) const {
586 return with_exception_<Ex const>(*this, std::move(fn));
589 template <class... CatchFns>
590 inline void exception_wrapper::handle(CatchFns... fns) {
592 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
594 onNoExceptionError();
596 this->handle_(AllStdEx{}, *this, fns...);
598 template <class... CatchFns>
599 inline void exception_wrapper::handle(CatchFns... fns) const {
601 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
603 onNoExceptionError();
605 this->handle_(AllStdEx{}, *this, fns...);