Fixes for Try::withException
[folly.git] / folly / Try.h
1 /*
2  * Copyright 2017 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/ExceptionWrapper.h>
20 #include <folly/Likely.h>
21 #include <folly/Memory.h>
22 #include <folly/Portability.h>
23 #include <folly/Unit.h>
24 #include <exception>
25 #include <stdexcept>
26 #include <type_traits>
27 #include <utility>
28
29 namespace folly {
30
31 class TryException : public std::logic_error {
32  public:
33   using std::logic_error::logic_error;
34 };
35
36 class UsingUninitializedTry : public TryException {
37  public:
38   UsingUninitializedTry() : TryException("Using uninitialized try") {}
39 };
40
41 /*
42  * Try<T> is a wrapper that contains either an instance of T, an exception, or
43  * nothing. Exceptions are stored as exception_wrappers so that the user can
44  * minimize rethrows if so desired.
45  *
46  * To represent success or a captured exception, use Try<Unit>.
47  */
48 template <class T>
49 class Try {
50   static_assert(!std::is_reference<T>::value,
51                 "Try may not be used with reference types");
52
53   enum class Contains {
54     VALUE,
55     EXCEPTION,
56     NOTHING,
57   };
58
59  public:
60   /*
61    * The value type for the Try
62    */
63   typedef T element_type;
64
65   /*
66    * Construct an empty Try
67    */
68   Try() : contains_(Contains::NOTHING) {}
69
70   /*
71    * Construct a Try with a value by copy
72    *
73    * @param v The value to copy in
74    */
75   explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
76
77   /*
78    * Construct a Try with a value by move
79    *
80    * @param v The value to move in
81    */
82   explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
83
84   /// Implicit conversion from Try<void> to Try<Unit>
85   template <class T2 = T>
86   /* implicit */
87   Try(typename std::enable_if<std::is_same<Unit, T2>::value,
88                               Try<void> const&>::type t);
89
90   /*
91    * Construct a Try with an exception_wrapper
92    *
93    * @param e The exception_wrapper
94    */
95   explicit Try(exception_wrapper e)
96       : contains_(Contains::EXCEPTION), e_(std::move(e)) {}
97
98   /*
99    * DEPRECATED
100    * Construct a Try with an exception_pointer
101    *
102    * @param ep The exception_pointer. Will be rethrown.
103    */
104   FOLLY_DEPRECATED("use Try(exception_wrapper)")
105   explicit Try(std::exception_ptr ep)
106     : contains_(Contains::EXCEPTION) {
107     try {
108       std::rethrow_exception(ep);
109     } catch (std::exception& e) {
110       e_ = exception_wrapper(std::current_exception(), e);
111     } catch (...) {
112       e_ = exception_wrapper(std::current_exception());
113     }
114   }
115
116   // Move constructor
117   Try(Try<T>&& t) noexcept;
118   // Move assigner
119   Try& operator=(Try<T>&& t) noexcept;
120
121   // Copy constructor
122   Try(const Try& t);
123   // Copy assigner
124   Try& operator=(const Try& t);
125
126   ~Try();
127
128   /*
129    * Get a mutable reference to the contained value. If the Try contains an
130    * exception it will be rethrown.
131    *
132    * @returns mutable reference to the contained value
133    */
134   T& value()&;
135   /*
136    * Get a rvalue reference to the contained value. If the Try contains an
137    * exception it will be rethrown.
138    *
139    * @returns rvalue reference to the contained value
140    */
141   T&& value()&&;
142   /*
143    * Get a const reference to the contained value. If the Try contains an
144    * exception it will be rethrown.
145    *
146    * @returns const reference to the contained value
147    */
148   const T& value() const&;
149
150   /*
151    * If the Try contains an exception, rethrow it. Otherwise do nothing.
152    */
153   void throwIfFailed() const;
154
155   /*
156    * Const dereference operator. If the Try contains an exception it will be
157    * rethrown.
158    *
159    * @returns const reference to the contained value
160    */
161   const T& operator*() const { return value(); }
162   /*
163    * Dereference operator. If the Try contains an exception it will be rethrown.
164    *
165    * @returns mutable reference to the contained value
166    */
167   T& operator*() { return value(); }
168
169   /*
170    * Const arrow operator. If the Try contains an exception it will be
171    * rethrown.
172    *
173    * @returns const reference to the contained value
174    */
175   const T* operator->() const { return &value(); }
176   /*
177    * Arrow operator. If the Try contains an exception it will be rethrown.
178    *
179    * @returns mutable reference to the contained value
180    */
181   T* operator->() { return &value(); }
182
183   /*
184    * @returns True if the Try contains a value, false otherwise
185    */
186   bool hasValue() const { return contains_ == Contains::VALUE; }
187   /*
188    * @returns True if the Try contains an exception, false otherwise
189    */
190   bool hasException() const { return contains_ == Contains::EXCEPTION; }
191
192   /*
193    * @returns True if the Try contains an exception of type Ex, false otherwise
194    */
195   template <class Ex>
196   bool hasException() const {
197     return hasException() && e_.is_compatible_with<Ex>();
198   }
199
200   exception_wrapper& exception() {
201     if (UNLIKELY(!hasException())) {
202       throw TryException("exception(): Try does not contain an exception");
203     }
204     return e_;
205   }
206
207   const exception_wrapper& exception() const {
208     if (UNLIKELY(!hasException())) {
209       throw TryException("exception(): Try does not contain an exception");
210     }
211     return e_;
212   }
213
214   /*
215    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
216    *          otherwise, returns `nullptr`.
217    */
218   std::exception* tryGetExceptionObject() {
219     return hasException() ? e_.get_exception() : nullptr;
220   }
221   std::exception const* tryGetExceptionObject() const {
222     return hasException() ? e_.get_exception() : nullptr;
223   }
224
225   /*
226    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
227    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
228    *          returns `nullptr`.
229    */
230   template <class E>
231   E* tryGetExceptionObject() {
232     return hasException() ? e_.get_exception<E>() : nullptr;
233   }
234   template <class E>
235   E const* tryGetExceptionObject() const {
236     return hasException() ? e_.get_exception<E>() : nullptr;
237   }
238
239   /*
240    * If the Try contains an exception and it is of type Ex, execute func(Ex)
241    *
242    * @param func a function that takes a single parameter of type const Ex&
243    *
244    * @returns True if the Try held an Ex and func was executed, false otherwise
245    */
246   template <class Ex, class F>
247   bool withException(F func) {
248     if (!hasException()) {
249       return false;
250     }
251     return e_.with_exception<Ex>(std::move(func));
252   }
253   template <class Ex, class F>
254   bool withException(F func) const {
255     if (!hasException()) {
256       return false;
257     }
258     return e_.with_exception<Ex>(std::move(func));
259   }
260
261   /*
262    * If the Try contains an exception and it is of type compatible with Ex as
263    * deduced from the first parameter of func, execute func(Ex)
264    *
265    * @param func a function that takes a single parameter of type const Ex&
266    *
267    * @returns True if the Try held an Ex and func was executed, false otherwise
268    */
269   template <class F>
270   bool withException(F func) {
271     if (!hasException()) {
272       return false;
273     }
274     return e_.with_exception(std::move(func));
275   }
276   template <class F>
277   bool withException(F func) const {
278     if (!hasException()) {
279       return false;
280     }
281     return e_.with_exception(std::move(func));
282   }
283
284   template <bool isTry, typename R>
285   typename std::enable_if<isTry, R>::type get() {
286     return std::forward<R>(*this);
287   }
288
289   template <bool isTry, typename R>
290   typename std::enable_if<!isTry, R>::type get() {
291     return std::forward<R>(value());
292   }
293
294  private:
295   Contains contains_;
296   union {
297     T value_;
298     exception_wrapper e_;
299   };
300 };
301
302 /*
303  * Specialization of Try for void value type. Encapsulates either success or an
304  * exception.
305  */
306 template <>
307 class Try<void> {
308  public:
309   /*
310    * The value type for the Try
311    */
312   typedef void element_type;
313
314   // Construct a Try holding a successful and void result
315   Try() : hasValue_(true) {}
316
317   /*
318    * Construct a Try with an exception_wrapper
319    *
320    * @param e The exception_wrapper
321    */
322   explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {}
323
324   /*
325    * DEPRECATED
326    * Construct a Try with an exception_pointer
327    *
328    * @param ep The exception_pointer. Will be rethrown.
329    */
330   FOLLY_DEPRECATED("use Try(exception_wrapper)")
331   explicit Try(std::exception_ptr ep) : hasValue_(false) {
332     try {
333       std::rethrow_exception(ep);
334     } catch (const std::exception& e) {
335       e_ = exception_wrapper(std::current_exception(), e);
336     } catch (...) {
337       e_ = exception_wrapper(std::current_exception());
338     }
339   }
340
341   // Copy assigner
342   Try& operator=(const Try<void>& t) {
343     hasValue_ = t.hasValue_;
344     e_ = t.e_;
345     return *this;
346   }
347   // Copy constructor
348   Try(const Try<void>& t) {
349     *this = t;
350   }
351
352   // If the Try contains an exception, throws it
353   void value() const { throwIfFailed(); }
354   // Dereference operator. If the Try contains an exception, throws it
355   void operator*() const { return value(); }
356
357   // If the Try contains an exception, throws it
358   inline void throwIfFailed() const;
359
360   // @returns False if the Try contains an exception, true otherwise
361   bool hasValue() const { return hasValue_; }
362   // @returns True if the Try contains an exception, false otherwise
363   bool hasException() const { return !hasValue_; }
364
365   // @returns True if the Try contains an exception of type Ex, false otherwise
366   template <class Ex>
367   bool hasException() const {
368     return hasException() && e_.is_compatible_with<Ex>();
369   }
370
371   /*
372    * @throws TryException if the Try doesn't contain an exception
373    *
374    * @returns mutable reference to the exception contained by this Try
375    */
376   exception_wrapper& exception() {
377     if (UNLIKELY(!hasException())) {
378       throw TryException("exception(): Try does not contain an exception");
379     }
380     return e_;
381   }
382
383   const exception_wrapper& exception() const {
384     if (UNLIKELY(!hasException())) {
385       throw TryException("exception(): Try does not contain an exception");
386     }
387     return e_;
388   }
389
390   /*
391    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
392    *          otherwise, returns `nullptr`.
393    */
394   std::exception* tryGetExceptionObject() {
395     return hasException() ? e_.get_exception() : nullptr;
396   }
397   std::exception const* tryGetExceptionObject() const {
398     return hasException() ? e_.get_exception() : nullptr;
399   }
400
401   /*
402    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
403    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
404    *          returns `nullptr`.
405    */
406   template <class E>
407   E* tryGetExceptionObject() {
408     return hasException() ? e_.get_exception<E>() : nullptr;
409   }
410   template <class E>
411   E const* tryGetExceptionObject() const {
412     return hasException() ? e_.get_exception<E>() : nullptr;
413   }
414
415   /*
416    * If the Try contains an exception and it is of type Ex, execute func(Ex)
417    *
418    * @param func a function that takes a single parameter of type const Ex&
419    *
420    * @returns True if the Try held an Ex and func was executed, false otherwise
421    */
422   template <class Ex, class F>
423   bool withException(F func) {
424     if (!hasException()) {
425       return false;
426     }
427     return e_.with_exception<Ex>(std::move(func));
428   }
429   template <class Ex, class F>
430   bool withException(F func) const {
431     if (!hasException()) {
432       return false;
433     }
434     return e_.with_exception<Ex>(std::move(func));
435   }
436
437   /*
438    * If the Try contains an exception and it is of type compatible with Ex as
439    * deduced from the first parameter of func, execute func(Ex)
440    *
441    * @param func a function that takes a single parameter of type const Ex&
442    *
443    * @returns True if the Try held an Ex and func was executed, false otherwise
444    */
445   template <class F>
446   bool withException(F func) {
447     if (!hasException()) {
448       return false;
449     }
450     return e_.with_exception(std::move(func));
451   }
452   template <class F>
453   bool withException(F func) const {
454     if (!hasException()) {
455       return false;
456     }
457     return e_.with_exception(std::move(func));
458   }
459
460   template <bool, typename R>
461   R get() {
462     return std::forward<R>(*this);
463   }
464
465  private:
466   bool hasValue_;
467   exception_wrapper e_;
468 };
469
470 /*
471  * Extracts value from try and returns it. Throws if try contained an exception.
472  *
473  * @param t Try to extract value from
474  *
475  * @returns value contained in t
476  */
477 template <typename T>
478 T moveFromTry(Try<T>& t);
479
480 /*
481  * Throws if try contained an exception.
482  *
483  * @param t Try to move from
484  */
485 void moveFromTry(Try<void>& t);
486
487 /*
488  * @param f a function to execute and capture the result of (value or exception)
489  *
490  * @returns Try holding the result of f
491  */
492 template <typename F>
493 typename std::enable_if<
494   !std::is_same<typename std::result_of<F()>::type, void>::value,
495   Try<typename std::result_of<F()>::type>>::type
496 makeTryWith(F&& f);
497
498 /*
499  * Specialization of makeTryWith for void return
500  *
501  * @param f a function to execute and capture the result of
502  *
503  * @returns Try<void> holding the result of f
504  */
505 template <typename F>
506 typename std::enable_if<
507   std::is_same<typename std::result_of<F()>::type, void>::value,
508   Try<void>>::type
509 makeTryWith(F&& f);
510
511 template <typename... Ts>
512 std::tuple<Ts...> unwrapTryTuple(std::tuple<folly::Try<Ts>...>&& ts);
513
514 } // folly
515
516 #include <folly/Try-inl.h>