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