efcc5a9749d2757efe14eb3d01118f7fc4632e98
[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         e_(exception_wrapper::from_exception_ptr(ep)) {}
119
120   // Move constructor
121   Try(Try<T>&& t) noexcept;
122   // Move assigner
123   Try& operator=(Try<T>&& t) noexcept;
124
125   // Copy constructor
126   Try(const Try& t);
127   // Copy assigner
128   Try& operator=(const Try& t);
129
130   ~Try();
131
132   /*
133    * Get a mutable reference to the contained value. If the Try contains an
134    * exception it will be rethrown.
135    *
136    * @returns mutable reference to the contained value
137    */
138   T& value()&;
139   /*
140    * Get a rvalue reference to the contained value. If the Try contains an
141    * exception it will be rethrown.
142    *
143    * @returns rvalue reference to the contained value
144    */
145   T&& value()&&;
146   /*
147    * Get a const reference to the contained value. If the Try contains an
148    * exception it will be rethrown.
149    *
150    * @returns const reference to the contained value
151    */
152   const T& value() const&;
153
154   /*
155    * If the Try contains an exception, rethrow it. Otherwise do nothing.
156    */
157   void throwIfFailed() const;
158
159   /*
160    * Const dereference operator. If the Try contains an exception it will be
161    * rethrown.
162    *
163    * @returns const reference to the contained value
164    */
165   const T& operator*() const { return value(); }
166   /*
167    * Dereference operator. If the Try contains an exception it will be rethrown.
168    *
169    * @returns mutable reference to the contained value
170    */
171   T& operator*() { return value(); }
172
173   /*
174    * Const arrow operator. If the Try contains an exception it will be
175    * rethrown.
176    *
177    * @returns const reference to the contained value
178    */
179   const T* operator->() const { return &value(); }
180   /*
181    * Arrow operator. If the Try contains an exception it will be rethrown.
182    *
183    * @returns mutable reference to the contained value
184    */
185   T* operator->() { return &value(); }
186
187   /*
188    * @returns True if the Try contains a value, false otherwise
189    */
190   bool hasValue() const { return contains_ == Contains::VALUE; }
191   /*
192    * @returns True if the Try contains an exception, false otherwise
193    */
194   bool hasException() const { return contains_ == Contains::EXCEPTION; }
195
196   /*
197    * @returns True if the Try contains an exception of type Ex, false otherwise
198    */
199   template <class Ex>
200   bool hasException() const {
201     return hasException() && e_.is_compatible_with<Ex>();
202   }
203
204   exception_wrapper& exception() & {
205     if (!hasException()) {
206       try_detail::throwTryDoesNotContainException();
207     }
208     return e_;
209   }
210
211   exception_wrapper&& exception() && {
212     if (!hasException()) {
213       try_detail::throwTryDoesNotContainException();
214     }
215     return std::move(e_);
216   }
217
218   const exception_wrapper& exception() const & {
219     if (!hasException()) {
220       try_detail::throwTryDoesNotContainException();
221     }
222     return e_;
223   }
224
225   const exception_wrapper&& exception() const && {
226     if (!hasException()) {
227       try_detail::throwTryDoesNotContainException();
228     }
229     return std::move(e_);
230   }
231
232   /*
233    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
234    *          otherwise, returns `nullptr`.
235    */
236   std::exception* tryGetExceptionObject() {
237     return hasException() ? e_.get_exception() : nullptr;
238   }
239   std::exception const* tryGetExceptionObject() const {
240     return hasException() ? e_.get_exception() : nullptr;
241   }
242
243   /*
244    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
245    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
246    *          returns `nullptr`.
247    */
248   template <class E>
249   E* tryGetExceptionObject() {
250     return hasException() ? e_.get_exception<E>() : nullptr;
251   }
252   template <class E>
253   E const* tryGetExceptionObject() const {
254     return hasException() ? e_.get_exception<E>() : nullptr;
255   }
256
257   /*
258    * If the Try contains an exception and it is of type Ex, execute func(Ex)
259    *
260    * @param func a function that takes a single parameter of type const Ex&
261    *
262    * @returns True if the Try held an Ex and func was executed, false otherwise
263    */
264   template <class Ex, class F>
265   bool withException(F func) {
266     if (!hasException()) {
267       return false;
268     }
269     return e_.with_exception<Ex>(std::move(func));
270   }
271   template <class Ex, class F>
272   bool withException(F func) const {
273     if (!hasException()) {
274       return false;
275     }
276     return e_.with_exception<Ex>(std::move(func));
277   }
278
279   /*
280    * If the Try contains an exception and it is of type compatible with Ex as
281    * deduced from the first parameter of func, execute func(Ex)
282    *
283    * @param func a function that takes a single parameter of type const Ex&
284    *
285    * @returns True if the Try held an Ex and func was executed, false otherwise
286    */
287   template <class F>
288   bool withException(F func) {
289     if (!hasException()) {
290       return false;
291     }
292     return e_.with_exception(std::move(func));
293   }
294   template <class F>
295   bool withException(F func) const {
296     if (!hasException()) {
297       return false;
298     }
299     return e_.with_exception(std::move(func));
300   }
301
302   template <bool isTry, typename R>
303   typename std::enable_if<isTry, R>::type get() {
304     return std::forward<R>(*this);
305   }
306
307   template <bool isTry, typename R>
308   typename std::enable_if<!isTry, R>::type get() {
309     return std::forward<R>(value());
310   }
311
312  private:
313   Contains contains_;
314   union {
315     T value_;
316     exception_wrapper e_;
317   };
318 };
319
320 /*
321  * Specialization of Try for void value type. Encapsulates either success or an
322  * exception.
323  */
324 template <>
325 class Try<void> {
326  public:
327   /*
328    * The value type for the Try
329    */
330   typedef void element_type;
331
332   // Construct a Try holding a successful and void result
333   Try() : hasValue_(true) {}
334
335   /*
336    * Construct a Try with an exception_wrapper
337    *
338    * @param e The exception_wrapper
339    */
340   explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {}
341
342   /*
343    * DEPRECATED
344    * Construct a Try with an exception_pointer
345    *
346    * @param ep The exception_pointer. Will be rethrown.
347    */
348   FOLLY_DEPRECATED("use Try(exception_wrapper)")
349   explicit Try(std::exception_ptr ep)
350       : hasValue_(false), e_(exception_wrapper::from_exception_ptr(ep)) {}
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   exception_wrapper&& exception() && {
395     if (!hasException()) {
396       try_detail::throwTryDoesNotContainException();
397     }
398     return std::move(e_);
399   }
400
401   const exception_wrapper& exception() const & {
402     if (!hasException()) {
403       try_detail::throwTryDoesNotContainException();
404     }
405     return e_;
406   }
407
408   const exception_wrapper&& exception() const && {
409     if (!hasException()) {
410       try_detail::throwTryDoesNotContainException();
411     }
412     return std::move(e_);
413   }
414
415   /*
416    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
417    *          otherwise, returns `nullptr`.
418    */
419   std::exception* tryGetExceptionObject() {
420     return hasException() ? e_.get_exception() : nullptr;
421   }
422   std::exception const* tryGetExceptionObject() const {
423     return hasException() ? e_.get_exception() : nullptr;
424   }
425
426   /*
427    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
428    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
429    *          returns `nullptr`.
430    */
431   template <class E>
432   E* tryGetExceptionObject() {
433     return hasException() ? e_.get_exception<E>() : nullptr;
434   }
435   template <class E>
436   E const* tryGetExceptionObject() const {
437     return hasException() ? e_.get_exception<E>() : nullptr;
438   }
439
440   /*
441    * If the Try contains an exception and it is of type Ex, execute func(Ex)
442    *
443    * @param func a function that takes a single parameter of type const Ex&
444    *
445    * @returns True if the Try held an Ex and func was executed, false otherwise
446    */
447   template <class Ex, class F>
448   bool withException(F func) {
449     if (!hasException()) {
450       return false;
451     }
452     return e_.with_exception<Ex>(std::move(func));
453   }
454   template <class Ex, class F>
455   bool withException(F func) const {
456     if (!hasException()) {
457       return false;
458     }
459     return e_.with_exception<Ex>(std::move(func));
460   }
461
462   /*
463    * If the Try contains an exception and it is of type compatible with Ex as
464    * deduced from the first parameter of func, execute func(Ex)
465    *
466    * @param func a function that takes a single parameter of type const Ex&
467    *
468    * @returns True if the Try held an Ex and func was executed, false otherwise
469    */
470   template <class F>
471   bool withException(F func) {
472     if (!hasException()) {
473       return false;
474     }
475     return e_.with_exception(std::move(func));
476   }
477   template <class F>
478   bool withException(F func) const {
479     if (!hasException()) {
480       return false;
481     }
482     return e_.with_exception(std::move(func));
483   }
484
485   template <bool, typename R>
486   R get() {
487     return std::forward<R>(*this);
488   }
489
490  private:
491   bool hasValue_;
492   exception_wrapper e_;
493 };
494
495 /*
496  * @param f a function to execute and capture the result of (value or exception)
497  *
498  * @returns Try holding the result of f
499  */
500 template <typename F>
501 typename std::enable_if<
502   !std::is_same<typename std::result_of<F()>::type, void>::value,
503   Try<typename std::result_of<F()>::type>>::type
504 makeTryWith(F&& f);
505
506 /*
507  * Specialization of makeTryWith for void return
508  *
509  * @param f a function to execute and capture the result of
510  *
511  * @returns Try<void> holding the result of f
512  */
513 template <typename F>
514 typename std::enable_if<
515   std::is_same<typename std::result_of<F()>::type, void>::value,
516   Try<void>>::type
517 makeTryWith(F&& f);
518
519 /**
520  * Tuple<Try<Type>...> -> std::tuple<Type...>
521  *
522  * Unwraps a tuple-like type containing a sequence of Try<Type> instances to
523  * std::tuple<Type>
524  */
525 template <typename Tuple>
526 auto unwrapTryTuple(Tuple&&);
527
528 } // namespace folly
529
530 #include <folly/Try-inl.h>