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_type2_ {};
25 template <class Ret, class Class, class Arg>
26 struct exception_wrapper::arg_type2_<Ret (Class::*)(Arg)> {
29 template <class Ret, class Class, class Arg>
30 struct exception_wrapper::arg_type2_<Ret (Class::*)(Arg) const> {
33 template <class Ret, class Class>
34 struct exception_wrapper::arg_type2_<Ret (Class::*)(...)> {
35 using type = AnyException;
37 template <class Ret, class Class>
38 struct exception_wrapper::arg_type2_<Ret (Class::*)(...) const> {
39 using type = AnyException;
42 template <class Fn, class>
43 struct exception_wrapper::arg_type_ {};
45 struct exception_wrapper::arg_type_<Fn, void_t<decltype(&Fn::operator())>>
46 : public arg_type2_<decltype(&Fn::operator())> {};
47 template <class Ret, class Arg>
48 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
52 struct exception_wrapper::arg_type_<Ret (*)(...)> {
53 using type = AnyException;
56 template <class Ret, class... Args>
57 inline Ret exception_wrapper::noop_(Args...) {
61 inline std::type_info const* exception_wrapper::uninit_type_(
62 exception_wrapper const*) {
66 template <class Ex, class DEx>
67 inline exception_wrapper::Buffer::Buffer(in_place_t, Ex&& ex) {
68 ::new (static_cast<void*>(&buff_)) DEx(std::forward<Ex>(ex));
72 inline Ex& exception_wrapper::Buffer::as() noexcept {
73 return *static_cast<Ex*>(static_cast<void*>(&buff_));
76 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
77 return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
80 inline std::exception const* exception_wrapper::as_exception_or_null_(
81 std::exception const& ex) {
84 inline std::exception const* exception_wrapper::as_exception_or_null_(
89 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
90 std::exception const& e) {
91 return reinterpret_cast<std::uintptr_t>(&e);
93 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(AnyException e) {
94 return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
96 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
97 return 0 == exception_or_type_ % 2;
99 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
101 return reinterpret_cast<std::exception const*>(exception_or_type_);
103 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
104 return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
107 inline void exception_wrapper::ExceptionPtr::copy_(
108 exception_wrapper const* from, exception_wrapper* to) {
109 ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
111 inline void exception_wrapper::ExceptionPtr::move_(
112 exception_wrapper* from, exception_wrapper* to) {
113 ::new (static_cast<void*>(&to->eptr_))
114 ExceptionPtr(std::move(from->eptr_));
117 inline void exception_wrapper::ExceptionPtr::delete_(
118 exception_wrapper* that) {
119 that->eptr_.~ExceptionPtr();
120 that->vptr_ = &uninit_;
122 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
123 exception_wrapper const* that) {
124 std::rethrow_exception(that->eptr_.ptr_);
126 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
127 exception_wrapper const* that) {
128 if (auto e = get_exception_(that)) {
131 return that->eptr_.as_type_();
133 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
134 exception_wrapper const* that) {
135 return that->eptr_.has_exception_() ? that->eptr_.as_exception_()
138 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
139 exception_wrapper const* that) {
144 inline void exception_wrapper::InPlace<Ex>::copy_(
145 exception_wrapper const* from, exception_wrapper* to) {
146 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
147 Ex(from->buff_.as<Ex>());
150 inline void exception_wrapper::InPlace<Ex>::move_(
151 exception_wrapper* from, exception_wrapper* to) {
152 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
153 Ex(std::move(from->buff_.as<Ex>()));
157 inline void exception_wrapper::InPlace<Ex>::delete_(
158 exception_wrapper* that) {
159 that->buff_.as<Ex>().~Ex();
160 that->vptr_ = &uninit_;
163 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
164 exception_wrapper const* that) {
165 throw that->buff_.as<Ex>(); // @nolint
168 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
169 exception_wrapper const*) {
173 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
174 exception_wrapper const* that) {
175 return as_exception_or_null_(that->buff_.as<Ex>());
178 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
179 exception_wrapper const* that) {
182 } catch (Ex const& ex) {
183 return exception_wrapper{std::current_exception(), ex};
188 [[noreturn]] inline void
189 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
190 throw ex_; // @nolint
193 inline std::exception const*
194 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
195 return as_exception_or_null_(ex_);
198 inline exception_wrapper
199 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
203 return exception_wrapper{std::current_exception(), ex};
206 inline void exception_wrapper::SharedPtr::copy_(
207 exception_wrapper const* from, exception_wrapper* to) {
208 ::new (static_cast<void*>(std::addressof(to->sptr_)))
209 SharedPtr(from->sptr_);
211 inline void exception_wrapper::SharedPtr::move_(
212 exception_wrapper* from, exception_wrapper* to) {
213 ::new (static_cast<void*>(std::addressof(to->sptr_)))
214 SharedPtr(std::move(from->sptr_));
217 inline void exception_wrapper::SharedPtr::delete_(
218 exception_wrapper* that) {
219 that->sptr_.~SharedPtr();
220 that->vptr_ = &uninit_;
222 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
223 exception_wrapper const* that) {
224 that->sptr_.ptr_->throw_();
225 folly::assume_unreachable();
227 inline std::type_info const* exception_wrapper::SharedPtr::type_(
228 exception_wrapper const* that) {
229 return that->sptr_.ptr_->info_;
231 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
232 exception_wrapper const* that) {
233 return that->sptr_.ptr_->get_exception_();
235 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
236 exception_wrapper const* that) {
237 return that->sptr_.ptr_->get_exception_ptr_();
240 template <class Ex, class DEx>
241 inline exception_wrapper::exception_wrapper(Ex&& ex, OnHeapTag)
242 : sptr_{std::make_shared<SharedPtr::Impl<DEx>>(std::forward<Ex>(ex))},
243 vptr_(&SharedPtr::ops_) {}
245 template <class Ex, class DEx>
246 inline exception_wrapper::exception_wrapper(Ex&& ex, InSituTag)
247 : buff_{in_place, std::forward<Ex>(ex)}, vptr_(&InPlace<DEx>::ops_) {}
249 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
250 : exception_wrapper{} {
251 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
254 inline exception_wrapper::exception_wrapper(
255 exception_wrapper const& that) : exception_wrapper{} {
256 that.vptr_->copy_(&that, this); // could throw
260 // If `this == &that`, this move assignment operator leaves the object in a
261 // valid but unspecified state.
262 inline exception_wrapper& exception_wrapper::operator=(
263 exception_wrapper&& that) noexcept {
264 vptr_->delete_(this); // Free the current exception
265 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
269 inline exception_wrapper& exception_wrapper::operator=(
270 exception_wrapper const& that) {
271 exception_wrapper(that).swap(*this);
275 inline exception_wrapper::~exception_wrapper() {
280 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
281 : eptr_{std::move(ptr), ExceptionPtr::as_int_(ex)},
282 vptr_(&ExceptionPtr::ops_) {
291 exception_wrapper::IsStdException<Ex_>,
292 exception_wrapper::IsRegularExceptionType<Ex_>>())>
293 inline exception_wrapper::exception_wrapper(Ex&& ex)
294 : exception_wrapper{std::forward<Ex>(ex), PlacementOf<Ex_>{}} {
296 assert(typeid(ex) == typeid(Ex_) ||
297 !"Dynamic and static exception types don't match. Exception would "
298 "be sliced when storing in exception_wrapper.");
305 exception_wrapper::IsRegularExceptionType<Ex_>())>
306 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
307 : exception_wrapper{std::forward<Ex>(ex), PlacementOf<Ex_>{}} {
309 assert(typeid(ex) == typeid(Ex_) ||
310 !"Dynamic and static exception types don't match. Exception would "
311 "be sliced when storing in exception_wrapper.");
314 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
315 exception_wrapper tmp(std::move(that));
316 that = std::move(*this);
317 *this = std::move(tmp);
320 inline exception_wrapper::operator bool() const noexcept {
321 return vptr_ != &uninit_;
324 inline bool exception_wrapper::operator!() const noexcept {
325 return !static_cast<bool>(*this);
328 inline void exception_wrapper::reset() {
329 vptr_->delete_(this);
332 inline bool exception_wrapper::has_exception_ptr() const noexcept {
333 return vptr_ == &ExceptionPtr::ops_;
336 inline std::exception* exception_wrapper::get_exception() noexcept {
337 return const_cast<std::exception*>(vptr_->get_exception_(this));
339 inline std::exception const* exception_wrapper::get_exception() const noexcept {
340 return vptr_->get_exception_(this);
343 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
345 // Computing an exception_ptr is expensive so cache the result.
346 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
348 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
349 return vptr_->get_exception_ptr_(this).eptr_.ptr_;
352 inline std::type_info const& exception_wrapper::none() noexcept {
355 inline std::type_info const& exception_wrapper::unknown() noexcept {
356 return typeid(Unknown);
359 inline std::type_info const& exception_wrapper::type() const noexcept {
360 return *vptr_->type_(this);
363 inline folly::fbstring exception_wrapper::what() const {
364 if (auto e = get_exception()) {
365 return class_name() + ": " + e->what();
370 inline folly::fbstring exception_wrapper::class_name() const {
374 : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
378 inline bool exception_wrapper::is_compatible_with() const noexcept {
379 return with_exception([](Ex const&) {});
382 [[noreturn]] inline void exception_wrapper::throwException() const {
384 onNoExceptionError();
387 template <class CatchFn, bool IsConst>
388 struct exception_wrapper::ExceptionTypeOf {
389 using type = arg_type<_t<std::decay<CatchFn>>>;
391 std::is_reference<type>::value,
392 "Always catch exceptions by reference.");
394 !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
395 "handle() or with_exception() called on a const exception_wrapper "
396 "and asked to catch a non-const exception. Handler will never fire. "
397 "Catch exception by const reference to fix this.");
400 // Nests a throw in the proper try/catch blocks
401 template <bool IsConst>
402 struct exception_wrapper::HandleReduce {
408 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
409 auto operator()(ThrowFn&& th, CatchFn& ca) const {
410 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
411 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
415 // If we got here because a catch function threw, rethrow.
428 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
429 auto operator()(ThrowFn&& th, CatchFn& ca) const {
430 return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
434 // If we got here because a catch function threw, rethrow.
445 // When all the handlers expect types derived from std::exception, we can
446 // sometimes invoke the handlers without throwing any exceptions.
447 template <bool IsConst>
448 struct exception_wrapper::HandleStdExceptReduce {
449 using StdEx = AddConstIf<IsConst, std::exception>;
454 FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
455 auto operator()(ThrowFn&& th, CatchFn& ca) const {
456 using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
457 return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
459 if (auto e = const_cast<StdEx*>(th(continuation))) {
460 if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
473 FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
474 auto operator()(ThrowFn&& th, CatchFn& ca) const {
475 return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
476 // The following continuation causes ca() to execute if *this contains
477 // an exception /not/ derived from std::exception.
478 auto continuation = [&ca](StdEx* e) {
479 return e != nullptr ? e : ((void)ca(), nullptr);
481 if (th(continuation) != nullptr) {
489 // Called when some types in the catch clauses are not derived from
491 template <class This, class... CatchFns>
492 inline void exception_wrapper::handle_(
493 std::false_type, This& this_, CatchFns&... fns) {
494 bool handled = false;
495 auto impl = exception_wrapper_detail::fold(
496 HandleReduce<std::is_const<This>::value>{&handled},
497 [&] { this_.throwException(); },
502 // Called when all types in the catch clauses are either derived from
503 // std::exception or a catch-all clause.
504 template <class This, class... CatchFns>
505 inline void exception_wrapper::handle_(
506 std::true_type, This& this_, CatchFns&... fns) {
507 using StdEx = exception_wrapper_detail::
508 AddConstIf<std::is_const<This>::value, std::exception>;
509 auto impl = exception_wrapper_detail::fold(
510 HandleStdExceptReduce<std::is_const<This>::value>{},
511 [&](auto&& continuation) {
513 const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
516 // This continuation gets evaluated if CatchFns... does not include a
517 // catch-all handler. It is a no-op.
518 auto continuation = [](StdEx* ex) { return ex; };
519 if (StdEx* e = impl(continuation)) {
520 throw *e; // Not handled. Throw.
524 namespace exception_wrapper_detail {
525 template <class Ex, class Fn>
528 auto operator()(Ex& ex) {
533 template <class Ex, class Fn>
534 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
535 return {std::move(fn)};
538 inline Fn catch_(void const*, Fn fn) {
541 } // namespace exception_wrapper_detail
543 template <class Ex, class This, class Fn>
544 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
549 auto fn = exception_wrapper_detail::catch_(
550 static_cast<Ex*>(nullptr), std::move(fn_));
551 auto&& all = [&](...) { handled = false; };
552 handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
556 template <class Ex, class Fn>
557 inline bool exception_wrapper::with_exception(Fn fn) {
558 return with_exception_<Ex>(*this, std::move(fn));
560 template <class Ex, class Fn>
561 inline bool exception_wrapper::with_exception(Fn fn) const {
562 return with_exception_<Ex const>(*this, std::move(fn));
565 template <class... CatchFns>
566 inline void exception_wrapper::handle(CatchFns... fns) {
568 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
570 onNoExceptionError();
572 this->handle_(AllStdEx{}, *this, fns...);
574 template <class... CatchFns>
575 inline void exception_wrapper::handle(CatchFns... fns) const {
577 exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
579 onNoExceptionError();
581 this->handle_(AllStdEx{}, *this, fns...);