Commit 4008ab21 authored by Jacob Lacouture's avatar Jacob Lacouture Committed by Facebook Github Bot

Future<Try<T>> -> Future<T>

Summary:
future.then([] -> T)  currently returns Future<T>
future.then([] -> Try<T>)  currently returns Future<Try<T>>

This changes that behavior, such that future.then([] -> Try<T>) will
return Future<T>.

This is a more desirable interface for futures. It also simplifies
implementation of a separate task (propagating exceptions more
efficiently through chained callbacks) because it enables us to internally
substitute lambdas returning T with lambdas returning Try<T>.

Reviewed By: yfeldblum, ryantimwilson

Differential Revision: D14318624

fbshipit-source-id: 51363e82ceb86bfecf87ae661188be9ca3dd8002
parent d10d3f04
......@@ -219,7 +219,8 @@ void Try<void>::throwIfFailed() const {
template <typename F>
typename std::enable_if<
!std::is_same<invoke_result_t<F>, void>::value,
!std::is_same<invoke_result_t<F>, void>::value &&
!isTry<invoke_result_t<F>>::value,
Try<invoke_result_t<F>>>::type
makeTryWith(F&& f) {
using ResultType = invoke_result_t<F>;
......@@ -246,6 +247,20 @@ typename std::
}
}
template <typename F>
typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>::
type
makeTryWith(F&& f) {
using ResultType = invoke_result_t<F>;
try {
return f();
} catch (std::exception& e) {
return ResultType(exception_wrapper(std::current_exception(), e));
} catch (...) {
return ResultType(exception_wrapper(std::current_exception()));
}
}
template <typename T, typename... Args>
T* tryEmplace(Try<T>& t, Args&&... args) noexcept {
try {
......
......@@ -585,6 +585,12 @@ class Try<void> {
};
};
template <typename T>
struct isTry : std::false_type {};
template <typename T>
struct isTry<Try<T>> : std::true_type {};
/*
* @param f a function to execute and capture the result of (value or exception)
*
......@@ -592,7 +598,8 @@ class Try<void> {
*/
template <typename F>
typename std::enable_if<
!std::is_same<invoke_result_t<F>, void>::value,
!std::is_same<invoke_result_t<F>, void>::value &&
!isTry<invoke_result_t<F>>::value,
Try<invoke_result_t<F>>>::type
makeTryWith(F&& f);
......@@ -608,6 +615,20 @@ typename std::
enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type
makeTryWith(F&& f);
/*
* Specialization of makeTryWith for functions that return Try<T>
* Causes makeTryWith to not double-wrap the try.
*
* @param f a function to execute and capture the result of
*
* @returns result of f if f did not throw. Otherwise Try<T> containing
* exception
*/
template <typename F>
typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>::
type
makeTryWith(F&& f);
/*
* Try to in-place construct a new value from the specified arguments.
*
......
......@@ -51,6 +51,12 @@ struct isFutureOrSemiFuture : std::false_type {
using Return = Inner;
};
template <typename T>
struct isFutureOrSemiFuture<Try<T>> : std::false_type {
using Inner = lift_unit_t<T>;
using Return = Inner;
};
template <typename T>
struct isFutureOrSemiFuture<Future<T>> : std::true_type {
typedef T Inner;
......@@ -58,16 +64,22 @@ struct isFutureOrSemiFuture<Future<T>> : std::true_type {
};
template <typename T>
struct isFutureOrSemiFuture<SemiFuture<T>> : std::true_type {
struct isFutureOrSemiFuture<Future<Try<T>>> : std::true_type {
typedef T Inner;
using Return = SemiFuture<Inner>;
using Return = Future<Inner>;
};
template <typename T>
struct isTry : std::false_type {};
struct isFutureOrSemiFuture<SemiFuture<T>> : std::true_type {
typedef T Inner;
using Return = SemiFuture<Inner>;
};
template <typename T>
struct isTry<Try<T>> : std::true_type {};
struct isFutureOrSemiFuture<SemiFuture<Try<T>>> : std::true_type {
typedef T Inner;
using Return = SemiFuture<Inner>;
};
namespace futures {
namespace detail {
......
......@@ -34,7 +34,7 @@ namespace folly {
* callbacks on the same Future (which is indefinitely unsupported), consider
* refactoring to use SharedPromise to "split" the Future.
*
* The ShardPromise must be kept alive manually. Consider FutureSplitter for
* The SharedPromise must be kept alive manually. Consider FutureSplitter for
* automatic lifetime management.
*/
template <class T>
......
......@@ -694,7 +694,7 @@ TEST(SemiFuture, MakeSemiFutureFromFutureWithTry) {
});
p.setException(make_exception_wrapper<std::logic_error>("Try"));
auto tryResult = std::move(sf).get();
ASSERT_EQ(tryResult.value(), "Try");
ASSERT_EQ(tryResult, "Try");
}
namespace {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment