Commit 2c23497f authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Consistency with folly::SemiFuture continuations 1/n: Add Future<T>::thenValue.

Summary:
Step 1 in adding full set of r-value-qualified unambiguous continuation methods to folly::Future for consistency with folly::SemiFuture.

 * Add r-value qualified Future<T>::thenValue.
 * Make callableResult structs consistently named so they work for Future and SemiFuture.
 * Correct call behaviour for consistency with invokeCallbackReturningFutureAsRvalue test.

Reviewed By: yfeldblum

Differential Revision: D7944603

fbshipit-source-id: 081383a91749310472a8106289671cffa6139691
parent 48eee7b8
......@@ -747,8 +747,7 @@ Future<T> SemiFuture<T>::toUnsafeFuture() && {
template <class T>
template <typename F>
SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
SemiFuture<T>::defer(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
......@@ -766,15 +765,14 @@ SemiFuture<T>::defer(F&& func) && {
template <class T>
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
SemiFuture<T>::deferValue(F&& func) && {
return std::move(*this).defer(
[f = std::forward<F>(func)](folly::Try<T>&& t) mutable {
return f(t.template get<
false,
typename futures::detail::Extract<F>::FirstArg>());
});
return std::move(*this).defer([f = std::forward<F>(func)](
folly::Try<T>&& t) mutable {
return f(t.template get<
false,
typename futures::detail::valueCallableResult<T, F>::FirstArg>());
});
}
template <class T>
......@@ -953,9 +951,22 @@ Future<T>::then(R(Caller::*func)(Args...), Caller *instance) {
});
}
template <class T>
template <typename F>
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
Future<T>::thenValue(F&& func) && {
return std::move(*this).then([f = std::forward<F>(func)](
folly::Try<T>&& t) mutable {
return std::forward<F>(f)(
t.template get<
false,
typename futures::detail::valueCallableResult<T, F>::FirstArg>());
});
}
template <class T>
Future<Unit> Future<T>::then() {
return then([] () {});
return then([]() {});
}
// onError where the callback returns T
......
......@@ -94,6 +94,7 @@ struct ArgType<> {
template <bool isTry, typename F, typename... Args>
struct argResult {
using ArgList = ArgType<Args...>;
using Result = resultOf<F, Args...>;
};
......@@ -131,7 +132,7 @@ struct callableResult {
};
template <typename T, typename F>
struct deferCallableResult {
struct tryCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
......@@ -140,11 +141,11 @@ struct deferCallableResult {
detail::argResult<true, F, Try<T>&&>,
detail::argResult<true, F, Try<T>&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner value_type;
};
template <typename T, typename F>
struct deferValueCallableResult {
struct valueCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
......@@ -153,7 +154,8 @@ struct deferValueCallableResult {
detail::argResult<false, F, T&&>,
detail::argResult<false, F, T&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner value_type;
typedef typename Arg::ArgList::FirstArg FirstArg;
};
template <typename L>
......
......@@ -419,16 +419,14 @@ class SemiFuture : private futures::detail::FutureBase<T> {
* of driveable executor here.
*/
template <typename F>
SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
defer(F&& func) &&;
/**
* Defer for functions taking a T rather than a Try<T>.
*/
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
deferValue(F&& func) &&;
/// Set an error callback for this SemiFuture. The callback should take a
......@@ -760,6 +758,18 @@ class Future : private futures::detail::FutureBase<T> {
.via(oldX);
}
/**
* Then for functions taking a T rather than a Try<T>.
*/
template <typename F>
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
thenValue(F&& func) &&;
template <typename R, typename... Args>
auto thenValue(R (&func)(Args...)) && {
return std::move(*this).thenValue(&func);
}
/// Convenience method for ignoring the value and creating a Future<Unit>.
/// Exceptions still propagate.
/// This function is identical to .unit().
......@@ -778,7 +788,7 @@ class Future : private futures::detail::FutureBase<T> {
/// below). For instance,
///
/// makeFuture()
/// .then([] {
/// .thenValue([] {
/// throw std::runtime_error("oh no!");
/// return 42;
/// })
......
......@@ -680,6 +680,10 @@ static std::string doWorkStatic(Try<std::string>&& t) {
return t.value() + ";static";
}
static std::string doWorkStaticValue(std::string&& t) {
return t + ";value";
}
TEST(Future, thenFunction) {
struct Worker {
std::string doWork(Try<std::string>&& t) {
......@@ -691,11 +695,13 @@ TEST(Future, thenFunction) {
} w;
auto f = makeFuture<std::string>("start")
.then(doWorkStatic)
.then(Worker::doWorkStatic)
.then(&Worker::doWork, &w);
.then(doWorkStatic)
.then(Worker::doWorkStatic)
.then(&Worker::doWork, &w)
.then(doWorkStaticValue)
.thenValue(doWorkStaticValue);
EXPECT_EQ(f.value(), "start;static;class-static;class");
EXPECT_EQ(f.value(), "start;static;class-static;class;value;value");
}
static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {
......@@ -731,6 +737,11 @@ TEST(Future, thenStdFunction) {
auto f = makeFuture(19).then(std::move(fn));
EXPECT_EQ(f.value(), 42);
}
{
std::function<int(int)> fn = [](int i) { return i + 23; };
auto f = makeFuture(19).thenValue(std::move(fn));
EXPECT_EQ(f.value(), 42);
}
{
std::function<int(Try<int>&)> fn = [](Try<int>& t){ return t.value() + 2; };
auto f = makeFuture(1).then(std::move(fn));
......@@ -1146,6 +1157,10 @@ TEST(Future, invokeCallbackReturningFutureAsRvalue) {
EXPECT_EQ(101, makeFuture<int>(100).then(foo).value());
EXPECT_EQ(202, makeFuture<int>(200).then(cfoo).value());
EXPECT_EQ(303, makeFuture<int>(300).then(Foo()).value());
EXPECT_EQ(101, makeFuture<int>(100).thenValue(foo).value());
EXPECT_EQ(202, makeFuture<int>(200).thenValue(cfoo).value());
EXPECT_EQ(303, makeFuture<int>(300).thenValue(Foo()).value());
}
TEST(Future, futureWithinCtxCleanedUpWhenTaskFinishedInTime) {
......
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