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