/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
class exception_wrapper {
+ protected:
+ template <typename Ex>
+ struct optimize;
+
public:
- exception_wrapper() : throwfn_(nullptr) { }
+ exception_wrapper() = default;
+
+ // Implicitly construct an exception_wrapper from a qualifying exception.
+ // See the optimize struct for details.
+ template <typename Ex, typename =
+ typename std::enable_if<optimize<typename std::decay<Ex>::type>::value>
+ ::type>
+ /* implicit */ exception_wrapper(Ex&& exn) {
+ typedef typename std::decay<Ex>::type DEx;
+ item_ = std::make_shared<DEx>(std::forward<Ex>(exn));
+ throwfn_ = folly::detail::Thrower<DEx>::doThrow;
+ }
+
+ // The following two constructors are meant to emulate the behavior of
+ // try_and_catch in performance sensitive code as well as to be flexible
+ // enough to wrap exceptions of unknown type. There is an overload that
+ // takes an exception reference so that the wrapper can extract and store
+ // the exception's type and what() when possible.
+ //
+ // The canonical use case is to construct an all-catching exception wrapper
+ // with minimal overhead like so:
+ //
+ // try {
+ // // some throwing code
+ // } catch (const std::exception& e) {
+ // // won't lose e's type and what()
+ // exception_wrapper ew{std::current_exception(), e};
+ // } catch (...) {
+ // // everything else
+ // exception_wrapper ew{std::current_exception()};
+ // }
+ //
+ // try_and_catch is cleaner and preferable. Use it unless you're sure you need
+ // something like this instead.
+ template <typename Ex>
+ explicit exception_wrapper(std::exception_ptr eptr, Ex& exn) {
+ assign_eptr(eptr, exn);
+ }
+
+ explicit exception_wrapper(std::exception_ptr eptr) {
+ assign_eptr(eptr);
+ }
void throwException() const {
if (throwfn_) {
return item_ || eptr_;
}
+ // This implementation is similar to std::exception_ptr's implementation
+ // where two exception_wrappers are equal when the address in the underlying
+ // reference field both point to the same exception object. The reference
+ // field remains the same when the exception_wrapper is copied or when
+ // the exception_wrapper is "rethrown".
+ bool operator==(const exception_wrapper& a) const {
+ if (item_) {
+ return a.item_ && item_.get() == a.item_.get();
+ } else {
+ return eptr_ == a.eptr_;
+ }
+ }
+
+ bool operator!=(const exception_wrapper& a) const {
+ return !(*this == a);
+ }
+
// This will return a non-nullptr only if the exception is held as a
// copy. It is the only interface which will distinguish between an
// exception held this way, and by exception_ptr. You probably
fbstring what() const {
if (item_) {
- return exceptionStr(*item_.get());
+ return exceptionStr(*item_);
} else if (eptr_) {
return estr_;
} else {
}
}
+ fbstring class_name() const {
+ if (item_) {
+ auto& i = *item_;
+ return demangle(typeid(i));
+ } else if (eptr_) {
+ return ename_;
+ } else {
+ return fbstring();
+ }
+ }
+
template <class Ex>
bool is_compatible_with() const {
if (item_) {
- return dynamic_cast<const Ex*>(getCopied());
+ return dynamic_cast<const Ex*>(item_.get());
} else if (eptr_) {
try {
std::rethrow_exception(eptr_);
return false;
}
+ // 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 <class Ex, class F>
- bool with_exception(F f) {
- if (item_) {
- if (auto ex = dynamic_cast<Ex*>(getCopied())) {
- f(*ex);
- return true;
- }
- } else if (eptr_) {
- try {
- std::rethrow_exception(eptr_);
- } catch (std::exception& e) {
- if (auto ex = dynamic_cast<Ex*>(&e)) {
- f(*ex);
- return true;
- }
- } catch (...) {
- // fall through
- }
- }
- return false;
+ typename std::enable_if<
+ std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) {
+ return with_exception1<typename std::decay<Ex>::type>(f, this);
+ }
+
+ // Const overload
+ template <class Ex, class F>
+ typename std::enable_if<
+ std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) const {
+ return with_exception1<const typename std::decay<Ex>::type>(f, this);
}
+ // Overload for non-exceptions. Always rethrows.
template <class Ex, class F>
- bool with_exception(F f) const {
- return with_exception<const Ex>(f);
+ typename std::enable_if<
+ !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) const {
+ try {
+ throwException();
+ } catch (typename std::decay<Ex>::type& e) {
+ f(e);
+ return true;
+ } catch (...) {
+ // fall through
+ }
+ return false;
}
std::exception_ptr getExceptionPtr() const {
return std::exception_ptr();
}
- protected:
+protected:
+ template <typename Ex>
+ struct optimize {
+ static const bool value =
+ std::is_base_of<std::exception, Ex>::value &&
+ std::is_copy_assignable<Ex>::value &&
+ !std::is_abstract<Ex>::value;
+ };
+
+ template <typename Ex>
+ void assign_eptr(std::exception_ptr eptr, Ex& e) {
+ this->eptr_ = eptr;
+ this->estr_ = exceptionStr(e).toStdString();
+ this->ename_ = demangle(typeid(e)).toStdString();
+ }
+
+ void assign_eptr(std::exception_ptr eptr) {
+ this->eptr_ = eptr;
+ }
+
// Optimized case: if we know what type the exception is, we can
// store a copy of the concrete type, and a helper function so we
// can rethrow it.
std::shared_ptr<std::exception> item_;
- void (*throwfn_)(std::exception*);
+ void (*throwfn_)(std::exception*){nullptr};
// Fallback case: store the library wrapper, which is less efficient
- // but gets the job done. Also store the the what() string, so we
- // can at least get it back out without having to rethrow.
+ // 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_;
template <class T, class... Args>
friend exception_wrapper make_exception_wrapper(Args&&... args);
+
+private:
+ // 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 <class Ex, class F, class T>
+ static bool with_exception1(F f, T* that) {
+ if (that->item_) {
+ if (auto ex = dynamic_cast<Ex*>(that->item_.get())) {
+ f(*ex);
+ return true;
+ }
+ } else if (that->eptr_) {
+ try {
+ std::rethrow_exception(that->eptr_);
+ } catch (std::exception& e) {
+ if (auto ex = dynamic_cast<Ex*>(&e)) {
+ f(*ex);
+ return true;
+ }
+ } catch (...) {
+ // fall through
+ }
+ }
+ return false;
+ }
};
template <class T, class... Args>
return ew;
}
+// For consistency with exceptionStr() functions in String.h
+inline fbstring exceptionStr(const exception_wrapper& ew) {
+ return ew.what();
+}
+
/*
* 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
try_and_catch() : Base() {}
template <typename Ex>
- typename std::enable_if<std::is_base_of<std::exception, Ex>::value>::type
- assign_eptr(Ex& e) {
- this->eptr_ = std::current_exception();
- this->estr_ = exceptionStr(e).toStdString();
- }
-
- template <typename Ex>
- typename std::enable_if<!std::is_base_of<std::exception, Ex>::value>::type
- assign_eptr(Ex& e) {
- this->eptr_ = std::current_exception();
- this->estr_ = exceptionStr(e).toStdString();
- }
-
- template <typename Ex>
- struct optimize {
- static const bool value =
- std::is_base_of<std::exception, Ex>::value &&
- std::is_copy_assignable<Ex>::value &&
- !std::is_abstract<Ex>::value;
- };
-
- template <typename Ex>
- typename std::enable_if<!optimize<Ex>::value>::type
- assign_exception(Ex& e) {
- assign_eptr(e);
+ typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
+ assign_exception(Ex& e, std::exception_ptr eptr) {
+ exception_wrapper::assign_eptr(eptr, e);
}
template <typename Ex>
- typename std::enable_if<optimize<Ex>::value>::type
- assign_exception(Ex& e) {
+ typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
+ assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
this->item_ = std::make_shared<Ex>(e);
this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
}
Base::call_fn(std::move(fn));
} catch (LastException& e) {
if (typeid(e) == typeid(LastException&)) {
- assign_exception(e);
+ assign_exception(e, std::current_exception());
} else {
- assign_eptr(e);
+ exception_wrapper::assign_eptr(std::current_exception(), e);
}
}
}
template<>
class try_and_catch<> : public exception_wrapper {
public:
- try_and_catch() {}
+ try_and_catch() = default;
protected:
template <typename F>