Split Baton wait methods into fast and slow paths
[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    * Get a const rvalue reference to the contained value. If the Try contains an
155    * exception it will be rethrown.
156    *
157    * @returns const rvalue 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 & {
173     return value();
174   }
175   /*
176    * Dereference operator. If the Try contains an exception it will be rethrown.
177    *
178    * @returns mutable reference to the contained value
179    */
180   T& operator*() & {
181     return value();
182   }
183   /*
184    * Mutable rvalue dereference operator.  If the Try contains an exception it
185    * will be rethrown.
186    *
187    * @returns rvalue reference to the contained value
188    */
189   T&& operator*() && {
190     return std::move(value());
191   }
192   /*
193    * Const rvalue dereference operator.  If the Try contains an exception it
194    * will be rethrown.
195    *
196    * @returns rvalue reference to the contained value
197    */
198   const T&& operator*() const && {
199     return std::move(value());
200   }
201
202   /*
203    * Const arrow operator. If the Try contains an exception it will be
204    * rethrown.
205    *
206    * @returns const reference to the contained value
207    */
208   const T* operator->() const { return &value(); }
209   /*
210    * Arrow operator. If the Try contains an exception it will be rethrown.
211    *
212    * @returns mutable reference to the contained value
213    */
214   T* operator->() { return &value(); }
215
216   /*
217    * @returns True if the Try contains a value, false otherwise
218    */
219   bool hasValue() const { return contains_ == Contains::VALUE; }
220   /*
221    * @returns True if the Try contains an exception, false otherwise
222    */
223   bool hasException() const { return contains_ == Contains::EXCEPTION; }
224
225   /*
226    * @returns True if the Try contains an exception of type Ex, false otherwise
227    */
228   template <class Ex>
229   bool hasException() const {
230     return hasException() && e_.is_compatible_with<Ex>();
231   }
232
233   exception_wrapper& exception() & {
234     if (!hasException()) {
235       try_detail::throwTryDoesNotContainException();
236     }
237     return e_;
238   }
239
240   exception_wrapper&& exception() && {
241     if (!hasException()) {
242       try_detail::throwTryDoesNotContainException();
243     }
244     return std::move(e_);
245   }
246
247   const exception_wrapper& exception() const & {
248     if (!hasException()) {
249       try_detail::throwTryDoesNotContainException();
250     }
251     return e_;
252   }
253
254   const exception_wrapper&& exception() const && {
255     if (!hasException()) {
256       try_detail::throwTryDoesNotContainException();
257     }
258     return std::move(e_);
259   }
260
261   /*
262    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
263    *          otherwise, returns `nullptr`.
264    */
265   std::exception* tryGetExceptionObject() {
266     return hasException() ? e_.get_exception() : nullptr;
267   }
268   std::exception const* tryGetExceptionObject() const {
269     return hasException() ? e_.get_exception() : nullptr;
270   }
271
272   /*
273    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
274    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
275    *          returns `nullptr`.
276    */
277   template <class E>
278   E* tryGetExceptionObject() {
279     return hasException() ? e_.get_exception<E>() : nullptr;
280   }
281   template <class E>
282   E const* tryGetExceptionObject() const {
283     return hasException() ? e_.get_exception<E>() : nullptr;
284   }
285
286   /*
287    * If the Try contains an exception and it is of type Ex, execute func(Ex)
288    *
289    * @param func a function that takes a single parameter of type const Ex&
290    *
291    * @returns True if the Try held an Ex and func was executed, false otherwise
292    */
293   template <class Ex, class F>
294   bool withException(F func) {
295     if (!hasException()) {
296       return false;
297     }
298     return e_.with_exception<Ex>(std::move(func));
299   }
300   template <class Ex, class F>
301   bool withException(F func) const {
302     if (!hasException()) {
303       return false;
304     }
305     return e_.with_exception<Ex>(std::move(func));
306   }
307
308   /*
309    * If the Try contains an exception and it is of type compatible with Ex as
310    * deduced from the first parameter of func, execute func(Ex)
311    *
312    * @param func a function that takes a single parameter of type const Ex&
313    *
314    * @returns True if the Try held an Ex and func was executed, false otherwise
315    */
316   template <class F>
317   bool withException(F func) {
318     if (!hasException()) {
319       return false;
320     }
321     return e_.with_exception(std::move(func));
322   }
323   template <class F>
324   bool withException(F func) const {
325     if (!hasException()) {
326       return false;
327     }
328     return e_.with_exception(std::move(func));
329   }
330
331   template <bool isTry, typename R>
332   typename std::enable_if<isTry, R>::type get() {
333     return std::forward<R>(*this);
334   }
335
336   template <bool isTry, typename R>
337   typename std::enable_if<!isTry, R>::type get() {
338     return std::forward<R>(value());
339   }
340
341  private:
342   Contains contains_;
343   union {
344     T value_;
345     exception_wrapper e_;
346   };
347 };
348
349 /*
350  * Specialization of Try for void value type. Encapsulates either success or an
351  * exception.
352  */
353 template <>
354 class Try<void> {
355  public:
356   /*
357    * The value type for the Try
358    */
359   typedef void element_type;
360
361   // Construct a Try holding a successful and void result
362   Try() : hasValue_(true) {}
363
364   /*
365    * Construct a Try with an exception_wrapper
366    *
367    * @param e The exception_wrapper
368    */
369   explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {}
370
371   /*
372    * DEPRECATED
373    * Construct a Try with an exception_pointer
374    *
375    * @param ep The exception_pointer. Will be rethrown.
376    */
377   FOLLY_DEPRECATED("use Try(exception_wrapper)")
378   explicit Try(std::exception_ptr ep)
379       : hasValue_(false), e_(exception_wrapper::from_exception_ptr(ep)) {}
380
381   // Copy assigner
382   Try& operator=(const Try<void>& t) {
383     hasValue_ = t.hasValue_;
384     e_ = t.e_;
385     return *this;
386   }
387   // Copy constructor
388   Try(const Try<void>& t) {
389     *this = t;
390   }
391
392   // If the Try contains an exception, throws it
393   void value() const { throwIfFailed(); }
394   // Dereference operator. If the Try contains an exception, throws it
395   void operator*() const { return value(); }
396
397   // If the Try contains an exception, throws it
398   inline void throwIfFailed() const;
399
400   // @returns False if the Try contains an exception, true otherwise
401   bool hasValue() const { return hasValue_; }
402   // @returns True if the Try contains an exception, false otherwise
403   bool hasException() const { return !hasValue_; }
404
405   // @returns True if the Try contains an exception of type Ex, false otherwise
406   template <class Ex>
407   bool hasException() const {
408     return hasException() && e_.is_compatible_with<Ex>();
409   }
410
411   /*
412    * @throws TryException if the Try doesn't contain an exception
413    *
414    * @returns mutable reference to the exception contained by this Try
415    */
416   exception_wrapper& exception() & {
417     if (!hasException()) {
418       try_detail::throwTryDoesNotContainException();
419     }
420     return e_;
421   }
422
423   exception_wrapper&& exception() && {
424     if (!hasException()) {
425       try_detail::throwTryDoesNotContainException();
426     }
427     return std::move(e_);
428   }
429
430   const exception_wrapper& exception() const & {
431     if (!hasException()) {
432       try_detail::throwTryDoesNotContainException();
433     }
434     return e_;
435   }
436
437   const exception_wrapper&& exception() const && {
438     if (!hasException()) {
439       try_detail::throwTryDoesNotContainException();
440     }
441     return std::move(e_);
442   }
443
444   /*
445    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
446    *          otherwise, returns `nullptr`.
447    */
448   std::exception* tryGetExceptionObject() {
449     return hasException() ? e_.get_exception() : nullptr;
450   }
451   std::exception const* tryGetExceptionObject() const {
452     return hasException() ? e_.get_exception() : nullptr;
453   }
454
455   /*
456    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
457    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
458    *          returns `nullptr`.
459    */
460   template <class E>
461   E* tryGetExceptionObject() {
462     return hasException() ? e_.get_exception<E>() : nullptr;
463   }
464   template <class E>
465   E const* tryGetExceptionObject() const {
466     return hasException() ? e_.get_exception<E>() : nullptr;
467   }
468
469   /*
470    * If the Try contains an exception and it is of type Ex, execute func(Ex)
471    *
472    * @param func a function that takes a single parameter of type const Ex&
473    *
474    * @returns True if the Try held an Ex and func was executed, false otherwise
475    */
476   template <class Ex, class F>
477   bool withException(F func) {
478     if (!hasException()) {
479       return false;
480     }
481     return e_.with_exception<Ex>(std::move(func));
482   }
483   template <class Ex, class F>
484   bool withException(F func) const {
485     if (!hasException()) {
486       return false;
487     }
488     return e_.with_exception<Ex>(std::move(func));
489   }
490
491   /*
492    * If the Try contains an exception and it is of type compatible with Ex as
493    * deduced from the first parameter of func, execute func(Ex)
494    *
495    * @param func a function that takes a single parameter of type const Ex&
496    *
497    * @returns True if the Try held an Ex and func was executed, false otherwise
498    */
499   template <class F>
500   bool withException(F func) {
501     if (!hasException()) {
502       return false;
503     }
504     return e_.with_exception(std::move(func));
505   }
506   template <class F>
507   bool withException(F func) const {
508     if (!hasException()) {
509       return false;
510     }
511     return e_.with_exception(std::move(func));
512   }
513
514   template <bool, typename R>
515   R get() {
516     return std::forward<R>(*this);
517   }
518
519  private:
520   bool hasValue_;
521   exception_wrapper e_;
522 };
523
524 /*
525  * @param f a function to execute and capture the result of (value or exception)
526  *
527  * @returns Try holding the result of f
528  */
529 template <typename F>
530 typename std::enable_if<
531   !std::is_same<typename std::result_of<F()>::type, void>::value,
532   Try<typename std::result_of<F()>::type>>::type
533 makeTryWith(F&& f);
534
535 /*
536  * Specialization of makeTryWith for void return
537  *
538  * @param f a function to execute and capture the result of
539  *
540  * @returns Try<void> holding the result of f
541  */
542 template <typename F>
543 typename std::enable_if<
544   std::is_same<typename std::result_of<F()>::type, void>::value,
545   Try<void>>::type
546 makeTryWith(F&& f);
547
548 /**
549  * Tuple<Try<Type>...> -> std::tuple<Type...>
550  *
551  * Unwraps a tuple-like type containing a sequence of Try<Type> instances to
552  * std::tuple<Type>
553  */
554 template <typename Tuple>
555 auto unwrapTryTuple(Tuple&&);
556
557 } // namespace folly
558
559 #include <folly/Try-inl.h>