2017
[folly.git] / folly / ExceptionWrapper.h
index 16f1b03639f4b42fc31a9cea988a68f3d02c0723..a6023267a4716f8d4889b814e04b9a689b562646 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 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.
@@ -19,6 +19,7 @@
 #include <exception>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 
@@ -61,8 +62,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
@@ -100,10 +102,11 @@ namespace folly {
  *       }) ||
  *   LOG(FATAL) << "Unrecognized exception";
  * }
+ * \endcode
  *
  */
 class exception_wrapper {
- protected:
+ private:
   template <typename Ex>
   struct optimize;
 
@@ -187,18 +190,7 @@ class exception_wrapper {
 
   template <class Ex>
   bool is_compatible_with() const {
-    if (item_) {
-      return dynamic_cast<const Ex*>(item_.get());
-    } else if (eptr_) {
-      try {
-        std::rethrow_exception(eptr_);
-      } catch (typename std::decay<Ex>::type&) {
-        return true;
-      } catch (...) {
-        // fall through
-      }
-    }
-    return false;
+    return with_exception<Ex>([](const Ex&) {});
   }
 
   template <class F>
@@ -210,46 +202,21 @@ class exception_wrapper {
   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));
+    return with_exception<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>
-  typename std::enable_if<
-    std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
-    bool>::type
-  with_exception(F f) {
+  bool 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>
-  typename std::enable_if<
-    !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
-    bool>::type
-  with_exception(F f) const {
-    try {
-      if (*this) {
-        throwException();
-      }
-    } catch (typename std::decay<Ex>::type& e) {
-      f(e);
-      return true;
-    } catch (...) {
-      // fall through
-    }
-    return false;
+  bool with_exception(F f) const {
+    return with_exception1<typename std::decay<Ex>::type>(f, this);
   }
 
   std::exception_ptr getExceptionPtr() const {
@@ -267,7 +234,7 @@ class exception_wrapper {
     return std::exception_ptr();
   }
 
- protected:
+ private:
   template <typename Ex>
   struct optimize {
     static const bool value =
@@ -332,20 +299,38 @@ class exception_wrapper {
     }
   };
 
+  template <typename T>
+  using is_exception_ = std::is_base_of<std::exception, T>;
+
+  template <bool V, typename T, typename F>
+  using conditional_t_ = typename std::conditional<V, T, F>::type;
+
+  template <typename T, typename F>
+  static typename std::enable_if<is_exception_<T>::value, T*>::type
+  try_dynamic_cast_exception(F* from) {
+    return dynamic_cast<T*>(from);
+  }
+  template <typename T, typename F>
+  static typename std::enable_if<!is_exception_<T>::value, T*>::type
+  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 <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())) {
+    using CEx = conditional_t_<std::is_const<T>::value, const Ex, Ex>;
+    if (is_exception_<Ex>::value && that->item_) {
+      if (auto ex = try_dynamic_cast_exception<CEx>(that->item_.get())) {
         f(*ex);
         return true;
       }
     } else if (that->eptr_) {
       try {
         std::rethrow_exception(that->eptr_);
-      } catch (Ex& e) {
+      } catch (CEx& e) {
         f(e);
         return true;
       } catch (...) {
@@ -363,7 +348,7 @@ exception_wrapper make_exception_wrapper(Args&&... args) {
   return ew;
 }
 
-// For consistency with exceptionStr() functions in String.h
+// For consistency with exceptionStr() functions in ExceptionString.h
 fbstring exceptionStr(const exception_wrapper& ew);
 
 /*
@@ -406,67 +391,43 @@ fbstring exceptionStr(const exception_wrapper& ew);
  * });
  */
 
-namespace detail {
-
-template <typename... Exceptions>
-class try_and_catch;
-
-template <typename LastException, typename... Exceptions>
-class try_and_catch<LastException, Exceptions...> :
-    public try_and_catch<Exceptions...> {
- public:
-  template <typename F>
-  explicit try_and_catch(F&& fn) : Base() {
-    call_fn(fn);
-  }
+namespace try_and_catch_detail {
 
- protected:
-  typedef try_and_catch<Exceptions...> Base;
+template <bool V, typename T = void>
+using enable_if_t_ = typename std::enable_if<V, T>::type;
 
-  try_and_catch() : Base() {}
+template <typename... Args>
+using is_wrap_ctor = std::is_constructible<exception_wrapper, Args...>;
 
-  template <typename Ex>
-  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<exception_wrapper::optimize<Ex>::value>::type
-  assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
-    exception_wrapper::assign_sptr(std::make_shared<Ex>(e));
-  }
+template <typename Ex>
+inline enable_if_t_<!is_wrap_ctor<Ex&>::value, exception_wrapper> make(Ex& ex) {
+  return exception_wrapper(std::current_exception(), ex);
+}
 
-  template <typename F>
-  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 <typename Ex>
+inline enable_if_t_<is_wrap_ctor<Ex&>::value, exception_wrapper> make(Ex& ex) {
+  return typeid(Ex&) == typeid(ex)
+      ? exception_wrapper(ex)
+      : exception_wrapper(std::current_exception(), ex);
+}
 
-template<>
-class try_and_catch<> : public exception_wrapper {
- public:
-  try_and_catch() = default;
+template <typename F>
+inline exception_wrapper impl(F&& f) {
+  return (f(), exception_wrapper());
+}
 
- protected:
-  template <typename F>
-  void call_fn(F&& fn) {
-    fn();
+template <typename F, typename Ex, typename... Exs>
+inline exception_wrapper impl(F&& f) {
+  try {
+    return impl<F, Exs...>(std::forward<F>(f));
+  } catch (Ex& ex) {
+    return make(ex);
   }
-};
 }
+} // try_and_catch_detail
 
 template <typename... Exceptions, typename F>
 exception_wrapper try_and_catch(F&& fn) {
-  return detail::try_and_catch<Exceptions...>(std::forward<F>(fn));
-} // detail
-
+  return try_and_catch_detail::impl<F, Exceptions...>(std::forward<F>(fn));
+}
 } // folly