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