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 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
355 // Computing an exception_ptr is expensive so cache the result.
356 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
358 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
359 return vptr_->get_exception_ptr_(this).eptr_.ptr_;
362 inline std::type_info const& exception_wrapper::none() noexcept {
365 inline std::type_info const& exception_wrapper::unknown() noexcept {
366 return typeid(Unknown);
369 inline std::type_info const& exception_wrapper::type() const noexcept {
370 return *vptr_->type_(this);
373 inline folly::fbstring exception_wrapper::what() const {
374 if (auto e = get_exception()) {
375 return class_name() + ": " + e->what();
380 inline folly::fbstring exception_wrapper::class_name() const {
384 : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
388 inline bool exception_wrapper::is_compatible_with() const noexcept {
389 return with_exception([](Ex const&) {});
392 [[noreturn]] inline void exception_wrapper::throw_exception() const {
394 onNoExceptionError();
397 template <class CatchFn, bool IsConst>
398 struct exception_wrapper::ExceptionTypeOf {
399 using type = arg_type<_t<std::decay<CatchFn>>>;
401 std::is_reference<type>::value,
402 "Always catch exceptions by reference.");
404 !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
405 "handle() or with_exception() called on a const exception_wrapper "
406 "and asked to catch a non-const exception. Handler will never fire. "
407 "Catch exception by const reference to fix this.");
410 // Nests a throw in the proper try/catch blocks
411 template <bool IsConst>
412 struct exception_wrapper::HandleReduce {
418 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
419 auto operator()(ThrowFn&& th, CatchFn& ca) const {
420 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
421 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
425 // If we got here because a catch function threw, rethrow.
438 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
439 auto operator()(ThrowFn&& th, CatchFn& ca) const {
440 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
444 // If we got here because a catch function threw, rethrow.
455 // When all the handlers expect types derived from std::exception, we can
456 // sometimes invoke the handlers without throwing any exceptions.
457 template <bool IsConst>
458 struct exception_wrapper::HandleStdExceptReduce {
459 using StdEx = AddConstIf<IsConst, std::exception>;
464 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
465 auto operator()(ThrowFn&& th, CatchFn& ca) const {
466 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
467 return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
469 if (auto e = const_cast<StdEx*>(th(continuation))) {
470 if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
483 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
484 auto operator()(ThrowFn&& th, CatchFn& ca) const {
485 return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
486 // The following continuation causes ca() to execute if *this contains
487 // an exception /not/ derived from std::exception.
488 auto continuation = [&ca](StdEx* e) {
489 return e != nullptr ? e : ((void)ca(), nullptr);
491 if (th(continuation) != nullptr) {
499 // Called when some types in the catch clauses are not derived from
501 template <class This, class... CatchFns>
502 inline void exception_wrapper::handle_(
503 std::false_type, This& this_, CatchFns&... fns) {
504 bool handled = false;
505 auto impl = exception_wrapper_detail::fold(
506 HandleReduce<std::is_const<This>::value>{&handled},
507 [&] { this_.throw_exception(); },
512 // Called when all types in the catch clauses are either derived from
513 // std::exception or a catch-all clause.
514 template <class This, class... CatchFns>
515 inline void exception_wrapper::handle_(
516 std::true_type, This& this_, CatchFns&... fns) {
517 using StdEx = exception_wrapper_detail::
518 AddConstIf<std::is_const<This>::value, std::exception>;
519 auto impl = exception_wrapper_detail::fold(
520 HandleStdExceptReduce<std::is_const<This>::value>{},
521 [&](auto&& continuation) {
523 const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
526 // This continuation gets evaluated if CatchFns... does not include a
527 // catch-all handler. It is a no-op.
528 auto continuation = [](StdEx* ex) { return ex; };
529 if (StdEx* e = impl(continuation)) {
530 throw *e; // Not handled. Throw.
534 namespace exception_wrapper_detail {
535 template <class Ex, class Fn>
538 auto operator()(Ex& ex) {
543 template <class Ex, class Fn>
544 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
545 return {std::move(fn)};
548 inline Fn catch_(void const*, Fn fn) {
551 } // namespace exception_wrapper_detail
553 template <class Ex, class This, class Fn>
554 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
559 auto fn = exception_wrapper_detail::catch_(
560 static_cast<Ex*>(nullptr), std::move(fn_));
561 auto&& all = [&](...) { handled = false; };
562 handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
566 template <class Ex, class Fn>
567 inline bool exception_wrapper::with_exception(Fn fn) {
568 return with_exception_<Ex>(*this, std::move(fn));
570 template <class Ex, class Fn>
571 inline bool exception_wrapper::with_exception(Fn fn) const {
572 return with_exception_<Ex const>(*this, std::move(fn));
575 template <class... CatchFns>
576 inline void exception_wrapper::handle(CatchFns... fns) {
578 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
580 onNoExceptionError();
582 this->handle_(AllStdEx{}, *this, fns...);
584 template <class... CatchFns>
585 inline void exception_wrapper::handle(CatchFns... fns) const {
587 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
589 onNoExceptionError();
591 this->handle_(AllStdEx{}, *this, fns...);