Keep the std::exception* or std::type_info* in folly::exception_wrapper
authorYedidya Feldblum <yfeldblum@fb.com>
Wed, 4 Jan 2017 21:24:48 +0000 (13:24 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 4 Jan 2017 21:33:09 +0000 (13:33 -0800)
Summary:
[Folly] Keep the `std::exception*` or `std::type_info*` in `folly::exception_wrapper`.

In the case of construction with a `std::exception_ptr`. Instead of keeping a pair of `std::string`s, strictly losing information (not that we use any more information, but theoretically we could).

Of note:
* Shrinks the size of `folly::exception_wrapper`, in all cases, to 48 bytes. Down from 32 bytes + 2 S bytes on 64-bit architectures, where S is the size in bytes of `std::string`. In particular, `libstdc++`'s implementation is 32 bytes, while `libc++`'s and Folly's implementations are 24 bytes on 64-bit architectures, for total original sizes of 96 bytes or 80 bytes.
* Allows to avoid rethrowing in `with_exception` in the case of an instance constructed with an `std::exception_ptr` plus an `std::exception&`.

Reviewed By: ericniebler

Differential Revision: D4369935

fbshipit-source-id: 35155e0b271959a4878fe077fc911b17767a2358

folly/ExceptionWrapper.cpp
folly/ExceptionWrapper.h

index 3a5c862..d819c60 100644 (file)
@@ -36,8 +36,10 @@ fbstring exception_wrapper::class_name() const {
   if (item_) {
     auto& i = *item_;
     return demangle(typeid(i));
-  } else if (eptr_) {
-    return ename_;
+  } else if (eptr_ && eobj_) {
+    return demangle(typeid(*eobj_));
+  } else if (eptr_ && etype_) {
+    return demangle(*etype_);
   } else {
     return fbstring();
   }
@@ -46,10 +48,12 @@ fbstring exception_wrapper::class_name() const {
 fbstring exception_wrapper::what() const {
   if (item_) {
     return exceptionStr(*item_);
-  } else if (eptr_) {
-    return estr_;
+  } else if (eptr_ && eobj_) {
+    return class_name() + ": " + eobj_->what();
+  } else if (eptr_ && etype_) {
+    return class_name();
   } else {
-    return fbstring();
+    return class_name();
   }
 }
 
index b573f23..6bb39cc 100644 (file)
@@ -108,6 +108,9 @@ namespace folly {
  */
 class exception_wrapper {
  private:
+  template <typename T>
+  using is_exception_ = std::is_base_of<std::exception, T>;
+
   template <typename Ex>
   struct optimize;
 
@@ -250,10 +253,19 @@ class exception_wrapper {
   }
 
   template <typename Ex>
-  void assign_eptr(std::exception_ptr eptr, Ex& e) {
+  _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->estr_ = exceptionStr(e).toStdString();
-    this->ename_ = demangle(typeid(e)).toStdString();
+    this->etype_ = &typeid(e);
   }
 
   void assign_eptr(std::exception_ptr eptr) {
@@ -270,8 +282,8 @@ class exception_wrapper {
   // 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);
@@ -297,9 +309,6 @@ class exception_wrapper {
     }
   };
 
-  template <typename T>
-  using is_exception_ = std::is_base_of<std::exception, T>;
-
   template <typename T, typename F>
   static _t<std::enable_if<is_exception_<T>::value, T*>>
   try_dynamic_cast_exception(F* from) {
@@ -317,8 +326,11 @@ class exception_wrapper {
   template <class Ex, class F, class T>
   static bool with_exception1(F f, T* that) {
     using CEx = _t<std::conditional<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())) {
+    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;
       }