/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2014-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#pragma once
#include <folly/ExceptionWrapper.h>
#include <folly/Memory.h>
#include <folly/Portability.h>
#include <folly/Unit.h>
+#include <folly/Utility.h>
#include <exception>
#include <stdexcept>
#include <type_traits>
namespace folly {
-class TryException : public std::logic_error {
+class FOLLY_EXPORT TryException : public std::logic_error {
public:
using std::logic_error::logic_error;
};
-class UsingUninitializedTry : public TryException {
+class FOLLY_EXPORT UsingUninitializedTry : public TryException {
public:
- UsingUninitializedTry() : TryException("Using unitialized try") {}
+ UsingUninitializedTry() : TryException("Using uninitialized try") {}
};
+namespace try_detail {
+[[noreturn]] void throwTryDoesNotContainException();
+[[noreturn]] void throwUsingUninitializedTry();
+} // namespace try_detail
+
/*
* Try<T> is a wrapper that contains either an instance of T, an exception, or
* nothing. Exceptions are stored as exception_wrappers so that the user can
*/
explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
+ template <typename... Args>
+ explicit Try(in_place_t, Args&&... args) noexcept(
+ noexcept(::new (nullptr) T(std::declval<Args&&>()...)))
+ : contains_(Contains::VALUE), value_(std::forward<Args>(args)...) {}
+
/// Implicit conversion from Try<void> to Try<Unit>
template <class T2 = T>
/* implicit */
* @param e The exception_wrapper
*/
explicit Try(exception_wrapper e)
- : contains_(Contains::EXCEPTION),
- e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+ : contains_(Contains::EXCEPTION), e_(std::move(e)) {}
/*
* DEPRECATED
*/
FOLLY_DEPRECATED("use Try(exception_wrapper)")
explicit Try(std::exception_ptr ep)
- : contains_(Contains::EXCEPTION) {
- try {
- std::rethrow_exception(ep);
- } catch (const std::exception& e) {
- e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
- } catch (...) {
- e_ = folly::make_unique<exception_wrapper>(std::current_exception());
- }
- }
+ : contains_(Contains::EXCEPTION),
+ e_(exception_wrapper::from_exception_ptr(ep)) {}
// Move constructor
Try(Try<T>&& t) noexcept;
*
* @returns mutable reference to the contained value
*/
- T& value()&;
+ T& value() &;
/*
* Get a rvalue reference to the contained value. If the Try contains an
* exception it will be rethrown.
*
* @returns rvalue reference to the contained value
*/
- T&& value()&&;
+ T&& value() &&;
/*
* Get a const reference to the contained value. If the Try contains an
* exception it will be rethrown.
*
* @returns const reference to the contained value
*/
- const T& value() const&;
+ const T& value() const &;
+ /*
+ * Get a const rvalue reference to the contained value. If the Try contains an
+ * exception it will be rethrown.
+ *
+ * @returns const rvalue reference to the contained value
+ */
+ const T&& value() const &&;
/*
* If the Try contains an exception, rethrow it. Otherwise do nothing.
*
* @returns const reference to the contained value
*/
- const T& operator*() const { return value(); }
+ const T& operator*() const & {
+ return value();
+ }
/*
* Dereference operator. If the Try contains an exception it will be rethrown.
*
* @returns mutable reference to the contained value
*/
- T& operator*() { return value(); }
+ T& operator*() & {
+ return value();
+ }
+ /*
+ * Mutable rvalue dereference operator. If the Try contains an exception it
+ * will be rethrown.
+ *
+ * @returns rvalue reference to the contained value
+ */
+ T&& operator*() && {
+ return std::move(value());
+ }
+ /*
+ * Const rvalue dereference operator. If the Try contains an exception it
+ * will be rethrown.
+ *
+ * @returns rvalue reference to the contained value
+ */
+ const T&& operator*() const && {
+ return std::move(value());
+ }
/*
* Const arrow operator. If the Try contains an exception it will be
*/
template <class Ex>
bool hasException() const {
- return hasException() && e_->is_compatible_with<Ex>();
+ return hasException() && e_.is_compatible_with<Ex>();
}
- exception_wrapper& exception() {
- if (UNLIKELY(!hasException())) {
- throw TryException("exception(): Try does not contain an exception");
+ exception_wrapper& exception() & {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
+ }
+ return e_;
+ }
+
+ exception_wrapper&& exception() && {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
+ }
+ return std::move(e_);
+ }
+
+ const exception_wrapper& exception() const & {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
}
- return *e_;
+ return e_;
}
- const exception_wrapper& exception() const {
- if (UNLIKELY(!hasException())) {
- throw TryException("exception(): Try does not contain an exception");
+ const exception_wrapper&& exception() const && {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
}
- return *e_;
+ return std::move(e_);
+ }
+
+ /*
+ * @returns a pointer to the `std::exception` held by `*this`, if one is held;
+ * otherwise, returns `nullptr`.
+ */
+ std::exception* tryGetExceptionObject() {
+ return hasException() ? e_.get_exception() : nullptr;
+ }
+ std::exception const* tryGetExceptionObject() const {
+ return hasException() ? e_.get_exception() : nullptr;
+ }
+
+ /*
+ * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
+ * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
+ * returns `nullptr`.
+ */
+ template <class E>
+ E* tryGetExceptionObject() {
+ return hasException() ? e_.get_exception<E>() : nullptr;
+ }
+ template <class E>
+ E const* tryGetExceptionObject() const {
+ return hasException() ? e_.get_exception<E>() : nullptr;
}
/*
* @returns True if the Try held an Ex and func was executed, false otherwise
*/
template <class Ex, class F>
+ bool withException(F func) {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception<Ex>(std::move(func));
+ }
+ template <class Ex, class F>
bool withException(F func) const {
if (!hasException()) {
return false;
}
- return e_->with_exception(std::move(func));
+ return e_.with_exception<Ex>(std::move(func));
+ }
+
+ /*
+ * If the Try contains an exception and it is of type compatible with Ex as
+ * deduced from the first parameter of func, execute func(Ex)
+ *
+ * @param func a function that takes a single parameter of type const Ex&
+ *
+ * @returns True if the Try held an Ex and func was executed, false otherwise
+ */
+ template <class F>
+ bool withException(F func) {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception(std::move(func));
+ }
+ template <class F>
+ bool withException(F func) const {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception(std::move(func));
}
template <bool isTry, typename R>
Contains contains_;
union {
T value_;
- std::unique_ptr<exception_wrapper> e_;
+ exception_wrapper e_;
};
};
*
* @param e The exception_wrapper
*/
- explicit Try(exception_wrapper e)
- : hasValue_(false),
- e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+ explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {}
/*
* DEPRECATED
* @param ep The exception_pointer. Will be rethrown.
*/
FOLLY_DEPRECATED("use Try(exception_wrapper)")
- explicit Try(std::exception_ptr ep) : hasValue_(false) {
- try {
- std::rethrow_exception(ep);
- } catch (const std::exception& e) {
- e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
- } catch (...) {
- e_ = folly::make_unique<exception_wrapper>(std::current_exception());
- }
- }
+ explicit Try(std::exception_ptr ep)
+ : hasValue_(false), e_(exception_wrapper::from_exception_ptr(ep)) {}
// Copy assigner
Try& operator=(const Try<void>& t) {
hasValue_ = t.hasValue_;
- if (t.e_) {
- e_ = folly::make_unique<exception_wrapper>(*t.e_);
- }
+ e_ = t.e_;
return *this;
}
// Copy constructor
// @returns True if the Try contains an exception of type Ex, false otherwise
template <class Ex>
bool hasException() const {
- return hasException() && e_->is_compatible_with<Ex>();
+ return hasException() && e_.is_compatible_with<Ex>();
}
/*
*
* @returns mutable reference to the exception contained by this Try
*/
- exception_wrapper& exception() {
- if (UNLIKELY(!hasException())) {
- throw TryException("exception(): Try does not contain an exception");
+ exception_wrapper& exception() & {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
+ }
+ return e_;
+ }
+
+ exception_wrapper&& exception() && {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
+ }
+ return std::move(e_);
+ }
+
+ const exception_wrapper& exception() const & {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
}
- return *e_;
+ return e_;
}
- const exception_wrapper& exception() const {
- if (UNLIKELY(!hasException())) {
- throw TryException("exception(): Try does not contain an exception");
+ const exception_wrapper&& exception() const && {
+ if (!hasException()) {
+ try_detail::throwTryDoesNotContainException();
}
- return *e_;
+ return std::move(e_);
+ }
+
+ /*
+ * @returns a pointer to the `std::exception` held by `*this`, if one is held;
+ * otherwise, returns `nullptr`.
+ */
+ std::exception* tryGetExceptionObject() {
+ return hasException() ? e_.get_exception() : nullptr;
+ }
+ std::exception const* tryGetExceptionObject() const {
+ return hasException() ? e_.get_exception() : nullptr;
+ }
+
+ /*
+ * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose
+ * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,
+ * returns `nullptr`.
+ */
+ template <class E>
+ E* tryGetExceptionObject() {
+ return hasException() ? e_.get_exception<E>() : nullptr;
+ }
+ template <class E>
+ E const* tryGetExceptionObject() const {
+ return hasException() ? e_.get_exception<E>() : nullptr;
}
/*
* @returns True if the Try held an Ex and func was executed, false otherwise
*/
template <class Ex, class F>
+ bool withException(F func) {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception<Ex>(std::move(func));
+ }
+ template <class Ex, class F>
bool withException(F func) const {
if (!hasException()) {
return false;
}
- return e_->with_exception(std::move(func));
+ return e_.with_exception<Ex>(std::move(func));
+ }
+
+ /*
+ * If the Try contains an exception and it is of type compatible with Ex as
+ * deduced from the first parameter of func, execute func(Ex)
+ *
+ * @param func a function that takes a single parameter of type const Ex&
+ *
+ * @returns True if the Try held an Ex and func was executed, false otherwise
+ */
+ template <class F>
+ bool withException(F func) {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception(std::move(func));
+ }
+ template <class F>
+ bool withException(F func) const {
+ if (!hasException()) {
+ return false;
+ }
+ return e_.with_exception(std::move(func));
}
template <bool, typename R>
private:
bool hasValue_;
- std::unique_ptr<exception_wrapper> e_{nullptr};
+ exception_wrapper e_;
};
-/*
- * Extracts value from try and returns it. Throws if try contained an exception.
- *
- * @param t Try to extract value from
- *
- * @returns value contained in t
- */
-template <typename T>
-T moveFromTry(Try<T>& t);
-
-/*
- * Throws if try contained an exception.
- *
- * @param t Try to move from
- */
-void moveFromTry(Try<void>& t);
-
/*
* @param f a function to execute and capture the result of (value or exception)
*
Try<void>>::type
makeTryWith(F&& f);
-template <typename... Ts>
-std::tuple<Ts...> unwrapTryTuple(std::tuple<folly::Try<Ts>...>&& ts);
+/**
+ * Tuple<Try<Type>...> -> std::tuple<Type...>
+ *
+ * Unwraps a tuple-like type containing a sequence of Try<Type> instances to
+ * std::tuple<Type>
+ */
+template <typename Tuple>
+auto unwrapTryTuple(Tuple&&);
-} // folly
+} // namespace folly
#include <folly/Try-inl.h>