b4152dd46c2c796559f62d42c19e034d73c8feaf
[folly.git] / folly / futures / Try.h
1 /*
2  * Copyright 2016 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/Portability.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   FOLLY_DEPRECATED("use Try(exception_wrapper)")
98   explicit Try(std::exception_ptr ep)
99     : contains_(Contains::EXCEPTION) {
100     try {
101       std::rethrow_exception(ep);
102     } catch (const std::exception& e) {
103       e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
104     } catch (...) {
105       e_ = folly::make_unique<exception_wrapper>(std::current_exception());
106     }
107   }
108
109   // Move constructor
110   Try(Try<T>&& t) noexcept;
111   // Move assigner
112   Try& operator=(Try<T>&& t) noexcept;
113
114   // Copy constructor
115   Try(const Try& t);
116   // Copy assigner
117   Try& operator=(const Try& t);
118
119   ~Try();
120
121   /*
122    * Get a mutable reference to the contained value. If the Try contains an
123    * exception it will be rethrown.
124    *
125    * @returns mutable reference to the contained value
126    */
127   T& value()&;
128   /*
129    * Get a rvalue reference to the contained value. If the Try contains an
130    * exception it will be rethrown.
131    *
132    * @returns rvalue reference to the contained value
133    */
134   T&& value()&&;
135   /*
136    * Get a const reference to the contained value. If the Try contains an
137    * exception it will be rethrown.
138    *
139    * @returns const reference to the contained value
140    */
141   const T& value() const&;
142
143   /*
144    * If the Try contains an exception, rethrow it. Otherwise do nothing.
145    */
146   void throwIfFailed() const;
147
148   /*
149    * Const dereference operator. If the Try contains an exception it will be
150    * rethrown.
151    *
152    * @returns const reference to the contained value
153    */
154   const T& operator*() const { return value(); }
155   /*
156    * Dereference operator. If the Try contains an exception it will be rethrown.
157    *
158    * @returns mutable reference to the contained value
159    */
160   T& operator*() { return value(); }
161
162   /*
163    * Const arrow operator. If the Try contains an exception it will be
164    * rethrown.
165    *
166    * @returns const reference to the contained value
167    */
168   const T* operator->() const { return &value(); }
169   /*
170    * Arrow operator. If the Try contains an exception it will be rethrown.
171    *
172    * @returns mutable reference to the contained value
173    */
174   T* operator->() { return &value(); }
175
176   /*
177    * @returns True if the Try contains a value, false otherwise
178    */
179   bool hasValue() const { return contains_ == Contains::VALUE; }
180   /*
181    * @returns True if the Try contains an exception, false otherwise
182    */
183   bool hasException() const { return contains_ == Contains::EXCEPTION; }
184
185   /*
186    * @returns True if the Try contains an exception of type Ex, false otherwise
187    */
188   template <class Ex>
189   bool hasException() const {
190     return hasException() && e_->is_compatible_with<Ex>();
191   }
192
193   exception_wrapper& exception() {
194     if (UNLIKELY(!hasException())) {
195       throw FutureException("exception(): Try does not contain an exception");
196     }
197     return *e_;
198   }
199
200   const exception_wrapper& exception() const {
201     if (UNLIKELY(!hasException())) {
202       throw FutureException("exception(): Try does not contain an exception");
203     }
204     return *e_;
205   }
206
207   /*
208    * If the Try contains an exception and it is of type Ex, execute func(Ex)
209    *
210    * @param func a function that takes a single parameter of type const Ex&
211    *
212    * @returns True if the Try held an Ex and func was executed, false otherwise
213    */
214   template <class Ex, class F>
215   bool withException(F func) const {
216     if (!hasException()) {
217       return false;
218     }
219     return e_->with_exception(std::move(func));
220   }
221
222   template <bool isTry, typename R>
223   typename std::enable_if<isTry, R>::type get() {
224     return std::forward<R>(*this);
225   }
226
227   template <bool isTry, typename R>
228   typename std::enable_if<!isTry, R>::type get() {
229     return std::forward<R>(value());
230   }
231
232  private:
233   Contains contains_;
234   union {
235     T value_;
236     std::unique_ptr<exception_wrapper> e_;
237   };
238 };
239
240 /*
241  * Specialization of Try for void value type. Encapsulates either success or an
242  * exception.
243  */
244 template <>
245 class Try<void> {
246  public:
247   // Construct a Try holding a successful and void result
248   Try() : hasValue_(true) {}
249
250   /*
251    * Construct a Try with an exception_wrapper
252    *
253    * @param e The exception_wrapper
254    */
255   explicit Try(exception_wrapper e)
256     : hasValue_(false),
257       e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
258
259   /*
260    * DEPRECATED
261    * Construct a Try with an exception_pointer
262    *
263    * @param ep The exception_pointer. Will be rethrown.
264    */
265   FOLLY_DEPRECATED("use Try(exception_wrapper)")
266   explicit Try(std::exception_ptr ep) : hasValue_(false) {
267     try {
268       std::rethrow_exception(ep);
269     } catch (const std::exception& e) {
270       e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
271     } catch (...) {
272       e_ = folly::make_unique<exception_wrapper>(std::current_exception());
273     }
274   }
275
276   // Copy assigner
277   Try& operator=(const Try<void>& t) {
278     hasValue_ = t.hasValue_;
279     if (t.e_) {
280       e_ = folly::make_unique<exception_wrapper>(*t.e_);
281     }
282     return *this;
283   }
284   // Copy constructor
285   Try(const Try<void>& t) {
286     *this = t;
287   }
288
289   // If the Try contains an exception, throws it
290   void value() const { throwIfFailed(); }
291   // Dereference operator. If the Try contains an exception, throws it
292   void operator*() const { return value(); }
293
294   // If the Try contains an exception, throws it
295   inline void throwIfFailed() const;
296
297   // @returns False if the Try contains an exception, true otherwise
298   bool hasValue() const { return hasValue_; }
299   // @returns True if the Try contains an exception, false otherwise
300   bool hasException() const { return !hasValue_; }
301
302   // @returns True if the Try contains an exception of type Ex, false otherwise
303   template <class Ex>
304   bool hasException() const {
305     return hasException() && e_->is_compatible_with<Ex>();
306   }
307
308   /*
309    * @throws FutureException if the Try doesn't contain an exception
310    *
311    * @returns mutable reference to the exception contained by this Try
312    */
313   exception_wrapper& exception() {
314     if (UNLIKELY(!hasException())) {
315       throw FutureException("exception(): Try does not contain an exception");
316     }
317     return *e_;
318   }
319
320   const exception_wrapper& exception() const {
321     if (UNLIKELY(!hasException())) {
322       throw FutureException("exception(): Try does not contain an exception");
323     }
324     return *e_;
325   }
326
327   /*
328    * If the Try contains an exception and it is of type Ex, execute func(Ex)
329    *
330    * @param func a function that takes a single parameter of type const Ex&
331    *
332    * @returns True if the Try held an Ex and func was executed, false otherwise
333    */
334   template <class Ex, class F>
335   bool withException(F func) const {
336     if (!hasException()) {
337       return false;
338     }
339     return e_->with_exception(std::move(func));
340   }
341
342   template <bool, typename R>
343   R get() {
344     return std::forward<R>(*this);
345   }
346
347  private:
348   bool hasValue_;
349   std::unique_ptr<exception_wrapper> e_{nullptr};
350 };
351
352 /*
353  * Extracts value from try and returns it. Throws if try contained an exception.
354  *
355  * @param t Try to extract value from
356  *
357  * @returns value contained in t
358  */
359 template <typename T>
360 T moveFromTry(Try<T>& t);
361
362 /*
363  * Throws if try contained an exception.
364  *
365  * @param t Try to move from
366  */
367 void moveFromTry(Try<void>& t);
368
369 /*
370  * @param f a function to execute and capture the result of (value or exception)
371  *
372  * @returns Try holding the result of f
373  */
374 template <typename F>
375 typename std::enable_if<
376   !std::is_same<typename std::result_of<F()>::type, void>::value,
377   Try<typename std::result_of<F()>::type>>::type
378 makeTryWith(F&& f);
379
380 /*
381  * Specialization of makeTryWith for void return
382  *
383  * @param f a function to execute and capture the result of
384  *
385  * @returns Try<void> holding the result of f
386  */
387 template <typename F>
388 typename std::enable_if<
389   std::is_same<typename std::result_of<F()>::type, void>::value,
390   Try<void>>::type
391 makeTryWith(F&& f);
392
393 } // folly
394
395 #include <folly/futures/Try-inl.h>