+/*
+ * Throwing exceptions can be a convenient way to handle errors. Storing
+ * exceptions in an exception_ptr makes it easy to handle exceptions in a
+ * different thread or at a later time. exception_ptr can also be used in a very
+ * generic result/exception wrapper.
+ *
+ * However, there are some issues with throwing exceptions and
+ * std::exception_ptr. These issues revolve around throw being expensive,
+ * particularly in a multithreaded environment (see
+ * ExceptionWrapperBenchmark.cpp).
+ *
+ * Imagine we have a library that has an API which returns a result/exception
+ * wrapper. Let's consider some approaches for implementing this wrapper.
+ * First, we could store a std::exception. This approach loses the derived
+ * exception type, which can make exception handling more difficult for users
+ * that prefer rethrowing the exception. We could use a folly::dynamic for every
+ * possible type of exception. This is not very flexible - adding new types of
+ * exceptions requires a change to the result/exception wrapper. We could use an
+ * exception_ptr. However, constructing an exception_ptr as well as accessing
+ * the error requires a call to throw. That means that there will be two calls
+ * to throw in order to process the exception. For performance sensitive
+ * applications, this may be unacceptable.
+ *
+ * exception_wrapper is designed to handle exception management for both
+ * convenience and high performance use cases. make_exception_wrapper is
+ * templated on derived type, allowing us to rethrow the exception properly for
+ * users that prefer convenience. These explicitly named exception types can
+ * therefore be handled without any peformance penalty. exception_wrapper is
+ * also flexible enough to accept any type. If a caught exception is not of an
+ * explicitly named type, then std::exception_ptr is used to preserve the
+ * exception state. For performance sensitive applications, the accessor methods
+ * can test or extract a pointer to a specific exception type with very little
+ * overhead.
+ *
+ * Example usage:
+ *
+ * exception_wrapper globalExceptionWrapper;
+ *
+ * // Thread1
+ * void doSomethingCrazy() {
+ * int rc = doSomethingCrazyWithLameReturnCodes();
+ * if (rc == NAILED_IT) {
+ * globalExceptionWrapper = exception_wrapper();
+ * } else if (rc == FACE_PLANT) {
+ * globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
+ * } else if (rc == FAIL_WHALE) {
+ * globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
+ * }
+ * }
+ *
+ * // Thread2: Exceptions are ok!
+ * void processResult() {
+ * try {
+ * globalExceptionWrapper.throwException();
+ * } catch (const FacePlantException& e) {
+ * LOG(ERROR) << "FACEPLANT!";
+ * } catch (const FailWhaleException& e) {
+ * LOG(ERROR) << "FAILWHALE!";
+ * }
+ * }
+ *
+ * // Thread2: Exceptions are bad!
+ * void processResult() {
+ * globalExceptionWrapper.with_exception(
+ * [&](FacePlantException& faceplant) {
+ * LOG(ERROR) << "FACEPLANT";
+ * }) ||
+ * globalExceptionWrapper.with_exception(
+ * [&](FailWhaleException& failwhale) {
+ * LOG(ERROR) << "FAILWHALE!";
+ * }) ||
+ * LOG(FATAL) << "Unrecognized exception";
+ * }
+ *
+ */