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