X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FExceptionWrapper.h;h=bd5bc11972c5555ace1a269d767e8e1459c0a470;hb=120cc11d827f07e1bcbcc12132d871f167d5342b;hp=3b44e53f2d7709c6a773090e82bd5d8bfe1af3cb;hpb=a3259f635f9b2fb5a109672c0f16d4a497fa3b67;p=folly.git diff --git a/folly/ExceptionWrapper.h b/folly/ExceptionWrapper.h index 3b44e53f..bd5bc119 100644 --- a/folly/ExceptionWrapper.h +++ b/folly/ExceptionWrapper.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,18 @@ * limitations under the License. */ -#ifndef FOLLY_EXCEPTIONWRAPPER_H -#define FOLLY_EXCEPTIONWRAPPER_H +#pragma once -#include #include #include -#include -#include +#include +#include +#include +#include + +#include +#include +#include namespace folly { @@ -59,8 +63,9 @@ namespace folly { * can test or extract a pointer to a specific exception type with very little * overhead. * - * Example usage: - * + * \par Example usage: + * \par + * \code * exception_wrapper globalExceptionWrapper; * * // Thread1 @@ -88,34 +93,34 @@ namespace folly { * * // Thread2: Exceptions are bad! * void processResult() { - * auto ep = globalExceptionWrapper.get(); - * if (!ep.with_exception([&]( - * FacePlantException& faceplant) { - * LOG(ERROR) << "FACEPLANT"; - * })) { - * ep.with_exception([&]( - * FailWhaleException& failwhale) { + * globalExceptionWrapper.with_exception( + * [&](FacePlantException& faceplant) { + * LOG(ERROR) << "FACEPLANT"; + * }) || + * globalExceptionWrapper.with_exception( + * [&](FailWhaleException& failwhale) { * LOG(ERROR) << "FAILWHALE!"; - * }); - * } + * }) || + * LOG(FATAL) << "Unrecognized exception"; * } + * \endcode * */ class exception_wrapper { - protected: - template - struct optimize; + private: + template + using is_exception_ = std::is_base_of; public: exception_wrapper() = default; - // Implicitly construct an exception_wrapper from a qualifying exception. - // See the optimize struct for details. - template ::value>::type> + template < + typename Ex, + typename DEx = _t>, + typename = _t::value>>, + typename = decltype(DEx(std::forward(std::declval())))> /* implicit */ exception_wrapper(Ex&& exn) { - item_ = std::make_shared(std::forward(exn)); - throwfn_ = folly::detail::Thrower::doThrow; + assign_sptr(std::forward(exn)); } // The following two constructors are meant to emulate the behavior of @@ -148,13 +153,9 @@ class exception_wrapper { assign_eptr(eptr); } - void throwException() const { - if (throwfn_) { - throwfn_(item_.get()); - } else if (eptr_) { - std::rethrow_exception(eptr_); - } - } + // If the exception_wrapper does not contain an exception, std::terminate() + // is invoked to assure the [[noreturn]] behaviour. + [[noreturn]] void throwException() const; explicit operator bool() const { return item_ || eptr_; @@ -184,107 +185,76 @@ class exception_wrapper { std::exception* getCopied() { return item_.get(); } const std::exception* getCopied() const { return item_.get(); } - fbstring what() const { - if (item_) { - return exceptionStr(*item_); - } else if (eptr_) { - return estr_; - } else { - return fbstring(); - } + fbstring what() const; + fbstring class_name() const; + + template + bool is_compatible_with() const { + return with_exception([](const Ex&) {}); } - fbstring class_name() const { - if (item_) { - auto& i = *item_; - return demangle(typeid(i)); - } else if (eptr_) { - return ename_; - } else { - return fbstring(); - } + template + bool with_exception(F&& f) { + using arg_type = _t::arg_type>>; + return with_exception(std::forward(f)); } - template - bool is_compatible_with() const { - if (item_) { - return dynamic_cast(item_.get()); - } else if (eptr_) { - try { - std::rethrow_exception(eptr_); - } catch (std::exception& e) { - return dynamic_cast(&e); - } catch (...) { - // fall through - } - } - return false; + template + bool with_exception(F&& f) const { + using arg_type = _t::arg_type>>; + return with_exception(std::forward(f)); } // If this exception wrapper wraps an exception of type Ex, with_exception // will call f with the wrapped exception as an argument and return true, and // will otherwise return false. template - typename std::enable_if< - std::is_base_of::type>::value, - bool>::type - with_exception(F f) { - return with_exception1::type>(f, this); + bool with_exception(F f) { + return with_exception1<_t>>(std::forward(f), this); } // Const overload template - typename std::enable_if< - std::is_base_of::type>::value, - bool>::type - with_exception(F f) const { - return with_exception1::type>(f, this); + bool with_exception(F f) const { + return with_exception1<_t>>(std::forward(f), this); } - // Overload for non-exceptions. Always rethrows. - template - typename std::enable_if< - !std::is_base_of::type>::value, - bool>::type - with_exception(F f) const { - try { - throwException(); - } catch (typename std::decay::type& e) { - f(e); - return true; - } catch (...) { - // fall through - } - return false; - } - - std::exception_ptr getExceptionPtr() const { + std::exception_ptr to_exception_ptr() const { if (eptr_) { return eptr_; } try { - throwException(); + if (*this) { + throwException(); + } } catch (...) { return std::current_exception(); } return std::exception_ptr(); } -protected: + private: + template + void assign_sptr(Args&&... args) { + this->item_ = std::make_shared(std::forward(args)...); + this->throwfn_ = Thrower::doThrow; + } + template - struct optimize { - static const bool value = - std::is_base_of::value && - std::is_copy_assignable::value && - !std::is_abstract::value; - }; + _t::value>> assign_eptr( + std::exception_ptr eptr, + Ex& e) { + this->eptr_ = eptr; + this->eobj_ = &const_cast<_t>&>(e); + } template - void assign_eptr(std::exception_ptr eptr, Ex& e) { + _t::value>> assign_eptr( + std::exception_ptr eptr, + Ex& e) { this->eptr_ = eptr; - this->estr_ = exceptionStr(e).toStdString(); - this->ename_ = demangle(typeid(e)).toStdString(); + this->etype_ = &typeid(e); } void assign_eptr(std::exception_ptr eptr) { @@ -295,37 +265,70 @@ protected: // store a copy of the concrete type, and a helper function so we // can rethrow it. std::shared_ptr item_; - void (*throwfn_)(std::exception*){nullptr}; + void (*throwfn_)(std::exception&){nullptr}; // Fallback case: store the library wrapper, which is less efficient // but gets the job done. Also store exceptionPtr() the name of the // exception type, so we can at least get those back out without // having to rethrow. std::exception_ptr eptr_; - std::string estr_; - std::string ename_; + std::exception* eobj_{nullptr}; + const std::type_info* etype_{nullptr}; template friend exception_wrapper make_exception_wrapper(Args&&... args); -private: + private: + template + struct functor_traits { + template + struct impl; + template + struct impl { using arg_type = A; }; + template + struct impl { using arg_type = A; }; + using functor_op = decltype(&_t>::operator()); + using arg_type = typename impl::arg_type; + }; + + template + class Thrower { + public: + static void doThrow(std::exception& obj) { + throw static_cast(obj); + } + }; + + template + static _t::value, T*>> + try_dynamic_cast_exception(F* from) { + return dynamic_cast(from); + } + template + static _t::value, T*>> + try_dynamic_cast_exception(F*) { + return nullptr; + } + // What makes this useful is that T can be exception_wrapper* or // const exception_wrapper*, and the compiler will use the // instantiation which works with F. template static bool with_exception1(F f, T* that) { - if (that->item_) { - if (auto ex = dynamic_cast(that->item_.get())) { + using CEx = _t::value, const Ex, Ex>>; + if (is_exception_::value && + (that->item_ || (that->eptr_ && that->eobj_))) { + auto raw = + that->item_ ? that->item_.get() : that->eptr_ ? that->eobj_ : nullptr; + if (auto ex = try_dynamic_cast_exception(raw)) { f(*ex); return true; } } else if (that->eptr_) { try { std::rethrow_exception(that->eptr_); - } catch (std::exception& e) { - if (auto ex = dynamic_cast(&e)) { - f(*ex); - return true; - } + } catch (CEx& e) { + f(e); + return true; } catch (...) { // fall through } @@ -334,14 +337,16 @@ private: } }; -template +template exception_wrapper make_exception_wrapper(Args&&... args) { exception_wrapper ew; - ew.item_ = std::make_shared(std::forward(args)...); - ew.throwfn_ = folly::detail::Thrower::doThrow; + ew.assign_sptr(std::forward(args)...); return ew; } +// For consistency with exceptionStr() functions in ExceptionString.h +fbstring exceptionStr(const exception_wrapper& ew); + /* * try_and_catch is a simple replacement for try {} catch(){} that allows you to * specify which derived exceptions you would like to catch and store in an @@ -382,60 +387,42 @@ exception_wrapper make_exception_wrapper(Args&&... args) { * }); */ -template -class try_and_catch; +namespace try_and_catch_detail { -template -class try_and_catch : - public try_and_catch { - public: - template - explicit try_and_catch(F&& fn) : Base() { - call_fn(fn); - } +template +using is_wrap_ctor = std::is_constructible; - protected: - typedef try_and_catch Base; - - try_and_catch() : Base() {} +template +inline _t::value, exception_wrapper>> make( + Ex& ex) { + return exception_wrapper(std::current_exception(), ex); +} - template - typename std::enable_if::value>::type - assign_exception(Ex& e, std::exception_ptr eptr) { - exception_wrapper::assign_eptr(eptr, e); - } +template +inline _t::value, exception_wrapper>> make( + Ex& ex) { + return typeid(Ex&) == typeid(ex) + ? exception_wrapper(ex) + : exception_wrapper(std::current_exception(), ex); +} - template - typename std::enable_if::value>::type - assign_exception(Ex& e, std::exception_ptr eptr) { - this->item_ = std::make_shared(e); - this->throwfn_ = folly::detail::Thrower::doThrow; - } +template +inline exception_wrapper impl(F&& f) { + return (f(), exception_wrapper()); +} - template - void call_fn(F&& fn) { - try { - Base::call_fn(std::move(fn)); - } catch (LastException& e) { - if (typeid(e) == typeid(LastException&)) { - assign_exception(e, std::current_exception()); - } else { - exception_wrapper::assign_eptr(std::current_exception(), e); - } - } +template +inline exception_wrapper impl(F&& f) { + try { + return impl(std::forward(f)); + } catch (Ex& ex) { + return make(ex); } -}; - -template<> -class try_and_catch<> : public exception_wrapper { - public: - try_and_catch() {} +} +} // try_and_catch_detail - protected: - template - void call_fn(F&& fn) { - fn(); - } -}; +template +exception_wrapper try_and_catch(F&& fn) { + return try_and_catch_detail::impl(std::forward(fn)); } -#endif +} // folly