1 | /* |
2 | * Copyright 2018-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 | #pragma once |
18 | |
19 | #include <exception> |
20 | #include <type_traits> |
21 | #include <utility> |
22 | |
23 | #include <folly/CPortability.h> |
24 | #include <folly/CppAttributes.h> |
25 | #include <folly/Portability.h> |
26 | |
27 | namespace folly { |
28 | |
29 | /// throw_exception |
30 | /// |
31 | /// Throw an exception if exceptions are enabled, or terminate if compiled with |
32 | /// -fno-exceptions. |
33 | template <typename Ex> |
34 | [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception(Ex&& ex) { |
35 | #if FOLLY_HAS_EXCEPTIONS |
36 | throw static_cast<Ex&&>(ex); |
37 | #else |
38 | (void)ex; |
39 | std::terminate(); |
40 | #endif |
41 | } |
42 | |
43 | /// terminate_with |
44 | /// |
45 | /// Terminates as if by forwarding to throw_exception but in a noexcept context. |
46 | template <typename Ex> |
47 | [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with(Ex&& ex) noexcept { |
48 | throw_exception(static_cast<Ex&&>(ex)); |
49 | } |
50 | |
51 | // clang-format off |
52 | namespace detail { |
53 | template <typename T> |
54 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T&& to_exception_arg_(T&& t) { |
55 | return static_cast<T&&>(t); |
56 | } |
57 | template <std::size_t N> |
58 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN char const* to_exception_arg_( |
59 | char const (&array)[N]) { |
60 | return static_cast<char const*>(array); |
61 | } |
62 | template <typename Ex, typename... Args> |
63 | [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args&&... args) { |
64 | throw_exception(Ex(static_cast<Args&&>(args)...)); |
65 | } |
66 | template <typename Ex, typename... Args> |
67 | [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with_( |
68 | Args&&... args) noexcept { |
69 | throw_exception(Ex(static_cast<Args&&>(args)...)); |
70 | } |
71 | } // namespace detail |
72 | // clang-format on |
73 | |
74 | /// throw_exception |
75 | /// |
76 | /// Construct and throw an exception if exceptions are enabled, or terminate if |
77 | /// compiled with -fno-exceptions. |
78 | /// |
79 | /// Converts any arguments of type `char const[N]` to `char const*`. |
80 | template <typename Ex, typename... Args> |
81 | [[noreturn]] FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN void |
82 | throw_exception(Args&&... args) { |
83 | detail::throw_exception_<Ex>( |
84 | detail::to_exception_arg_(static_cast<Args&&>(args))...); |
85 | } |
86 | |
87 | /// terminate_with |
88 | /// |
89 | /// Terminates as if by forwarding to throw_exception but in a noexcept context. |
90 | // clang-format off |
91 | template <typename Ex, typename... Args> |
92 | [[noreturn]] FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN void |
93 | terminate_with(Args&&... args) noexcept { |
94 | detail::terminate_with_<Ex>( |
95 | detail::to_exception_arg_(static_cast<Args&&>(args))...); |
96 | } |
97 | // clang-format on |
98 | |
99 | /// invoke_cold |
100 | /// |
101 | /// Invoke the provided function with the provided arguments. |
102 | /// |
103 | /// Usage note: |
104 | /// Passing extra values as arguments rather than capturing them allows smaller |
105 | /// inlined native at the call-site. |
106 | /// |
107 | /// Example: |
108 | /// |
109 | /// if (i < 0) { |
110 | /// invoke_cold( |
111 | /// [](int j) { |
112 | /// std::string ret = doStepA(); |
113 | /// doStepB(ret); |
114 | /// doStepC(ret); |
115 | /// }, |
116 | /// i); |
117 | /// } |
118 | template <typename F, typename... A> |
119 | FOLLY_NOINLINE FOLLY_COLD auto invoke_cold(F&& f, A&&... a) |
120 | -> decltype(static_cast<F&&>(f)(static_cast<A&&>(a)...)) { |
121 | return static_cast<F&&>(f)(static_cast<A&&>(a)...); |
122 | } |
123 | |
124 | /// invoke_noreturn_cold |
125 | /// |
126 | /// Invoke the provided function with the provided arguments. If the invocation |
127 | /// returns, terminate. |
128 | /// |
129 | /// May be used with throw_exception in cases where construction of the object |
130 | /// to be thrown requires more than just invoking its constructor with a given |
131 | /// sequence of arguments passed by reference - for example, if a string message |
132 | /// must be computed before being passed to the constructor of the object to be |
133 | /// thrown. |
134 | /// |
135 | /// Usage note: |
136 | /// Passing extra values as arguments rather than capturing them allows smaller |
137 | /// inlined native code at the call-site. |
138 | /// |
139 | /// Example: |
140 | /// |
141 | /// if (i < 0) { |
142 | /// invoke_noreturn_cold( |
143 | /// [](int j) { |
144 | /// throw_exceptions(runtime_error(to<string>("invalid: ", j))); |
145 | /// }, |
146 | /// i); |
147 | /// } |
148 | template <typename F, typename... A> |
149 | [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void invoke_noreturn_cold( |
150 | F&& f, |
151 | A&&... a) { |
152 | static_cast<F&&>(f)(static_cast<A&&>(a)...); |
153 | std::terminate(); |
154 | } |
155 | |
156 | /// catch_exception |
157 | /// |
158 | /// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), |
159 | /// catches a thrown exception e of type E and invokes c, forwarding e and any |
160 | /// trailing arguments. |
161 | /// |
162 | /// Usage note: |
163 | /// As a general rule, pass Ex const& rather than unqualified Ex as the explicit |
164 | /// template argument E. The catch statement catches E without qualifiers so |
165 | /// if E is Ex then that translates to catch (Ex), but if E is Ex const& then |
166 | /// that translates to catch (Ex const&). |
167 | /// |
168 | /// Usage note: |
169 | /// Passing extra values as arguments rather than capturing them allows smaller |
170 | /// inlined native code at the call-site. |
171 | /// |
172 | /// Example: |
173 | /// |
174 | /// int input = // ... |
175 | /// int def = 45; |
176 | /// auto result = catch_exception<std::runtime_error const&>( |
177 | /// [=] { |
178 | /// if (input < 0) throw std::runtime_error("foo"); |
179 | /// return input; |
180 | /// }, |
181 | /// [](auto&& e, int num) { return num; }, |
182 | /// def); |
183 | /// assert(result == input < 0 ? def : input); |
184 | template <typename E, typename Try, typename Catch, typename... CatchA> |
185 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto |
186 | catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> typename std::common_type< |
187 | decltype(static_cast<Try&&>(t)()), |
188 | decltype(static_cast<Catch&&>( |
189 | c)(std::declval<E>(), static_cast<CatchA&&>(a)...))>::type { |
190 | #if FOLLY_HAS_EXCEPTIONS |
191 | try { |
192 | return static_cast<Try&&>(t)(); |
193 | } catch (E e) { |
194 | return invoke_cold(static_cast<Catch&&>(c), e, static_cast<CatchA&&>(a)...); |
195 | } |
196 | #else |
197 | [](auto&&...) {}(c, a...); // ignore |
198 | return static_cast<Try&&>(t)(); |
199 | #endif |
200 | } |
201 | |
202 | /// catch_exception |
203 | /// |
204 | /// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), |
205 | /// catches a thrown exception of any type and invokes c, forwarding any |
206 | /// trailing arguments. |
207 | // |
208 | /// Usage note: |
209 | /// Passing extra values as arguments rather than capturing them allows smaller |
210 | /// inlined native code at the call-site. |
211 | /// |
212 | /// Example: |
213 | /// |
214 | /// int input = // ... |
215 | /// int def = 45; |
216 | /// auto result = catch_exception( |
217 | /// [=] { |
218 | /// if (input < 0) throw 11; |
219 | /// return input; |
220 | /// }, |
221 | /// [](int num) { return num; }, |
222 | /// def); |
223 | /// assert(result == input < 0 ? def : input); |
224 | template <typename Try, typename Catch, typename... CatchA> |
225 | FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN auto |
226 | catch_exception(Try&& t, Catch&& c, CatchA&&... a) -> typename std::common_type< |
227 | decltype(static_cast<Try&&>(t)()), |
228 | decltype(static_cast<Catch&&>(c)(static_cast<CatchA&&>(a)...))>::type { |
229 | #if FOLLY_HAS_EXCEPTIONS |
230 | try { |
231 | return static_cast<Try&&>(t)(); |
232 | } catch (...) { |
233 | return invoke_cold(static_cast<Catch&&>(c), static_cast<CatchA&&>(a)...); |
234 | } |
235 | #else |
236 | [](auto&&...) {}(c, a...); // ignore |
237 | return static_cast<Try&&>(t)(); |
238 | #endif |
239 | } |
240 | |
241 | } // namespace folly |
242 | |