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