/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef FOLLY_EXCEPTIONWRAPPER_H
-#define FOLLY_EXCEPTIONWRAPPER_H
+#pragma once
#include <cassert>
#include <exception>
#include <memory>
-#include <folly/String.h>
+#include <folly/ExceptionString.h>
#include <folly/detail/ExceptionWrapper.h>
namespace folly {
*
* // Thread2: Exceptions are bad!
* void processResult() {
- * auto ep = globalExceptionWrapper.get();
- * if (!ep.with_exception<FacePlantException>([&](
- * FacePlantException& faceplant) {
- * LOG(ERROR) << "FACEPLANT";
- * })) {
- * ep.with_exception<FailWhaleException>([&](
- * FailWhaleException& failwhale) {
+ * globalExceptionWrapper.with_exception(
+ * [&](FacePlantException& faceplant) {
+ * LOG(ERROR) << "FACEPLANT";
+ * }) ||
+ * globalExceptionWrapper.with_exception(
+ * [&](FailWhaleException& failwhale) {
* LOG(ERROR) << "FAILWHALE!";
- * });
- * }
+ * }) ||
+ * LOG(FATAL) << "Unrecognized exception";
* }
*
*/
class exception_wrapper {
+ protected:
+ template <typename Ex>
+ struct optimize;
+
public:
- exception_wrapper() : throwfn_(nullptr) { }
-
- // Implicitly construct an exception_wrapper from any std::exception
- template <typename T, typename =
- typename std::enable_if<std::is_base_of<std::exception, T>::value>::type>
- /* implicit */ exception_wrapper(T&& exn) {
- item_ = std::make_shared<T>(std::forward<T>(exn));
- throwfn_ = folly::detail::Thrower<T>::doThrow;
+ 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 {
fbstring class_name() const {
if (item_) {
- return demangle(typeid(*item_));
+ auto& i = *item_;
+ return demangle(typeid(i));
} else if (eptr_) {
return ename_;
} else {
} else if (eptr_) {
try {
std::rethrow_exception(eptr_);
- } catch (std::exception& e) {
- return dynamic_cast<const Ex*>(&e);
+ } catch (typename std::decay<Ex>::type&) {
+ return true;
} catch (...) {
// fall through
}
return false;
}
+ template <class F>
+ bool with_exception(F&& f) {
+ using arg_type = typename functor_traits<F>::arg_type_decayed;
+ return with_exception<arg_type>(std::forward<F>(f));
+ }
+
+ template <class F>
+ bool with_exception(F&& f) const {
+ using arg_type = typename functor_traits<F>::arg_type_decayed;
+ return with_exception<const arg_type>(std::forward<F>(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 <class Ex, class F>
- bool with_exception(F f) {
- return with_exception1<Ex>(f, this);
+ 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>
- bool with_exception(F f) const {
- return with_exception1<const Ex>(f, this);
+ 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>
+ 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 {
}
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 exceptionPtr() the name of the
// exception type, so we can at least get those back out without
friend exception_wrapper make_exception_wrapper(Args&&... args);
private:
+ template <typename F>
+ struct functor_traits {
+ template <typename T>
+ struct impl;
+ template <typename C, typename R, typename A>
+ struct impl<R(C::*)(A)> { using arg_type = A; };
+ template <typename C, typename R, typename A>
+ struct impl<R(C::*)(A) const> { using arg_type = A; };
+ using functor_decayed = typename std::decay<F>::type;
+ using functor_op = decltype(&functor_decayed::operator());
+ using arg_type = typename impl<functor_op>::arg_type;
+ using arg_type_decayed = typename std::decay<arg_type>::type;
+ };
+
// 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.
} 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 (Ex& e) {
+ f(e);
+ return true;
} catch (...) {
// fall through
}
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>
- void assign_eptr(Ex& e) {
- this->eptr_ = std::current_exception();
- this->estr_ = exceptionStr(e).toStdString();
- this->ename_ = demangle(typeid(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>
}
};
}
-#endif