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