Fix include ordering for OpenSSLPtrTypes.h
[folly.git] / folly / ExceptionWrapper.h
index d14142f0b9ec93d8b8671827748e62feef357b10..bd5bc11972c5555ace1a269d767e8e1459c0a470 100644 (file)
@@ -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.
  * 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/detail/ExceptionWrapper.h>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <folly/ExceptionString.h>
+#include <folly/FBString.h>
+#include <folly/Traits.h>
 
 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,31 +93,70 @@ 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";
  * }
+ * \endcode
  *
  */
 class exception_wrapper {
+ private:
+  template <typename T>
+  using is_exception_ = std::is_base_of<std::exception, T>;
+
  public:
-  exception_wrapper() : throwfn_(nullptr) { }
+  exception_wrapper() = default;
+
+  template <
+      typename Ex,
+      typename DEx = _t<std::decay<Ex>>,
+      typename = _t<std::enable_if<is_exception_<DEx>::value>>,
+      typename = decltype(DEx(std::forward<Ex>(std::declval<Ex&&>())))>
+  /* implicit */ exception_wrapper(Ex&& exn) {
+    assign_sptr<DEx>(std::forward<Ex>(exn));
+  }
 
-  void throwException() const {
-    if (throwfn_) {
-      throwfn_(item_.get());
-    } else if (eptr_) {
-      std::rethrow_exception(eptr_);
-    }
+  // 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);
+  }
+
+  // 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_;
   }
@@ -141,101 +185,150 @@ 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 <class Ex>
+  bool is_compatible_with() const {
+    return with_exception<Ex>([](const Ex&) {});
   }
 
-  fbstring class_name() const {
-    if (item_) {
-      return demangle(typeid(*item_));
-    } else if (eptr_) {
-      return ename_;
-    } else {
-      return fbstring();
-    }
+  template <class F>
+  bool with_exception(F&& f) {
+    using arg_type = _t<std::decay<typename functor_traits<F>::arg_type>>;
+    return with_exception<arg_type>(std::forward<F>(f));
   }
 
-  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 (std::exception& e) {
-        return dynamic_cast<const Ex*>(&e);
-      } catch (...) {
-        // fall through
-      }
-    }
-    return false;
+  template <class F>
+  bool with_exception(F&& f) const {
+    using arg_type = _t<std::decay<typename functor_traits<F>::arg_type>>;
+    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>
   bool with_exception(F f) {
-    return with_exception1<Ex>(f, this);
+    return with_exception1<_t<std::decay<Ex>>>(std::forward<F>(f), this);
   }
 
+  // Const overload
   template <class Ex, class F>
   bool with_exception(F f) const {
-    return with_exception1<const Ex>(f, this);
+    return with_exception1<_t<std::decay<Ex>>>(std::forward<F>(f), this);
   }
 
-  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 <typename Ex, typename... Args>
+  void assign_sptr(Args&&... args) {
+    this->item_ = std::make_shared<Ex>(std::forward<Args>(args)...);
+    this->throwfn_ = Thrower<Ex>::doThrow;
+  }
+
+  template <typename Ex>
+  _t<std::enable_if<is_exception_<Ex>::value>> assign_eptr(
+      std::exception_ptr eptr,
+      Ex& e) {
+    this->eptr_ = eptr;
+    this->eobj_ = &const_cast<_t<std::remove_const<Ex>>&>(e);
+  }
+
+  template <typename Ex>
+  _t<std::enable_if<!is_exception_<Ex>::value>> assign_eptr(
+      std::exception_ptr eptr,
+      Ex& e) {
+    this->eptr_ = eptr;
+    this->etype_ = &typeid(e);
+  }
+
+  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
   // having to rethrow.
   std::exception_ptr eptr_;
-  std::string estr_;
-  std::string ename_;
+  std::exception* eobj_{nullptr};
+  const std::type_info* etype_{nullptr};
 
   template <class T, class... Args>
   friend exception_wrapper make_exception_wrapper(Args&&... args);
 
-private:
+ 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_op = decltype(&_t<std::decay<F>>::operator());
+    using arg_type = typename impl<functor_op>::arg_type;
+  };
+
+  template <class T>
+  class Thrower {
+   public:
+    static void doThrow(std::exception& obj) {
+      throw static_cast<T&>(obj);
+    }
+  };
+
+  template <typename T, typename F>
+  static _t<std::enable_if<is_exception_<T>::value, T*>>
+  try_dynamic_cast_exception(F* from) {
+    return dynamic_cast<T*>(from);
+  }
+  template <typename T, typename F>
+  static _t<std::enable_if<!is_exception_<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 <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 = _t<std::conditional<std::is_const<T>::value, const Ex, Ex>>;
+    if (is_exception_<Ex>::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<CEx>(raw)) {
         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 (CEx& e) {
+        f(e);
+        return true;
       } catch (...) {
         // fall through
       }
@@ -244,14 +337,16 @@ private:
   }
 };
 
-template <class T, class... Args>
+template <class Ex, class... Args>
 exception_wrapper make_exception_wrapper(Args&&... args) {
   exception_wrapper ew;
-  ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
-  ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
+  ew.assign_sptr<Ex>(std::forward<Args>(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
@@ -292,75 +387,42 @@ exception_wrapper make_exception_wrapper(Args&&... args) {
  * });
  */
 
-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);
-  }
-
- protected:
-  typedef try_and_catch<Exceptions...> Base;
+namespace try_and_catch_detail {
 
-  try_and_catch() : Base() {}
+template <typename... Args>
+using is_wrap_ctor = std::is_constructible<exception_wrapper, Args...>;
 
-  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>
+inline _t<std::enable_if<!is_wrap_ctor<Ex&>::value, exception_wrapper>> make(
+    Ex& ex) {
+  return exception_wrapper(std::current_exception(), ex);
+}
 
-  template <typename Ex>
-  typename std::enable_if<!optimize<Ex>::value>::type
-  assign_exception(Ex& e) {
-    assign_eptr(e);
-  }
+template <typename Ex>
+inline _t<std::enable_if<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 <typename Ex>
-  typename std::enable_if<optimize<Ex>::value>::type
-  assign_exception(Ex& e) {
-    this->item_ = std::make_shared<Ex>(e);
-    this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
-  }
+template <typename F>
+inline exception_wrapper impl(F&& f) {
+  return (f(), exception_wrapper());
+}
 
-  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);
-      } else {
-        assign_eptr(e);
-      }
-    }
+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);
   }
-};
-
-template<>
-class try_and_catch<> : public exception_wrapper {
- public:
-  try_and_catch() {}
+}
+} // try_and_catch_detail
 
- protected:
-  template <typename F>
-  void call_fn(F&& fn) {
-    fn();
-  }
-};
+template <typename... Exceptions, typename F>
+exception_wrapper try_and_catch(F&& fn) {
+  return try_and_catch_detail::impl<F, Exceptions...>(std::forward<F>(fn));
 }
-#endif
+} // folly