2 * Copyright 2014 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_EXCEPTIONWRAPPER_H
18 #define FOLLY_EXCEPTIONWRAPPER_H
23 #include <folly/detail/ExceptionWrapper.h>
28 * Throwing exceptions can be a convenient way to handle errors. Storing
29 * exceptions in an exception_ptr makes it easy to handle exceptions in a
30 * different thread or at a later time. exception_ptr can also be used in a very
31 * generic result/exception wrapper.
33 * However, there are some issues with throwing exceptions and
34 * std::exception_ptr. These issues revolve around throw being expensive,
35 * particularly in a multithreaded environment (see
36 * ExceptionWrapperBenchmark.cpp).
38 * Imagine we have a library that has an API which returns a result/exception
39 * wrapper. Let's consider some approaches for implementing this wrapper.
40 * First, we could store a std::exception. This approach loses the derived
41 * exception type, which can make exception handling more difficult for users
42 * that prefer rethrowing the exception. We could use a folly::dynamic for every
43 * possible type of exception. This is not very flexible - adding new types of
44 * exceptions requires a change to the result/exception wrapper. We could use an
45 * exception_ptr. However, constructing an exception_ptr as well as accessing
46 * the error requires a call to throw. That means that there will be two calls
47 * to throw in order to process the exception. For performance sensitive
48 * applications, this may be unacceptable.
50 * exception_wrapper is designed to handle exception management for both
51 * convenience and high performance use cases. make_exception_wrapper is
52 * templated on derived type, allowing us to rethrow the exception properly for
53 * users that prefer convenience. exception_wrapper is flexible enough to accept
54 * any std::exception. For performance sensitive applications, exception_wrapper
55 * exposes a get() function. These users can use dynamic_cast to retrieve
56 * desired derived types (hence the decision to limit usage to just
57 * std::exception instead of void*).
61 * exception_wrapper globalExceptionWrapper;
64 * void doSomethingCrazy() {
65 * int rc = doSomethingCrazyWithLameReturnCodes();
66 * if (rc == NAILED_IT) {
67 * globalExceptionWrapper = exception_wrapper();
68 * } else if (rc == FACE_PLANT) {
69 * globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
70 * } else if (rc == FAIL_WHALE) {
71 * globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
75 * // Thread2: Exceptions are ok!
76 * void processResult() {
78 * globalExceptionWrapper.throwException();
79 * } catch (const FacePlantException& e) {
80 * LOG(ERROR) << "FACEPLANT!";
81 * } catch (const FailWhaleException& e) {
82 * LOG(ERROR) << "FAILWHALE!";
86 * // Thread2: Exceptions are bad!
87 * void processResult() {
88 * auto ep = globalExceptionWrapper.get();
90 * auto faceplant = dynamic_cast<FacePlantException*>(ep);
92 * LOG(ERROR) << "FACEPLANT";
94 * auto failwhale = dynamic_cast<FailWhaleException*>(ep);
96 * LOG(ERROR) << "FAILWHALE!";
103 class exception_wrapper {
105 exception_wrapper() : throwfn_(nullptr) { }
107 void throwException() const {
109 throwfn_(item_.get());
113 std::exception* get() { return item_.get(); }
114 const std::exception* get() const { return item_.get(); }
116 std::exception* operator->() { return get(); }
117 const std::exception* operator->() const { return get(); }
119 std::exception& operator*() { assert(get()); return *get(); }
120 const std::exception& operator*() const { assert(get()); return *get(); }
122 explicit operator bool() const { return get(); }
124 std::exception_ptr getExceptionPtr() const {
128 return std::current_exception();
130 return std::exception_ptr();
134 std::shared_ptr<std::exception> item_;
135 void (*throwfn_)(std::exception*);
137 template <class T, class... Args>
138 friend exception_wrapper make_exception_wrapper(Args&&... args);
141 template <class T, class... Args>
142 exception_wrapper make_exception_wrapper(Args&&... args) {
143 exception_wrapper ew;
144 ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
145 ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
150 * try_and_catch is a simple replacement for try {} catch(){} that allows you to
151 * specify which derived exceptions you would like to catch and store in an
154 * Because we cannot build an equivalent of std::current_exception(), we need
155 * to catch every derived exception that we are interested in catching.
157 * Exceptions should be listed in the reverse order that you would write your
158 * catch statements (that is, std::exception& should be first).
160 * NOTE: Although implemented as a derived class (for syntactic delight), don't
161 * be confused - you should not pass around try_and_catch objects!
165 * // This catches my runtime_error and if I call throwException() on ew, it
166 * // will throw a runtime_error
167 * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
168 * if (badThingHappens()) {
169 * throw std::runtime_error("ZOMG!");
173 * // This will catch the exception and if I call throwException() on ew, it
174 * // will throw a std::exception
175 * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
176 * if (badThingHappens()) {
177 * throw std::exception();
181 * // This will not catch the exception and it will be thrown.
182 * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
183 * if (badThingHappens()) {
184 * throw std::exception();
189 template <typename... Exceptions>
192 template <typename LastException, typename... Exceptions>
193 class try_and_catch<LastException, Exceptions...> :
194 public try_and_catch<Exceptions...> {
196 template <typename F>
197 explicit try_and_catch(F&& fn) : Base() {
202 typedef try_and_catch<Exceptions...> Base;
204 try_and_catch() : Base() {}
206 template <typename F>
207 void call_fn(F&& fn) {
209 Base::call_fn(std::move(fn));
210 } catch (const LastException& e) {
211 this->item_ = std::make_shared<LastException>(e);
212 this->throwfn_ = folly::detail::Thrower<LastException>::doThrow;
218 class try_and_catch<> : public exception_wrapper {
223 template <typename F>
224 void call_fn(F&& fn) {