proxygen
ExceptionWrapper.h
Go to the documentation of this file.
1 /*
2  * Copyright 2014-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  * Author: Eric Niebler <eniebler@fb.com>
18  */
19 
20 #pragma once
21 
22 #include <cassert>
23 #include <cstdint>
24 #include <exception>
25 #include <iosfwd>
26 #include <memory>
27 #include <new>
28 #include <type_traits>
29 #include <typeinfo>
30 #include <utility>
31 
32 #include <folly/CPortability.h>
33 #include <folly/Demangle.h>
34 #include <folly/ExceptionString.h>
35 #include <folly/FBString.h>
36 #include <folly/Portability.h>
37 #include <folly/Traits.h>
38 #include <folly/Utility.h>
39 #include <folly/lang/Assume.h>
40 
41 #ifdef __GNUC__
42 #pragma GCC diagnostic push
43 #pragma GCC diagnostic ignored "-Wpragmas"
44 #pragma GCC diagnostic ignored "-Wpotentially-evaluated-expression"
45 // GCC gets confused about lambda scopes and issues shadow-local warnings for
46 // parameters in totally different functions.
48 #endif
49 
50 #define FOLLY_EXCEPTION_WRAPPER_H_INCLUDED
51 
52 namespace folly {
53 
54 #define FOLLY_REQUIRES_DEF(...) \
55  _t<std::enable_if<static_cast<bool>(__VA_ARGS__), long>>
56 
57 #define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__
58 
59 namespace exception_wrapper_detail {
60 
61 template <template <class> class T, class... As>
63 
64 template <bool If, class T>
66 
67 template <class Fn, class A>
69  return static_cast<A&&>(a);
70 }
71 
72 template <class Fn, class A, class B, class... Bs>
74 fold(Fn&& fn, A&& a, B&& b, Bs&&... bs) {
75  return fold(
76  // This looks like a use of fn after a move of fn, but in reality, this is
77  // just a cast and not a move. That's because regardless of which fold
78  // overload is selected, fn gets bound to a &&. Had fold taken fn by value
79  // there would indeed be a problem here.
80  static_cast<Fn&&>(fn),
81  static_cast<Fn&&>(fn)(static_cast<A&&>(a), static_cast<B&&>(b)),
82  static_cast<Bs&&>(bs)...);
83 }
84 
85 } // namespace exception_wrapper_detail
86 
162 class exception_wrapper final {
163  private:
164  struct FOLLY_EXPORT AnyException : std::exception {
165  std::type_info const* typeinfo_;
166  template <class T>
167  /* implicit */ AnyException(T&& t) noexcept : typeinfo_(&typeid(t)) {}
168  };
169 
170  template <class Fn>
171  struct arg_type_;
172  template <class Fn>
174 
175  // exception_wrapper is implemented as a simple variant over four
176  // different representations:
177  // 0. Empty, no exception.
178  // 1. An small object stored in-situ.
179  // 2. A larger object stored on the heap and referenced with a
180  // std::shared_ptr.
181  // 3. A std::exception_ptr, together with either:
182  // a. A pointer to the referenced std::exception object, or
183  // b. A pointer to a std::type_info object for the referenced exception,
184  // or for an unspecified type if the type is unknown.
185  // This is accomplished with the help of a union and a pointer to a hand-
186  // rolled virtual table. This virtual table contains pointers to functions
187  // that know which field of the union is active and do the proper action.
188  // The class invariant ensures that the vtable ptr and the union stay in sync.
189  struct VTable {
190  void (*copy_)(exception_wrapper const*, exception_wrapper*);
192  void (*delete_)(exception_wrapper*);
193  void (*throw_)(exception_wrapper const*);
194  std::type_info const* (*type_)(exception_wrapper const*);
195  std::exception const* (*get_exception_)(exception_wrapper const*);
196  exception_wrapper (*get_exception_ptr_)(exception_wrapper const*);
197  };
198 
199  [[noreturn]] static void onNoExceptionError(char const* name);
200 
201  template <class Ret, class... Args>
202  static Ret noop_(Args...);
203 
204  static std::type_info const* uninit_type_(exception_wrapper const*);
205 
206  static VTable const uninit_;
207 
208  template <class Ex>
209  using IsStdException = std::is_base_of<std::exception, _t<std::decay<Ex>>>;
210  template <bool B, class T>
212  template <class CatchFn>
213  using IsCatchAll =
214  std::is_same<arg_type<_t<std::decay<CatchFn>>>, AnyException>;
215 
216  struct Unknown {};
217 
218  // Sadly, with the gcc-4.9 platform, std::logic_error and std::runtime_error
219  // do not fit here. They also don't have noexcept copy-ctors, so the internal
220  // storage wouldn't be used anyway. For the gcc-5 platform, both logic_error
221  // and runtime_error can be safely stored internally.
222  struct Buffer {
223  using Storage =
226 
227  Buffer() : buff_{} {}
228 
229  template <class Ex, typename... As>
230  Buffer(in_place_type_t<Ex>, As&&... as_);
231  template <class Ex>
232  Ex& as() noexcept;
233  template <class Ex>
234  Ex const& as() const noexcept;
235  };
236 
237  struct ThrownTag {};
238  struct InSituTag {};
239  struct OnHeapTag {};
240 
241  template <class T>
242  using PlacementOf = _t<std::conditional<
244  ThrownTag,
245  _t<std::conditional<
246  sizeof(T) <= sizeof(Buffer::Storage) &&
247  alignof(T) <= alignof(Buffer::Storage) &&
248  noexcept(T(std::declval<T&&>())) &&
249  noexcept(T(std::declval<T const&>())),
250  InSituTag,
252 
253  static std::exception const* as_exception_or_null_(std::exception const& ex);
254  static std::exception const* as_exception_or_null_(AnyException);
255 
256  struct ExceptionPtr {
257  std::exception_ptr ptr_;
258  std::uintptr_t exception_or_type_; // odd for type_info
259  static_assert(
260  1 < alignof(std::exception) && 1 < alignof(std::type_info),
261  "Surprise! std::exception and std::type_info don't have alignment "
262  "greater than one. as_int_ below will not work!");
263 
264  static std::uintptr_t as_int_(
265  std::exception_ptr const& ptr,
266  std::exception const& e) noexcept;
267  static std::uintptr_t as_int_(
268  std::exception_ptr const& ptr,
269  AnyException e) noexcept;
270  bool has_exception_() const;
271  std::exception const* as_exception_() const;
272  std::type_info const* as_type_() const;
273  static void copy_(exception_wrapper const* from, exception_wrapper* to);
274  static void move_(exception_wrapper* from, exception_wrapper* to);
275  static void delete_(exception_wrapper* that);
276  [[noreturn]] static void throw_(exception_wrapper const* that);
277  static std::type_info const* type_(exception_wrapper const* that);
278  static std::exception const* get_exception_(exception_wrapper const* that);
279  static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
280  static VTable const ops_;
281  };
282 
283  template <class Ex>
284  struct InPlace {
285  static_assert(IsStdException<Ex>::value, "only deriving std::exception");
286  static void copy_(exception_wrapper const* from, exception_wrapper* to);
287  static void move_(exception_wrapper* from, exception_wrapper* to);
288  static void delete_(exception_wrapper* that);
289  [[noreturn]] static void throw_(exception_wrapper const* that);
290  static std::type_info const* type_(exception_wrapper const*);
291  static std::exception const* get_exception_(exception_wrapper const* that);
292  static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
293  static constexpr VTable const ops_{copy_,
294  move_,
295  delete_,
296  throw_,
297  type_,
298  get_exception_,
299  get_exception_ptr_};
300  };
301 
302  struct SharedPtr {
303  struct Base {
304  std::type_info const* info_;
305  Base() = default;
306  explicit Base(std::type_info const& info) : info_(&info) {}
307  virtual ~Base() {}
308  virtual void throw_() const = 0;
309  virtual std::exception const* get_exception_() const noexcept = 0;
310  virtual exception_wrapper get_exception_ptr_() const noexcept = 0;
311  };
312  template <class Ex>
313  struct Impl final : public Base {
314  static_assert(IsStdException<Ex>::value, "only deriving std::exception");
315  Ex ex_;
316  Impl() = default;
317  // clang-format off
318  template <typename... As>
319  explicit Impl(As&&... as)
320  : Base{typeid(Ex)}, ex_(std::forward<As>(as)...) {}
321  [[noreturn]] void throw_() const override;
322  // clang-format on
323  std::exception const* get_exception_() const noexcept override;
324  exception_wrapper get_exception_ptr_() const noexcept override;
325  };
326  std::shared_ptr<Base> ptr_;
327 
328  static void copy_(exception_wrapper const* from, exception_wrapper* to);
329  static void move_(exception_wrapper* from, exception_wrapper* to);
330  static void delete_(exception_wrapper* that);
331  [[noreturn]] static void throw_(exception_wrapper const* that);
332  static std::type_info const* type_(exception_wrapper const* that);
333  static std::exception const* get_exception_(exception_wrapper const* that);
334  static exception_wrapper get_exception_ptr_(exception_wrapper const* that);
335  static VTable const ops_;
336  };
337 
338  union {
339  Buffer buff_{};
342  };
343  VTable const* vptr_{&uninit_};
344 
345  template <class Ex, typename... As>
346  exception_wrapper(ThrownTag, in_place_type_t<Ex>, As&&... as);
347 
348  template <class Ex, typename... As>
349  exception_wrapper(OnHeapTag, in_place_type_t<Ex>, As&&... as);
350 
351  template <class Ex, typename... As>
352  exception_wrapper(InSituTag, in_place_type_t<Ex>, As&&... as);
353 
354  template <class T>
357  std::is_copy_constructible<T>,
358  Negation<std::is_base_of<exception_wrapper, T>>,
359  Negation<std::is_abstract<T>>> {};
360 
361  template <class CatchFn, bool IsConst = false>
362  struct ExceptionTypeOf;
363 
364  template <bool IsConst>
365  struct HandleReduce;
366 
367  template <bool IsConst>
368  struct HandleStdExceptReduce;
369 
370  template <class This, class... CatchFns>
371  static void handle_(std::false_type, This& this_, CatchFns&... fns);
372 
373  template <class This, class... CatchFns>
374  static void handle_(std::true_type, This& this_, CatchFns&... fns);
375 
376  template <class Ex, class This, class Fn>
377  static bool with_exception_(This& this_, Fn fn_);
378 
379  public:
380  static exception_wrapper from_exception_ptr(
381  std::exception_ptr const& eptr) noexcept;
382 
385  exception_wrapper() noexcept {}
386 
390  exception_wrapper(exception_wrapper&& that) noexcept;
391 
395  exception_wrapper(exception_wrapper const& that) noexcept;
396 
401  exception_wrapper& operator=(exception_wrapper&& that) noexcept;
402 
406  exception_wrapper& operator=(exception_wrapper const& that) noexcept;
407 
408  ~exception_wrapper();
409 
415  explicit exception_wrapper(std::exception_ptr ptr) noexcept;
416 
421  template <class Ex>
422  exception_wrapper(std::exception_ptr ptr, Ex& ex) noexcept;
423 
430  template <
431  class Ex,
432  class Ex_ = _t<std::decay<Ex>>,
435  /* implicit */ exception_wrapper(Ex&& ex);
436 
444  template <
445  class Ex,
446  class Ex_ = _t<std::decay<Ex>>,
448  exception_wrapper(in_place_t, Ex&& ex);
449 
450  template <
451  class Ex,
452  typename... As,
454  exception_wrapper(in_place_type_t<Ex>, As&&... as);
455 
457  void swap(exception_wrapper& that) noexcept;
458 
460  explicit operator bool() const noexcept;
461 
463  bool operator!() const noexcept;
464 
467  void reset();
468 
473  bool has_exception_ptr() const noexcept;
474 
479  std::exception* get_exception() noexcept;
481  std::exception const* get_exception() const noexcept;
482 
489  template <typename Ex>
490  Ex* get_exception() noexcept;
492  template <typename Ex>
493  Ex const* get_exception() const noexcept;
494 
501  std::exception_ptr const& to_exception_ptr() noexcept;
503  std::exception_ptr to_exception_ptr() const noexcept;
504 
507  static std::type_info const& none() noexcept;
511  static std::type_info const& unknown() noexcept;
512 
517  std::type_info const& type() const noexcept;
518 
521  folly::fbstring what() const;
522 
526  folly::fbstring class_name() const;
527 
532  template <class Ex>
533  bool is_compatible_with() const noexcept;
534 
537  [[noreturn]] void throw_exception() const;
538 
542  // see std::throw_with_nested() for details on this semantic.
543  template <class Ex>
544  [[noreturn]] void throw_with_nested(Ex&& ex) const;
545 
572  template <class Ex = void const, class Fn>
573  bool with_exception(Fn fn);
575  template <class Ex = void const, class Fn>
576  bool with_exception(Fn fn) const;
577 
611  template <class... CatchFns>
612  void handle(CatchFns... fns);
614  template <class... CatchFns>
615  void handle(CatchFns... fns) const;
616 };
617 
618 template <class Ex>
620 
625 template <class Ex, typename... As>
627  return exception_wrapper{in_place_type<Ex>, std::forward<As>(as)...};
628 }
629 
634 template <class Ch>
635 std::basic_ostream<Ch>& operator<<(
636  std::basic_ostream<Ch>& sout,
637  exception_wrapper const& ew) {
638  return sout << ew.what();
639 }
640 
645  a.swap(b);
646 }
647 
648 // For consistency with exceptionStr() functions in ExceptionString.h
650 
651 namespace detail {
652 template <typename F>
654  return (f(), exception_wrapper());
655 }
656 
657 template <typename F, typename Ex, typename... Exs>
659  try {
660  return try_and_catch_<F, Exs...>(std::forward<F>(f));
661  } catch (Ex& ex) {
662  return exception_wrapper(std::current_exception(), ex);
663  }
664 }
665 } // namespace detail
666 
702 template <typename... Exceptions, typename F>
704  return detail::try_and_catch_<F, Exceptions...>(std::forward<F>(fn));
705 }
706 } // namespace folly
707 
709 
710 #undef FOLLY_REQUIRES
711 #undef FOLLY_REQUIRES_DEF
712 #ifdef __GNUC__
713 #pragma GCC diagnostic pop
714 #endif
void * ptr
TransportDirection operator!(TransportDirection dir)
folly::fbstring what() const
def info()
Definition: deadlock.py:447
_t< arg_type_< Fn >> arg_type
auto f
std::unique_ptr< int > A
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
char b
PskType type
_t< std::aligned_storage< 2 *sizeof(void *), alignof(std::exception)>> Storage
#define FOLLY_REQUIRES(...)
fbstring exceptionStr(const std::exception &e)
std::is_base_of< std::exception, _t< std::decay< Ex >>> IsStdException
folly::std T
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define FOLLY_EXPORT
Definition: CPortability.h:133
requires E e noexcept(noexcept(s.error(std::move(e))))
in_place_tag(&)(in_place_tag) in_place_t
Definition: Utility.h:229
exception_wrapper_detail::AddConstIf< B, T > AddConstIf
bool_constant< true > true_type
Definition: gtest-port.h:2210
_t< std::conditional< !IsStdException< T >::value, ThrownTag, _t< std::conditional< sizeof(T)<=sizeof(Buffer::Storage)&&alignof(T)<=alignof(Buffer::Storage)&&noexcept(T(std::declval< T && >()))&&noexcept(T(std::declval< T const & >())), InSituTag, OnHeapTag >>>> PlacementOf
const char * name
Definition: http_parser.c:437
exception_wrapper try_and_catch_(F &&f)
std::enable_if< detail::is_chrono_conversion< Tgt, Src >::value, Tgt >::type to(const Src &value)
Definition: Conv.h:677
Type type_
Definition: JSONSchema.cpp:208
typename T::type _t
Definition: Traits.h:171
AtomicCounter< T, DeterministicAtomic > Base
char a
PUSHMI_INLINE_VAR constexpr struct folly::pushmi::operators::from_fn from
exception_wrapper try_and_catch(F &&fn)
exception_wrapper make_exception_wrapper(As &&...as)
#define FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS
Definition: Portability.h:192
void swap(exception_wrapper &a, exception_wrapper &b) noexcept
static VTable const uninit_
std::is_same< arg_type< _t< std::decay< CatchFn >>>, AnyException > IsCatchAll
exception_wrapper try_and_catch_(F &&f)
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto fold(Fn &&, A &&a)
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
bool_constant< false > false_type
Definition: gtest-port.h:2209
Collect as()
Definition: Base.h:811
#define FOLLY_ATTR_VISIBILITY_HIDDEN
Definition: CPortability.h:160
_t< std::conditional< If, const T, T >> AddConstIf
static E * get_exception(std::exception_ptr eptr)
Definition: TryTest.cpp:541
constexpr None none
Definition: Optional.h:87
FOLLY_NOINLINE FOLLY_COLD void throw_exception(Ex &&ex)
Definition: Exception.h:32
#define B(name, bit)
Definition: CpuId.h:178
std::ostream & operator<<(std::ostream &out, dynamic const &d)
Definition: dynamic-inl.h:1158