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