Commit e1c576b4 authored by Hans Fugal's avatar Hans Fugal Committed by Sara Golemon

futures::chain

Summary:
`futures::chain<A,Z>(a, b, c, d, e, f, ..., z)` where `a` is a callback suitable for `Future<A>::then` and `z` is a callback suitable for `Future<Y>::then<Z>`.

This will be important, at least in the background, for the new `via`. It will probably also be useful to some user sometime.

I imagine this will be tweaked over time if people find magic ways to get it to deduce the types better. But this works and it's not *too* much trouble to specify A and Z.

Test Plan: unit

Reviewed By: davejwatson@fb.com

Subscribers: trunkagent, exa, folly-diffs@, yfeldblum, jsedgwick

FB internal diff: D1831073

Tasks: 6048744

Signature: t1:1831073:1423259292:711be0e047a2acb706fd74e529d5d5fb6abda566
parent 9f502239
...@@ -768,8 +768,38 @@ Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && { ...@@ -768,8 +768,38 @@ Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
return std::move(*this); return std::move(*this);
} }
namespace futures {
namespace {
template <class Z, class F, class... Callbacks>
Future<Z> chainHelper(F, Callbacks...);
template <class Z>
Future<Z> chainHelper(Future<Z> f) {
return f;
}
template <class Z, class F, class Fn, class... Callbacks>
Future<Z> chainHelper(F f, Fn fn, Callbacks... fns) {
return chainHelper<Z>(f.then(fn), fns...);
}
}
template <class A, class Z, class... Callbacks>
std::function<Future<Z>(Try<A>)>
chain(Callbacks... fns) {
MoveWrapper<Promise<A>> pw;
MoveWrapper<Future<Z>> fw(chainHelper<Z>(pw->getFuture(), fns...));
return [=](Try<A> t) mutable {
pw->fulfilTry(std::move(t));
return std::move(*fw);
};
}
} }
} // namespace folly
// I haven't included a Future<T&> specialization because I don't forsee us // I haven't included a Future<T&> specialization because I don't forsee us
// using it, however it is not difficult to add when needed. Refer to // using it, however it is not difficult to add when needed. Refer to
// Future<void> for guidance. std::future and boost::future code would also be // Future<void> for guidance. std::future and boost::future code would also be
......
...@@ -166,6 +166,19 @@ namespace futures { ...@@ -166,6 +166,19 @@ namespace futures {
/// needed. If your program never uses any timeouts or other time-based /// needed. If your program never uses any timeouts or other time-based
/// Futures you will pay no Timekeeper thread overhead. /// Futures you will pay no Timekeeper thread overhead.
Future<void> sleep(Duration, Timekeeper* = nullptr); Future<void> sleep(Duration, Timekeeper* = nullptr);
/// Create a Future chain from a sequence of callbacks. i.e.
///
/// f.then(a).then(b).then(c);
///
/// where f is a Future<A> and the result of the chain is a Future<Z>
/// becomes
///
/// f.then(chain<A,Z>(a, b, c));
// If anyone figures how to get chain to deduce A and Z, I'll buy you a drink.
template <class A, class Z, class... Callbacks>
std::function<Future<Z>(Try<A>)>
chain(Callbacks... fns);
} }
template <class T> template <class T>
......
...@@ -183,3 +183,20 @@ TEST_F(ViaFixture, viaAssignment) { ...@@ -183,3 +183,20 @@ TEST_F(ViaFixture, viaAssignment) {
// via()& // via()&
auto f2 = f.via(eastExecutor.get()); auto f2 = f.via(eastExecutor.get());
} }
TEST(Via, chain1) {
EXPECT_EQ(42,
makeFuture()
.then(futures::chain<void, int>([] { return 42; }))
.get());
}
TEST(Via, chain3) {
int count = 0;
auto f = makeFuture().then(futures::chain<void, int>(
[&]{ count++; return 3.14159; },
[&](double) { count++; return std::string("hello"); },
[&]{ count++; return makeFuture(42); }));
EXPECT_EQ(42, f.get());
EXPECT_EQ(3, count);
}
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