d06d5b6225b73e74e696aad711c3614cdf17a4d1
[folly.git] / folly / futures / Try.h
1 /*
2  * Copyright 2015 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 <type_traits>
20 #include <exception>
21 #include <algorithm>
22 #include <folly/ExceptionWrapper.h>
23 #include <folly/Likely.h>
24 #include <folly/Memory.h>
25 #include <folly/futures/Deprecated.h>
26 #include <folly/futures/FutureException.h>
27 #include <folly/futures/Unit.h>
28
29 namespace folly {
30
31 /*
32  * Try<T> is a wrapper that contains either an instance of T, an exception, or
33  * nothing. Its primary use case is as a representation of a Promise or Future's
34  * result and so it provides a number of methods that are useful in that
35  * context. Exceptions are stored as exception_wrappers so that the user can
36  * minimize rethrows if so desired.
37  *
38  * To represent success or a captured exception, use Try<Unit>
39  */
40 template <class T>
41 class Try {
42   static_assert(!std::is_reference<T>::value,
43                 "Try may not be used with reference types");
44
45   enum class Contains {
46     VALUE,
47     EXCEPTION,
48     NOTHING,
49   };
50
51  public:
52   /*
53    * The value type for the Try
54    */
55   typedef T element_type;
56
57   /*
58    * Construct an empty Try
59    */
60   Try() : contains_(Contains::NOTHING) {}
61
62   /*
63    * Construct a Try with a value by copy
64    *
65    * @param v The value to copy in
66    */
67   explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
68
69   /*
70    * Construct a Try with a value by move
71    *
72    * @param v The value to move in
73    */
74   explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
75
76   /// Implicit conversion from Try<void> to Try<Unit>
77   template <class T2 = T>
78   /* implicit */
79   Try(typename std::enable_if<std::is_same<Unit, T2>::value,
80                               Try<void> const&>::type t);
81
82   /*
83    * Construct a Try with an exception_wrapper
84    *
85    * @param e The exception_wrapper
86    */
87   explicit Try(exception_wrapper e)
88     : contains_(Contains::EXCEPTION),
89       e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
90
91   /*
92    * DEPRECATED
93    * Construct a Try with an exception_pointer
94    *
95    * @param ep The exception_pointer. Will be rethrown.
96    */
97   explicit Try(std::exception_ptr ep) DEPRECATED
98     : contains_(Contains::EXCEPTION) {
99     try {
100       std::rethrow_exception(ep);
101     } catch (const std::exception& e) {
102       e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
103     } catch (...) {
104       e_ = folly::make_unique<exception_wrapper>(std::current_exception());
105     }
106   }
107
108   // Move constructor
109   Try(Try<T>&& t) noexcept;
110   // Move assigner
111   Try& operator=(Try<T>&& t) noexcept;
112
113   // Copy constructor
114   Try(const Try& t);
115   // Copy assigner
116   Try& operator=(const Try& t);
117
118   ~Try();
119
120   /*
121    * Get a mutable reference to the contained value. If the Try contains an
122    * exception it will be rethrown.
123    *
124    * @returns mutable reference to the contained value
125    */
126   T& value();
127   /*
128    * Get a const reference to the contained value. If the Try contains an
129    * exception it will be rethrown.
130    *
131    * @returns const reference to the contained value
132    */
133   const T& value() const;
134
135   /*
136    * If the Try contains an exception, rethrow it. Otherwise do nothing.
137    */
138   void throwIfFailed() const;
139
140   /*
141    * Const dereference operator. If the Try contains an exception it will be
142    * rethrown.
143    *
144    * @returns const reference to the contained value
145    */
146   const T& operator*() const { return value(); }
147   /*
148    * Dereference operator. If the Try contains an exception it will be rethrown.
149    *
150    * @returns mutable reference to the contained value
151    */
152   T& operator*() { return value(); }
153
154   /*
155    * Const arrow operator. If the Try contains an exception it will be
156    * rethrown.
157    *
158    * @returns const reference to the contained value
159    */
160   const T* operator->() const { return &value(); }
161   /*
162    * Arrow operator. If the Try contains an exception it will be rethrown.
163    *
164    * @returns mutable reference to the contained value
165    */
166   T* operator->() { return &value(); }
167
168   /*
169    * @returns True if the Try contains a value, false otherwise
170    */
171   bool hasValue() const { return contains_ == Contains::VALUE; }
172   /*
173    * @returns True if the Try contains an exception, false otherwise
174    */
175   bool hasException() const { return contains_ == Contains::EXCEPTION; }
176
177   /*
178    * @returns True if the Try contains an exception of type Ex, false otherwise
179    */
180   template <class Ex>
181   bool hasException() const {
182     return hasException() && e_->is_compatible_with<Ex>();
183   }
184
185   exception_wrapper& exception() {
186     if (UNLIKELY(!hasException())) {
187       throw FutureException("exception(): Try does not contain an exception");
188     }
189     return *e_;
190   }
191
192   const exception_wrapper& exception() const {
193     if (UNLIKELY(!hasException())) {
194       throw FutureException("exception(): Try does not contain an exception");
195     }
196     return *e_;
197   }
198
199   /*
200    * If the Try contains an exception and it is of type Ex, execute func(Ex)
201    *
202    * @param func a function that takes a single parameter of type const Ex&
203    *
204    * @returns True if the Try held an Ex and func was executed, false otherwise
205    */
206   template <class Ex, class F>
207   bool withException(F func) const {
208     if (!hasException()) {
209       return false;
210     }
211     return e_->with_exception<Ex>(std::move(func));
212   }
213
214   template <bool isTry, typename R>
215   typename std::enable_if<isTry, R>::type get() {
216     return std::forward<R>(*this);
217   }
218
219   template <bool isTry, typename R>
220   typename std::enable_if<!isTry, R>::type get() {
221     return std::forward<R>(value());
222   }
223
224  private:
225   Contains contains_;
226   union {
227     T value_;
228     std::unique_ptr<exception_wrapper> e_;
229   };
230 };
231
232 /*
233  * Specialization of Try for void value type. Encapsulates either success or an
234  * exception.
235  */
236 template <>
237 class Try<void> {
238  public:
239   // Construct a Try holding a successful and void result
240   Try() : hasValue_(true) {}
241
242   /*
243    * Construct a Try with an exception_wrapper
244    *
245    * @param e The exception_wrapper
246    */
247   explicit Try(exception_wrapper e)
248     : hasValue_(false),
249       e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
250
251   /*
252    * DEPRECATED
253    * Construct a Try with an exception_pointer
254    *
255    * @param ep The exception_pointer. Will be rethrown.
256    */
257   explicit Try(std::exception_ptr ep) DEPRECATED : hasValue_(false) {
258     try {
259       std::rethrow_exception(ep);
260     } catch (const std::exception& e) {
261       e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
262     } catch (...) {
263       e_ = folly::make_unique<exception_wrapper>(std::current_exception());
264     }
265   }
266
267   // Copy assigner
268   Try& operator=(const Try<void>& t) {
269     hasValue_ = t.hasValue_;
270     if (t.e_) {
271       e_ = folly::make_unique<exception_wrapper>(*t.e_);
272     }
273     return *this;
274   }
275   // Copy constructor
276   Try(const Try<void>& t) {
277     *this = t;
278   }
279
280   // If the Try contains an exception, throws it
281   void value() const { throwIfFailed(); }
282   // Dereference operator. If the Try contains an exception, throws it
283   void operator*() const { return value(); }
284
285   // If the Try contains an exception, throws it
286   inline void throwIfFailed() const;
287
288   // @returns False if the Try contains an exception, true otherwise
289   bool hasValue() const { return hasValue_; }
290   // @returns True if the Try contains an exception, false otherwise
291   bool hasException() const { return !hasValue_; }
292
293   // @returns True if the Try contains an exception of type Ex, false otherwise
294   template <class Ex>
295   bool hasException() const {
296     return hasException() && e_->is_compatible_with<Ex>();
297   }
298
299   /*
300    * @throws FutureException if the Try doesn't contain an exception
301    *
302    * @returns mutable reference to the exception contained by this Try
303    */
304   exception_wrapper& exception() {
305     if (UNLIKELY(!hasException())) {
306       throw FutureException("exception(): Try does not contain an exception");
307     }
308     return *e_;
309   }
310
311   const exception_wrapper& exception() const {
312     if (UNLIKELY(!hasException())) {
313       throw FutureException("exception(): Try does not contain an exception");
314     }
315     return *e_;
316   }
317
318   /*
319    * If the Try contains an exception and it is of type Ex, execute func(Ex)
320    *
321    * @param func a function that takes a single parameter of type const Ex&
322    *
323    * @returns True if the Try held an Ex and func was executed, false otherwise
324    */
325   template <class Ex, class F>
326   bool withException(F func) const {
327     if (!hasException()) {
328       return false;
329     }
330     return e_->with_exception<Ex>(std::move(func));
331   }
332
333   template <bool, typename R>
334   R get() {
335     return std::forward<R>(*this);
336   }
337
338  private:
339   bool hasValue_;
340   std::unique_ptr<exception_wrapper> e_{nullptr};
341 };
342
343 /*
344  * Extracts value from try and returns it. Throws if try contained an exception.
345  *
346  * @param t Try to extract value from
347  *
348  * @returns value contained in t
349  */
350 template <typename T>
351 T moveFromTry(Try<T>&& t);
352
353 /*
354  * Throws if try contained an exception.
355  *
356  * @param t Try to move from
357  */
358 void moveFromTry(Try<void>&& t);
359
360 /*
361  * @param f a function to execute and capture the result of (value or exception)
362  *
363  * @returns Try holding the result of f
364  */
365 template <typename F>
366 typename std::enable_if<
367   !std::is_same<typename std::result_of<F()>::type, void>::value,
368   Try<typename std::result_of<F()>::type>>::type
369 makeTryWith(F&& f);
370
371 /*
372  * Specialization of makeTryWith for void return
373  *
374  * @param f a function to execute and capture the result of
375  *
376  * @returns Try<void> holding the result of f
377  */
378 template <typename F>
379 typename std::enable_if<
380   std::is_same<typename std::result_of<F()>::type, void>::value,
381   Try<void>>::type
382 makeTryWith(F&& f);
383
384 } // folly
385
386 #include <folly/futures/Try-inl.h>