1 /*
2  * Copyright 2014-present 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 #pragma once
17 
18 #include <folly/ExceptionWrapper.h>
19 #include <folly/Likely.h>
20 #include <folly/Memory.h>
21 #include <folly/Portability.h>
22 #include <folly/Unit.h>
23 #include <folly/Utility.h>
24 #include <folly/functional/Invoke.h>
25 #include <folly/lang/Exception.h>
26 #include <exception>
27 #include <stdexcept>
28 #include <type_traits>
29 #include <utility>
30 
31 namespace folly {
32 
33 class FOLLY_EXPORT TryException : public std::logic_error {
34  public:
35   using std::logic_error::logic_error;
36 };
37 
38 class FOLLY_EXPORT UsingUninitializedTry : public TryException {
39  public:
UsingUninitializedTry()40   UsingUninitializedTry() : TryException("Using uninitialized try") {}
41 };
42 
43 /*
44  * Try<T> is a wrapper that contains either an instance of T, an exception, or
45  * nothing. Exceptions are stored as exception_wrappers so that the user can
46  * minimize rethrows if so desired.
47  *
48  * To represent success or a captured exception, use Try<Unit>.
49  */
50 template <class T>
51 class Try {
52   static_assert(
53       !std::is_reference<T>::value,
54       "Try may not be used with reference types");
55 
56   enum class Contains {
57     VALUE,
58     EXCEPTION,
59     NOTHING,
60   };
61 
62  public:
63   /*
64    * The value type for the Try
65    */
66   typedef T element_type;
67 
68   /*
69    * Construct an empty Try
70    */
Try()71   Try() noexcept : contains_(Contains::NOTHING) {}
72 
73   /*
74    * Construct a Try with a value by copy
75    *
76    * @param v The value to copy in
77    */
Try(const T & v)78   explicit Try(const T& v) noexcept(
79       std::is_nothrow_copy_constructible<T>::value)
80       : contains_(Contains::VALUE), value_(v) {}
81 
82   /*
83    * Construct a Try with a value by move
84    *
85    * @param v The value to move in
86    */
Try(T && v)87   explicit Try(T&& v) noexcept(std::is_nothrow_move_constructible<T>::value)
88       : contains_(Contains::VALUE), value_(std::move(v)) {}
89 
90   template <typename... Args>
Try(in_place_t,Args &&...args)91   explicit Try(in_place_t, Args&&... args) noexcept(
92       std::is_nothrow_constructible<T, Args&&...>::value)
93       : contains_(Contains::VALUE), value_(static_cast<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, Try<void> const&>::
99           type t) noexcept;
100 
101   /*
102    * Construct a Try with an exception_wrapper
103    *
104    * @param e The exception_wrapper
105    */
Try(exception_wrapper e)106   explicit Try(exception_wrapper e) noexcept
107       : contains_(Contains::EXCEPTION), e_(std::move(e)) {}
108 
109   // Move constructor
110   Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value);
111   // Move assigner
112   Try& operator=(Try<T>&& t) noexcept(
113       std::is_nothrow_move_constructible<T>::value);
114 
115   // Copy constructor
116   Try(const Try& t) noexcept(std::is_nothrow_copy_constructible<T>::value);
117   // Copy assigner
118   Try& operator=(const Try& t) noexcept(
119       std::is_nothrow_copy_constructible<T>::value);
120 
121   ~Try();
122 
123   /*
124    * In-place construct the value in the Try object.
125    *
126    * Destroys any previous value prior to constructing the new value.
127    * Leaves *this in an empty state if the construction of T throws.
128    *
129    * @returns reference to the newly constructed value.
130    */
131   template <typename... Args>
132   T& emplace(Args&&... args) noexcept(
133       std::is_nothrow_constructible<T, Args&&...>::value);
134 
135   /*
136    * In-place construct an exception in the Try object.
137    *
138    * Destroys any previous value prior to constructing the new value.
139    * Leaves *this in an empty state if the construction of the exception_wrapper
140    * throws.
141    *
142    * Any arguments passed to emplaceException() are forwarded on to the
143    * exception_wrapper constructor.
144    *
145    * @returns reference to the newly constructed exception_wrapper.
146    */
147   template <typename... Args>
148   exception_wrapper& emplaceException(Args&&... args) noexcept(
149       std::is_nothrow_constructible<exception_wrapper, Args&&...>::value);
150 
151   /*
152    * Get a mutable reference to the contained value. If the Try contains an
153    * exception it will be rethrown.
154    *
155    * @returns mutable reference to the contained value
156    */
157   T& value() &;
158   /*
159    * Get a rvalue reference to the contained value. If the Try contains an
160    * exception it will be rethrown.
161    *
162    * @returns rvalue reference to the contained value
163    */
164   T&& value() &&;
165   /*
166    * Get a const reference to the contained value. If the Try contains an
167    * exception it will be rethrown.
168    *
169    * @returns const reference to the contained value
170    */
171   const T& value() const&;
172   /*
173    * Get a const rvalue reference to the contained value. If the Try contains an
174    * exception it will be rethrown.
175    *
176    * @returns const rvalue reference to the contained value
177    */
178   const T&& value() const&&;
179 
180   /*
181    * If the Try contains an exception, rethrow it. Otherwise do nothing.
182    */
183   void throwIfFailed() const;
184 
185   /*
186    * Const dereference operator. If the Try contains an exception it will be
187    * rethrown.
188    *
189    * @returns const reference to the contained value
190    */
191   const T& operator*() const& {
192     return value();
193   }
194   /*
195    * Dereference operator. If the Try contains an exception it will be rethrown.
196    *
197    * @returns mutable reference to the contained value
198    */
199   T& operator*() & {
200     return value();
201   }
202   /*
203    * Mutable rvalue dereference operator.  If the Try contains an exception it
204    * will be rethrown.
205    *
206    * @returns rvalue reference to the contained value
207    */
208   T&& operator*() && {
209     return std::move(value());
210   }
211   /*
212    * Const rvalue dereference operator.  If the Try contains an exception it
213    * will be rethrown.
214    *
215    * @returns rvalue reference to the contained value
216    */
217   const T&& operator*() const&& {
218     return std::move(value());
219   }
220 
221   /*
222    * Const arrow operator. If the Try contains an exception it will be
223    * rethrown.
224    *
225    * @returns const reference to the contained value
226    */
227   const T* operator->() const {
228     return &value();
229   }
230   /*
231    * Arrow operator. If the Try contains an exception it will be rethrown.
232    *
233    * @returns mutable reference to the contained value
234    */
235   T* operator->() {
236     return &value();
237   }
238 
239   /*
240    * @returns True if the Try contains a value, false otherwise
241    */
hasValue()242   bool hasValue() const {
243     return contains_ == Contains::VALUE;
244   }
245   /*
246    * @returns True if the Try contains an exception, false otherwise
247    */
hasException()248   bool hasException() const {
249     return contains_ == Contains::EXCEPTION;
250   }
251 
252   /*
253    * @returns True if the Try contains an exception of type Ex, false otherwise
254    */
255   template <class Ex>
hasException()256   bool hasException() const {
257     return hasException() && e_.is_compatible_with<Ex>();
258   }
259 
exception()260   exception_wrapper& exception() & {
261     if (!hasException()) {
262       throw_exception<TryException>("Try does not contain an exception");
263     }
264     return e_;
265   }
266 
exception()267   exception_wrapper&& exception() && {
268     if (!hasException()) {
269       throw_exception<TryException>("Try does not contain an exception");
270     }
271     return std::move(e_);
272   }
273 
exception()274   const exception_wrapper& exception() const& {
275     if (!hasException()) {
276       throw_exception<TryException>("Try does not contain an exception");
277     }
278     return e_;
279   }
280 
exception()281   const exception_wrapper&& exception() const&& {
282     if (!hasException()) {
283       throw_exception<TryException>("Try does not contain an exception");
284     }
285     return std::move(e_);
286   }
287 
288   /*
289    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
290    *          otherwise, returns `nullptr`.
291    */
tryGetExceptionObject()292   std::exception* tryGetExceptionObject() {
293     return hasException() ? e_.get_exception() : nullptr;
294   }
tryGetExceptionObject()295   std::exception const* tryGetExceptionObject() const {
296     return hasException() ? e_.get_exception() : nullptr;
297   }
298 
299   /*
300    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
301    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
302    *          returns `nullptr`.
303    */
304   template <class E>
tryGetExceptionObject()305   E* tryGetExceptionObject() {
306     return hasException() ? e_.get_exception<E>() : nullptr;
307   }
308   template <class E>
tryGetExceptionObject()309   E const* tryGetExceptionObject() const {
310     return hasException() ? e_.get_exception<E>() : nullptr;
311   }
312 
313   /*
314    * If the Try contains an exception and it is of type Ex, execute func(Ex)
315    *
316    * @param func a function that takes a single parameter of type const Ex&
317    *
318    * @returns True if the Try held an Ex and func was executed, false otherwise
319    */
320   template <class Ex, class F>
withException(F func)321   bool withException(F func) {
322     if (!hasException()) {
323       return false;
324     }
325     return e_.with_exception<Ex>(std::move(func));
326   }
327   template <class Ex, class F>
withException(F func)328   bool withException(F func) const {
329     if (!hasException()) {
330       return false;
331     }
332     return e_.with_exception<Ex>(std::move(func));
333   }
334 
335   /*
336    * If the Try contains an exception and it is of type compatible with Ex as
337    * deduced from the first parameter of func, execute func(Ex)
338    *
339    * @param func a function that takes a single parameter of type const Ex&
340    *
341    * @returns True if the Try held an Ex and func was executed, false otherwise
342    */
343   template <class F>
withException(F func)344   bool withException(F func) {
345     if (!hasException()) {
346       return false;
347     }
348     return e_.with_exception(std::move(func));
349   }
350   template <class F>
withException(F func)351   bool withException(F func) const {
352     if (!hasException()) {
353       return false;
354     }
355     return e_.with_exception(std::move(func));
356   }
357 
358   template <bool isTry, typename R>
get()359   typename std::enable_if<isTry, R>::type get() {
360     return std::forward<R>(*this);
361   }
362 
363   template <bool isTry, typename R>
get()364   typename std::enable_if<!isTry, R>::type get() {
365     return std::forward<R>(value());
366   }
367 
368  private:
369   void destroy() noexcept;
370 
371   Contains contains_;
372   union {
373     T value_;
374     exception_wrapper e_;
375   };
376 };
377 
378 /*
379  * Specialization of Try for void value type. Encapsulates either success or an
380  * exception.
381  */
382 template <>
383 class Try<void> {
384  public:
385   /*
386    * The value type for the Try
387    */
388   typedef void element_type;
389 
390   // Construct a Try holding a successful and void result
Try()391   Try() noexcept : hasValue_(true) {}
392 
393   /*
394    * Construct a Try with an exception_wrapper
395    *
396    * @param e The exception_wrapper
397    */
Try(exception_wrapper e)398   explicit Try(exception_wrapper e) noexcept
399       : hasValue_(false), e_(std::move(e)) {}
400 
401   // Copy assigner
402   inline Try& operator=(const Try<void>& t) noexcept;
403 
404   // Copy constructor
Try(const Try<void> & t)405   Try(const Try<void>& t) noexcept : hasValue_(t.hasValue_) {
406     if (t.hasException()) {
407       new (&e_) exception_wrapper(t.e_);
408     }
409   }
410 
~Try()411   ~Try() {
412     if (hasException()) {
413       e_.~exception_wrapper();
414     }
415   }
416 
417   /*
418    * In-place construct a 'void' value into this Try object.
419    *
420    * This has the effect of clearing any existing exception stored in the
421    * Try object.
422    */
emplace()423   void emplace() noexcept {
424     if (hasException()) {
425       e_.~exception_wrapper();
426       hasValue_ = true;
427     }
428   }
429 
430   /*
431    * In-place construct an exception in the Try object.
432    *
433    * Destroys any previous value prior to constructing the new value.
434    * Leaves *this in an empty state if the construction of the exception_wrapper
435    * throws.
436    *
437    * Any arguments passed to emplaceException() are forwarded on to the
438    * exception_wrapper constructor.
439    *
440    * @returns reference to the newly constructed exception_wrapper.
441    */
442   template <typename... Args>
443   exception_wrapper& emplaceException(Args&&... args) noexcept(
444       std::is_nothrow_constructible<exception_wrapper, Args&&...>::value);
445 
446   // If the Try contains an exception, throws it
value()447   void value() const {
448     throwIfFailed();
449   }
450   // Dereference operator. If the Try contains an exception, throws it
451   void operator*() const {
452     return value();
453   }
454 
455   // If the Try contains an exception, throws it
456   inline void throwIfFailed() const;
457 
458   // @returns False if the Try contains an exception, true otherwise
hasValue()459   bool hasValue() const {
460     return hasValue_;
461   }
462   // @returns True if the Try contains an exception, false otherwise
hasException()463   bool hasException() const {
464     return !hasValue_;
465   }
466 
467   // @returns True if the Try contains an exception of type Ex, false otherwise
468   template <class Ex>
hasException()469   bool hasException() const {
470     return hasException() && e_.is_compatible_with<Ex>();
471   }
472 
473   /*
474    * @throws TryException if the Try doesn't contain an exception
475    *
476    * @returns mutable reference to the exception contained by this Try
477    */
exception()478   exception_wrapper& exception() & {
479     if (!hasException()) {
480       throw_exception<TryException>("Try does not contain an exception");
481     }
482     return e_;
483   }
484 
exception()485   exception_wrapper&& exception() && {
486     if (!hasException()) {
487       throw_exception<TryException>("Try does not contain an exception");
488     }
489     return std::move(e_);
490   }
491 
exception()492   const exception_wrapper& exception() const& {
493     if (!hasException()) {
494       throw_exception<TryException>("Try does not contain an exception");
495     }
496     return e_;
497   }
498 
exception()499   const exception_wrapper&& exception() const&& {
500     if (!hasException()) {
501       throw_exception<TryException>("Try does not contain an exception");
502     }
503     return std::move(e_);
504   }
505 
506   /*
507    * @returns a pointer to the `std::exception` held by `*this`, if one is held;
508    *          otherwise, returns `nullptr`.
509    */
tryGetExceptionObject()510   std::exception* tryGetExceptionObject() {
511     return hasException() ? e_.get_exception() : nullptr;
512   }
tryGetExceptionObject()513   std::exception const* tryGetExceptionObject() const {
514     return hasException() ? e_.get_exception() : nullptr;
515   }
516 
517   /*
518    * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
519    *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
520    *          returns `nullptr`.
521    */
522   template <class E>
tryGetExceptionObject()523   E* tryGetExceptionObject() {
524     return hasException() ? e_.get_exception<E>() : nullptr;
525   }
526   template <class E>
tryGetExceptionObject()527   E const* tryGetExceptionObject() const {
528     return hasException() ? e_.get_exception<E>() : nullptr;
529   }
530 
531   /*
532    * If the Try contains an exception and it is of type Ex, execute func(Ex)
533    *
534    * @param func a function that takes a single parameter of type const Ex&
535    *
536    * @returns True if the Try held an Ex and func was executed, false otherwise
537    */
538   template <class Ex, class F>
withException(F func)539   bool withException(F func) {
540     if (!hasException()) {
541       return false;
542     }
543     return e_.with_exception<Ex>(std::move(func));
544   }
545   template <class Ex, class F>
withException(F func)546   bool withException(F func) const {
547     if (!hasException()) {
548       return false;
549     }
550     return e_.with_exception<Ex>(std::move(func));
551   }
552 
553   /*
554    * If the Try contains an exception and it is of type compatible with Ex as
555    * deduced from the first parameter of func, execute func(Ex)
556    *
557    * @param func a function that takes a single parameter of type const Ex&
558    *
559    * @returns True if the Try held an Ex and func was executed, false otherwise
560    */
561   template <class F>
withException(F func)562   bool withException(F func) {
563     if (!hasException()) {
564       return false;
565     }
566     return e_.with_exception(std::move(func));
567   }
568   template <class F>
withException(F func)569   bool withException(F func) const {
570     if (!hasException()) {
571       return false;
572     }
573     return e_.with_exception(std::move(func));
574   }
575 
576   template <bool, typename R>
get()577   R get() {
578     return std::forward<R>(*this);
579   }
580 
581  private:
582   bool hasValue_;
583   union {
584     exception_wrapper e_;
585   };
586 };
587 
588 /*
589  * @param f a function to execute and capture the result of (value or exception)
590  *
591  * @returns Try holding the result of f
592  */
593 template <typename F>
594 typename std::enable_if<
595     !std::is_same<invoke_result_t<F>, void>::value,
596     Try<invoke_result_t<F>>>::type
597 makeTryWith(F&& f);
598 
599 /*
600  * Specialization of makeTryWith for void return
601  *
602  * @param f a function to execute and capture the result of
603  *
604  * @returns Try<void> holding the result of f
605  */
606 template <typename F>
607 typename std::
608     enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type
609     makeTryWith(F&& f);
610 
611 /*
612  * Try to in-place construct a new value from the specified arguments.
613  *
614  * If T's constructor throws an exception then this is caught and the Try<T>
615  * object is initialised to hold that exception.
616  *
617  * @param args Are passed to T's constructor.
618  */
619 template <typename T, typename... Args>
620 T* tryEmplace(Try<T>& t, Args&&... args) noexcept;
621 
622 /*
623  * Overload of tryEmplace() for Try<void>.
624  */
625 inline void tryEmplace(Try<void>& t) noexcept;
626 
627 /*
628  * Try to in-place construct a new value from the result of a function.
629  *
630  * If the function completes successfully then attempts to in-place construct
631  * a value of type, T, passing the result of the function as the only parameter.
632  *
633  * If either the call to the function completes with an exception or the
634  * constructor completes with an exception then the exception is caught and
635  * stored in the Try object.
636  *
637  * @returns A pointer to the newly constructed object if it completed
638  * successfully, otherwise returns nullptr if the operation completed with
639  * an exception.
640  */
641 template <typename T, typename Func>
642 T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept;
643 
644 /*
645  * Specialization of tryEmplaceWith() for Try<void>.
646  *
647  * Calls func() and if it doesn't throw an exception then calls t.emplace().
648  * If func() throws then captures the exception in t using t.emplaceException().
649  *
650  * Func must be callable with zero parameters and must return void.
651  *
652  * @returns true if func() completed without an exception, false if func()
653  * threw an exception.
654  */
655 template <typename Func>
656 bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept;
657 
658 /**
659  * Tuple<Try<Type>...> -> std::tuple<Type...>
660  *
661  * Unwraps a tuple-like type containing a sequence of Try<Type> instances to
662  * std::tuple<Type>
663  */
664 template <typename Tuple>
665 auto unwrapTryTuple(Tuple&&);
666 
667 } // namespace folly
668 
669 #include <folly/Try-inl.h>
670