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>
Try(Try<T> && 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>
Try(typename std::enable_if<std::is_same<Unit,T2>::value,Try<void> const &>::type t)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>
noexcept(std::is_nothrow_move_constructible<T>::value)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>
Try(const Try<T> & 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>
noexcept(std::is_nothrow_copy_constructible<T>::value)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>
~Try()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>
emplace(Args &&...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>
emplaceException(Args &&...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>
value()140 T& Try<T>::value() & {
141   throwIfFailed();
142   return value_;
143 }
144 
145 template <class T>
value()146 T&& Try<T>::value() && {
147   throwIfFailed();
148   return std::move(value_);
149 }
150 
151 template <class T>
value()152 const T& Try<T>::value() const& {
153   throwIfFailed();
154   return value_;
155 }
156 
157 template <class T>
value()158 const T&& Try<T>::value() const&& {
159   throwIfFailed();
160   return std::move(value_);
161 }
162 
163 template <class T>
throwIfFailed()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>
destroy()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>
emplaceException(Args &&...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 
throwIfFailed()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
makeTryWith(F && f)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
makeTryWith(F && f)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>
tryEmplace(Try<T> & t,Args &&...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 
tryEmplace(Try<void> & t)261 void tryEmplace(Try<void>& t) noexcept {
262   t.emplace();
263 }
264 
265 template <typename T, typename Func>
tryEmplaceWith(Try<T> & t,Func && 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>
tryEmplaceWith(Try<void> & t,Func && 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