2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
27 #include <type_traits>
32 class TryException : public std::logic_error {
34 using std::logic_error::logic_error;
37 class UsingUninitializedTry : public TryException {
39 UsingUninitializedTry() : TryException("Using uninitialized try") {}
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.
47 * To represent success or a captured exception, use Try<Unit>.
51 static_assert(!std::is_reference<T>::value,
52 "Try may not be used with reference types");
62 * The value type for the Try
64 typedef T element_type;
67 * Construct an empty Try
69 Try() : contains_(Contains::NOTHING) {}
72 * Construct a Try with a value by copy
74 * @param v The value to copy in
76 explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
79 * Construct a Try with a value by move
81 * @param v The value to move in
83 explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
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)...) {}
90 /// Implicit conversion from Try<void> to Try<Unit>
91 template <class T2 = T>
93 Try(typename std::enable_if<std::is_same<Unit, T2>::value,
94 Try<void> const&>::type t);
97 * Construct a Try with an exception_wrapper
99 * @param e The exception_wrapper
101 explicit Try(exception_wrapper e)
102 : contains_(Contains::EXCEPTION), e_(std::move(e)) {}
106 * Construct a Try with an exception_pointer
108 * @param ep The exception_pointer. Will be rethrown.
110 FOLLY_DEPRECATED("use Try(exception_wrapper)")
111 explicit Try(std::exception_ptr ep)
112 : contains_(Contains::EXCEPTION) {
114 std::rethrow_exception(ep);
115 } catch (std::exception& e) {
116 e_ = exception_wrapper(std::current_exception(), e);
118 e_ = exception_wrapper(std::current_exception());
123 Try(Try<T>&& t) noexcept;
125 Try& operator=(Try<T>&& t) noexcept;
130 Try& operator=(const Try& t);
135 * Get a mutable reference to the contained value. If the Try contains an
136 * exception it will be rethrown.
138 * @returns mutable reference to the contained value
142 * Get a rvalue reference to the contained value. If the Try contains an
143 * exception it will be rethrown.
145 * @returns rvalue reference to the contained value
149 * Get a const reference to the contained value. If the Try contains an
150 * exception it will be rethrown.
152 * @returns const reference to the contained value
154 const T& value() const&;
157 * If the Try contains an exception, rethrow it. Otherwise do nothing.
159 void throwIfFailed() const;
162 * Const dereference operator. If the Try contains an exception it will be
165 * @returns const reference to the contained value
167 const T& operator*() const { return value(); }
169 * Dereference operator. If the Try contains an exception it will be rethrown.
171 * @returns mutable reference to the contained value
173 T& operator*() { return value(); }
176 * Const arrow operator. If the Try contains an exception it will be
179 * @returns const reference to the contained value
181 const T* operator->() const { return &value(); }
183 * Arrow operator. If the Try contains an exception it will be rethrown.
185 * @returns mutable reference to the contained value
187 T* operator->() { return &value(); }
190 * @returns True if the Try contains a value, false otherwise
192 bool hasValue() const { return contains_ == Contains::VALUE; }
194 * @returns True if the Try contains an exception, false otherwise
196 bool hasException() const { return contains_ == Contains::EXCEPTION; }
199 * @returns True if the Try contains an exception of type Ex, false otherwise
202 bool hasException() const {
203 return hasException() && e_.is_compatible_with<Ex>();
206 exception_wrapper& exception() {
207 if (UNLIKELY(!hasException())) {
208 throw TryException("exception(): Try does not contain an exception");
213 const exception_wrapper& exception() const {
214 if (UNLIKELY(!hasException())) {
215 throw TryException("exception(): Try does not contain an exception");
221 * @returns a pointer to the `std::exception` held by `*this`, if one is held;
222 * otherwise, returns `nullptr`.
224 std::exception* tryGetExceptionObject() {
225 return hasException() ? e_.get_exception() : nullptr;
227 std::exception const* tryGetExceptionObject() const {
228 return hasException() ? e_.get_exception() : nullptr;
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,
237 E* tryGetExceptionObject() {
238 return hasException() ? e_.get_exception<E>() : nullptr;
241 E const* tryGetExceptionObject() const {
242 return hasException() ? e_.get_exception<E>() : nullptr;
246 * If the Try contains an exception and it is of type Ex, execute func(Ex)
248 * @param func a function that takes a single parameter of type const Ex&
250 * @returns True if the Try held an Ex and func was executed, false otherwise
252 template <class Ex, class F>
253 bool withException(F func) {
254 if (!hasException()) {
257 return e_.with_exception<Ex>(std::move(func));
259 template <class Ex, class F>
260 bool withException(F func) const {
261 if (!hasException()) {
264 return e_.with_exception<Ex>(std::move(func));
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)
271 * @param func a function that takes a single parameter of type const Ex&
273 * @returns True if the Try held an Ex and func was executed, false otherwise
276 bool withException(F func) {
277 if (!hasException()) {
280 return e_.with_exception(std::move(func));
283 bool withException(F func) const {
284 if (!hasException()) {
287 return e_.with_exception(std::move(func));
290 template <bool isTry, typename R>
291 typename std::enable_if<isTry, R>::type get() {
292 return std::forward<R>(*this);
295 template <bool isTry, typename R>
296 typename std::enable_if<!isTry, R>::type get() {
297 return std::forward<R>(value());
304 exception_wrapper e_;
309 * Specialization of Try for void value type. Encapsulates either success or an
316 * The value type for the Try
318 typedef void element_type;
320 // Construct a Try holding a successful and void result
321 Try() : hasValue_(true) {}
324 * Construct a Try with an exception_wrapper
326 * @param e The exception_wrapper
328 explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {}
332 * Construct a Try with an exception_pointer
334 * @param ep The exception_pointer. Will be rethrown.
336 FOLLY_DEPRECATED("use Try(exception_wrapper)")
337 explicit Try(std::exception_ptr ep) : hasValue_(false) {
339 std::rethrow_exception(ep);
340 } catch (const std::exception& e) {
341 e_ = exception_wrapper(std::current_exception(), e);
343 e_ = exception_wrapper(std::current_exception());
348 Try& operator=(const Try<void>& t) {
349 hasValue_ = t.hasValue_;
354 Try(const Try<void>& t) {
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(); }
363 // If the Try contains an exception, throws it
364 inline void throwIfFailed() const;
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_; }
371 // @returns True if the Try contains an exception of type Ex, false otherwise
373 bool hasException() const {
374 return hasException() && e_.is_compatible_with<Ex>();
378 * @throws TryException if the Try doesn't contain an exception
380 * @returns mutable reference to the exception contained by this Try
382 exception_wrapper& exception() {
383 if (UNLIKELY(!hasException())) {
384 throw TryException("exception(): Try does not contain an exception");
389 const exception_wrapper& exception() const {
390 if (UNLIKELY(!hasException())) {
391 throw TryException("exception(): Try does not contain an exception");
397 * @returns a pointer to the `std::exception` held by `*this`, if one is held;
398 * otherwise, returns `nullptr`.
400 std::exception* tryGetExceptionObject() {
401 return hasException() ? e_.get_exception() : nullptr;
403 std::exception const* tryGetExceptionObject() const {
404 return hasException() ? e_.get_exception() : nullptr;
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,
413 E* tryGetExceptionObject() {
414 return hasException() ? e_.get_exception<E>() : nullptr;
417 E const* tryGetExceptionObject() const {
418 return hasException() ? e_.get_exception<E>() : nullptr;
422 * If the Try contains an exception and it is of type Ex, execute func(Ex)
424 * @param func a function that takes a single parameter of type const Ex&
426 * @returns True if the Try held an Ex and func was executed, false otherwise
428 template <class Ex, class F>
429 bool withException(F func) {
430 if (!hasException()) {
433 return e_.with_exception<Ex>(std::move(func));
435 template <class Ex, class F>
436 bool withException(F func) const {
437 if (!hasException()) {
440 return e_.with_exception<Ex>(std::move(func));
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)
447 * @param func a function that takes a single parameter of type const Ex&
449 * @returns True if the Try held an Ex and func was executed, false otherwise
452 bool withException(F func) {
453 if (!hasException()) {
456 return e_.with_exception(std::move(func));
459 bool withException(F func) const {
460 if (!hasException()) {
463 return e_.with_exception(std::move(func));
466 template <bool, typename R>
468 return std::forward<R>(*this);
473 exception_wrapper e_;
477 * Extracts value from try and returns it. Throws if try contained an exception.
479 * @param t Try to extract value from
481 * @returns value contained in t
483 template <typename T>
484 T moveFromTry(Try<T>& t);
487 * Throws if try contained an exception.
489 * @param t Try to move from
491 void moveFromTry(Try<void>& t);
494 * @param f a function to execute and capture the result of (value or exception)
496 * @returns Try holding the result of f
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
505 * Specialization of makeTryWith for void return
507 * @param f a function to execute and capture the result of
509 * @returns Try<void> holding the result of f
511 template <typename F>
512 typename std::enable_if<
513 std::is_same<typename std::result_of<F()>::type, void>::value,
517 template <typename... Ts>
518 std::tuple<Ts...> unwrapTryTuple(std::tuple<folly::Try<Ts>...>&& ts);
522 #include <folly/Try-inl.h>