1 | /* |
2 | * Copyright (c) Facebook, Inc. and its affiliates. |
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 <folly/Utility.h> |
20 | #include <folly/functional/Invoke.h> |
21 | |
22 | #include <stdexcept> |
23 | #include <tuple> |
24 | #include <utility> |
25 | |
26 | namespace folly { |
27 | |
28 | template <class T> |
29 | Try<T>::Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value) |
30 | : contains_(t.contains_) { |
31 | if (contains_ == Contains::VALUE) { |
32 | new (&value_) T(std::move(t.value_)); |
33 | } else if (contains_ == Contains::EXCEPTION) { |
34 | new (&e_) exception_wrapper(std::move(t.e_)); |
35 | } |
36 | } |
37 | |
38 | template <class T> |
39 | template <class T2> |
40 | Try<T>::Try(typename std::enable_if< |
41 | std::is_same<Unit, T2>::value, |
42 | Try<void> const&>::type t) noexcept |
43 | : contains_(Contains::NOTHING) { |
44 | if (t.hasValue()) { |
45 | contains_ = Contains::VALUE; |
46 | new (&value_) T(); |
47 | } else if (t.hasException()) { |
48 | contains_ = Contains::EXCEPTION; |
49 | new (&e_) exception_wrapper(t.exception()); |
50 | } |
51 | } |
52 | |
53 | template <class T> |
54 | Try<T>& Try<T>::operator=(Try<T>&& t) noexcept( |
55 | std::is_nothrow_move_constructible<T>::value) { |
56 | if (this == &t) { |
57 | return *this; |
58 | } |
59 | |
60 | destroy(); |
61 | |
62 | if (t.contains_ == Contains::VALUE) { |
63 | new (&value_) T(std::move(t.value_)); |
64 | } else if (t.contains_ == Contains::EXCEPTION) { |
65 | new (&e_) exception_wrapper(std::move(t.e_)); |
66 | } |
67 | |
68 | contains_ = t.contains_; |
69 | |
70 | return *this; |
71 | } |
72 | |
73 | template <class T> |
74 | Try<T>::Try(const Try<T>& t) noexcept( |
75 | std::is_nothrow_copy_constructible<T>::value) { |
76 | static_assert( |
77 | std::is_copy_constructible<T>::value, |
78 | "T must be copyable for Try<T> to be copyable" ); |
79 | contains_ = t.contains_; |
80 | if (contains_ == Contains::VALUE) { |
81 | new (&value_) T(t.value_); |
82 | } else if (contains_ == Contains::EXCEPTION) { |
83 | new (&e_) exception_wrapper(t.e_); |
84 | } |
85 | } |
86 | |
87 | template <class T> |
88 | Try<T>& Try<T>::operator=(const Try<T>& t) noexcept( |
89 | std::is_nothrow_copy_constructible<T>::value) { |
90 | static_assert( |
91 | std::is_copy_constructible<T>::value, |
92 | "T must be copyable for Try<T> to be copyable" ); |
93 | |
94 | if (this == &t) { |
95 | return *this; |
96 | } |
97 | |
98 | destroy(); |
99 | |
100 | if (t.contains_ == Contains::VALUE) { |
101 | new (&value_) T(t.value_); |
102 | } else if (t.contains_ == Contains::EXCEPTION) { |
103 | new (&e_) exception_wrapper(t.e_); |
104 | } |
105 | |
106 | contains_ = t.contains_; |
107 | |
108 | return *this; |
109 | } |
110 | |
111 | template <class T> |
112 | Try<T>::~Try() { |
113 | if (LIKELY(contains_ == Contains::VALUE)) { |
114 | value_.~T(); |
115 | } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) { |
116 | e_.~exception_wrapper(); |
117 | } |
118 | } |
119 | |
120 | template <typename T> |
121 | template <typename... Args> |
122 | T& Try<T>::emplace(Args&&... args) noexcept( |
123 | std::is_nothrow_constructible<T, Args&&...>::value) { |
124 | this->destroy(); |
125 | new (&value_) T(static_cast<Args&&>(args)...); |
126 | contains_ = Contains::VALUE; |
127 | return value_; |
128 | } |
129 | |
130 | template <typename T> |
131 | template <typename... Args> |
132 | exception_wrapper& Try<T>::emplaceException(Args&&... args) noexcept( |
133 | std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { |
134 | this->destroy(); |
135 | new (&e_) exception_wrapper(static_cast<Args&&>(args)...); |
136 | contains_ = Contains::EXCEPTION; |
137 | return e_; |
138 | } |
139 | |
140 | template <class T> |
141 | T& Try<T>::value() & { |
142 | throwIfFailed(); |
143 | return value_; |
144 | } |
145 | |
146 | template <class T> |
147 | T&& Try<T>::value() && { |
148 | throwIfFailed(); |
149 | return std::move(value_); |
150 | } |
151 | |
152 | template <class T> |
153 | const T& Try<T>::value() const& { |
154 | throwIfFailed(); |
155 | return value_; |
156 | } |
157 | |
158 | template <class T> |
159 | const T&& Try<T>::value() const&& { |
160 | throwIfFailed(); |
161 | return std::move(value_); |
162 | } |
163 | |
164 | template <class T> |
165 | void Try<T>::throwIfFailed() const { |
166 | switch (contains_) { |
167 | case Contains::VALUE: |
168 | return; |
169 | case Contains::EXCEPTION: |
170 | e_.throw_exception(); |
171 | case Contains::NOTHING: |
172 | default: |
173 | throw_exception<UsingUninitializedTry>(); |
174 | } |
175 | } |
176 | |
177 | template <class T> |
178 | void Try<T>::destroy() noexcept { |
179 | auto oldContains = std::exchange(contains_, Contains::NOTHING); |
180 | if (LIKELY(oldContains == Contains::VALUE)) { |
181 | value_.~T(); |
182 | } else if (UNLIKELY(oldContains == Contains::EXCEPTION)) { |
183 | e_.~exception_wrapper(); |
184 | } |
185 | } |
186 | |
187 | Try<void>& Try<void>::operator=(const Try<void>& t) noexcept { |
188 | if (t.hasException()) { |
189 | if (hasException()) { |
190 | e_ = t.e_; |
191 | } else { |
192 | new (&e_) exception_wrapper(t.e_); |
193 | hasValue_ = false; |
194 | } |
195 | } else { |
196 | if (hasException()) { |
197 | e_.~exception_wrapper(); |
198 | hasValue_ = true; |
199 | } |
200 | } |
201 | return *this; |
202 | } |
203 | |
204 | template <typename... Args> |
205 | exception_wrapper& Try<void>::emplaceException(Args&&... args) noexcept( |
206 | std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { |
207 | if (hasException()) { |
208 | e_.~exception_wrapper(); |
209 | } |
210 | new (&e_) exception_wrapper(static_cast<Args&&>(args)...); |
211 | hasValue_ = false; |
212 | return e_; |
213 | } |
214 | |
215 | void Try<void>::throwIfFailed() const { |
216 | if (hasException()) { |
217 | e_.throw_exception(); |
218 | } |
219 | } |
220 | |
221 | template <typename F> |
222 | typename std::enable_if< |
223 | !std::is_same<invoke_result_t<F>, void>::value, |
224 | Try<invoke_result_t<F>>>::type |
225 | makeTryWithNoUnwrap(F&& f) { |
226 | using ResultType = invoke_result_t<F>; |
227 | try { |
228 | return Try<ResultType>(f()); |
229 | } catch (std::exception& e) { |
230 | return Try<ResultType>(exception_wrapper(std::current_exception(), e)); |
231 | } catch (...) { |
232 | return Try<ResultType>(exception_wrapper(std::current_exception())); |
233 | } |
234 | } |
235 | |
236 | template <typename F> |
237 | typename std:: |
238 | enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type |
239 | makeTryWithNoUnwrap(F&& f) { |
240 | try { |
241 | f(); |
242 | return Try<void>(); |
243 | } catch (std::exception& e) { |
244 | return Try<void>(exception_wrapper(std::current_exception(), e)); |
245 | } catch (...) { |
246 | return Try<void>(exception_wrapper(std::current_exception())); |
247 | } |
248 | } |
249 | |
250 | template <typename F> |
251 | typename std:: |
252 | enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type |
253 | makeTryWith(F&& f) { |
254 | return makeTryWithNoUnwrap(std::forward<F>(f)); |
255 | } |
256 | |
257 | template <typename F> |
258 | typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>:: |
259 | type |
260 | makeTryWith(F&& f) { |
261 | using ResultType = invoke_result_t<F>; |
262 | try { |
263 | return f(); |
264 | } catch (std::exception& e) { |
265 | return ResultType(exception_wrapper(std::current_exception(), e)); |
266 | } catch (...) { |
267 | return ResultType(exception_wrapper(std::current_exception())); |
268 | } |
269 | } |
270 | |
271 | template <typename T, typename... Args> |
272 | T* tryEmplace(Try<T>& t, Args&&... args) noexcept { |
273 | try { |
274 | return std::addressof(t.emplace(static_cast<Args&&>(args)...)); |
275 | } catch (const std::exception& ex) { |
276 | t.emplaceException(std::current_exception(), ex); |
277 | return nullptr; |
278 | } catch (...) { |
279 | t.emplaceException(std::current_exception()); |
280 | return nullptr; |
281 | } |
282 | } |
283 | |
284 | void tryEmplace(Try<void>& t) noexcept { |
285 | t.emplace(); |
286 | } |
287 | |
288 | template <typename T, typename Func> |
289 | T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept { |
290 | static_assert( |
291 | std::is_constructible<T, folly::invoke_result_t<Func>>::value, |
292 | "Unable to initialise a value of type T with the result of 'func'" ); |
293 | try { |
294 | return std::addressof(t.emplace(static_cast<Func&&>(func)())); |
295 | } catch (const std::exception& ex) { |
296 | t.emplaceException(std::current_exception(), ex); |
297 | return nullptr; |
298 | } catch (...) { |
299 | t.emplaceException(std::current_exception()); |
300 | return nullptr; |
301 | } |
302 | } |
303 | |
304 | template <typename Func> |
305 | bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept { |
306 | static_assert( |
307 | std::is_void<folly::invoke_result_t<Func>>::value, |
308 | "Func returns non-void. Cannot be used to emplace Try<void>" ); |
309 | try { |
310 | static_cast<Func&&>(func)(); |
311 | t.emplace(); |
312 | return true; |
313 | } catch (const std::exception& ex) { |
314 | t.emplaceException(std::current_exception(), ex); |
315 | return false; |
316 | } catch (...) { |
317 | t.emplaceException(std::current_exception()); |
318 | return false; |
319 | } |
320 | } |
321 | |
322 | namespace try_detail { |
323 | |
324 | /** |
325 | * Trait that removes the layer of Try abstractions from the passed in type |
326 | */ |
327 | template <typename Type> |
328 | struct RemoveTry; |
329 | template <template <typename...> class TupleType, typename... Types> |
330 | struct RemoveTry<TupleType<folly::Try<Types>...>> { |
331 | using type = TupleType<Types...>; |
332 | }; |
333 | |
334 | template <std::size_t... Indices, typename Tuple> |
335 | auto unwrapTryTupleImpl(std::index_sequence<Indices...>, Tuple&& instance) { |
336 | using std::get; |
337 | using ReturnType = typename RemoveTry<typename std::decay<Tuple>::type>::type; |
338 | return ReturnType{(get<Indices>(std::forward<Tuple>(instance)).value())...}; |
339 | } |
340 | } // namespace try_detail |
341 | |
342 | template <typename Tuple> |
343 | auto unwrapTryTuple(Tuple&& instance) { |
344 | using TupleDecayed = typename std::decay<Tuple>::type; |
345 | using Seq = std::make_index_sequence<std::tuple_size<TupleDecayed>::value>; |
346 | return try_detail::unwrapTryTupleImpl(Seq{}, std::forward<Tuple>(instance)); |
347 | } |
348 | |
349 | } // namespace folly |
350 | |