From e1c576b407c95030c8d1a063bebd27dd6dc89980 Mon Sep 17 00:00:00 2001 From: Hans Fugal Date: Tue, 10 Feb 2015 12:24:07 -0800 Subject: [PATCH] futures::chain Summary: `futures::chain(a, b, c, d, e, f, ..., z)` where `a` is a callback suitable for `Future::then` and `z` is a callback suitable for `Future::then`. 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 --- folly/futures/Future-inl.h | 30 ++++++++++++++++++++++++++++++ folly/futures/Future.h | 13 +++++++++++++ folly/futures/test/ViaTest.cpp | 17 +++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/folly/futures/Future-inl.h b/folly/futures/Future-inl.h index 21a8134d..57864681 100644 --- a/folly/futures/Future-inl.h +++ b/folly/futures/Future-inl.h @@ -768,8 +768,38 @@ Future&& Future::waitVia(DrivableExecutor* e) && { return std::move(*this); } +namespace futures { + + namespace { + template + Future chainHelper(F, Callbacks...); + + template + Future chainHelper(Future f) { + return f; + } + + template + Future chainHelper(F f, Fn fn, Callbacks... fns) { + return chainHelper(f.then(fn), fns...); + } + } + + template + std::function(Try)> + chain(Callbacks... fns) { + MoveWrapper> pw; + MoveWrapper> fw(chainHelper(pw->getFuture(), fns...)); + return [=](Try t) mutable { + pw->fulfilTry(std::move(t)); + return std::move(*fw); + }; + } + } +} // namespace folly + // I haven't included a Future specialization because I don't forsee us // using it, however it is not difficult to add when needed. Refer to // Future for guidance. std::future and boost::future code would also be diff --git a/folly/futures/Future.h b/folly/futures/Future.h index 695c5282..c018a9e7 100644 --- a/folly/futures/Future.h +++ b/folly/futures/Future.h @@ -166,6 +166,19 @@ namespace futures { /// needed. If your program never uses any timeouts or other time-based /// Futures you will pay no Timekeeper thread overhead. Future 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 and the result of the chain is a Future + /// becomes + /// + /// f.then(chain(a, b, c)); + // If anyone figures how to get chain to deduce A and Z, I'll buy you a drink. + template + std::function(Try)> + chain(Callbacks... fns); } template diff --git a/folly/futures/test/ViaTest.cpp b/folly/futures/test/ViaTest.cpp index 5a76f3c1..5cb66827 100644 --- a/folly/futures/test/ViaTest.cpp +++ b/folly/futures/test/ViaTest.cpp @@ -183,3 +183,20 @@ TEST_F(ViaFixture, viaAssignment) { // via()& auto f2 = f.via(eastExecutor.get()); } + +TEST(Via, chain1) { + EXPECT_EQ(42, + makeFuture() + .then(futures::chain([] { return 42; })) + .get()); +} + +TEST(Via, chain3) { + int count = 0; + auto f = makeFuture().then(futures::chain( + [&]{ count++; return 3.14159; }, + [&](double) { count++; return std::string("hello"); }, + [&]{ count++; return makeFuture(42); })); + EXPECT_EQ(42, f.get()); + EXPECT_EQ(3, count); +} -- 2.34.1