(Wangle) Support .then(function pointer) syntax
authorHannes Roth <hannesr@fb.com>
Fri, 7 Mar 2014 16:02:11 +0000 (08:02 -0800)
committerDave Watson <davejwatson@fb.com>
Mon, 10 Mar 2014 20:51:03 +0000 (13:51 -0700)
Summary:
Support `.then(&static_function)`, `.then(&static_member_function)`, `.then(&Class::static_member_function)`, `.then(instance, &Class::member_function)` in Wangle.

C++ does not allow `.then(instance, &member_function)`, sadly.

This implementation just creates the closure for you and defers to the existing `then` implementations.

Test Plan: Added a test.

Reviewed By: hans@fb.com

FB internal diff: D1204954

folly/wangle/Future.h
folly/wangle/test/FutureTest.cpp

index d89f7134092f538c6f957ff1dd8baf5be3d60118..3b13b194bdfef95178c53ac3a4ce45dc4d6adbbf 100644 (file)
@@ -100,7 +100,7 @@ class Future {
     Future<typename std::result_of<F(Try<T>&&)>::type> >::type
   then(F&& func);
 
-  /// Variant where func returns a future<T> instead of a T. e.g.
+  /// Variant where func returns a Future<T> instead of a T. e.g.
   ///
   ///   Future<string> f2 = f1.then(
   ///     [](Try<T>&&) { return makeFuture<string>("foo"); });
@@ -110,6 +110,70 @@ class Future {
     Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
   then(F&& func);
 
+  /// Variant where func is an ordinary function (static method, method)
+  ///
+  ///   R doWork(Try<T>&&);
+  ///
+  ///   Future<R> f2 = f1.then(doWork);
+  ///
+  /// or
+  ///
+  ///   struct Worker {
+  ///     static R doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func is an member function
+  ///
+  ///   struct Worker {
+  ///     R doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
   /// Convenience method for ignoring the value and creating a Future<void>.
   /// Exceptions still propagate.
   Future<void> then();
index e5906058f1a33e37d0ebd6cfba961b9195d06ddf..8afbdc177bd259c40972fe56313c692ed54f7593 100644 (file)
@@ -91,6 +91,50 @@ TEST(Future, then) {
   EXPECT_TRUE(f.isReady());
 }
 
+static string doWorkStatic(Try<string>&& t) {
+  return t.value() + ";static";
+}
+
+TEST(Future, thenFunction) {
+  struct Worker {
+    string doWork(Try<string>&& t) {
+      return t.value() + ";class";
+    }
+    static string doWorkStatic(Try<string>&& t) {
+      return t.value() + ";class-static";
+    }
+  } w;
+
+  auto f = makeFuture<string>("start")
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&w, &Worker::doWork);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+static Future<string> doWorkStaticFuture(Try<string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Future, thenFunctionFuture) {
+  struct Worker {
+    Future<string> doWorkFuture(Try<string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<string> doWorkStaticFuture(Try<string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture<string>("start")
+    .then(doWorkStaticFuture)
+    .then(Worker::doWorkStaticFuture)
+    .then(&w, &Worker::doWorkFuture);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
 TEST(Future, value) {
   auto f = makeFuture(unique_ptr<int>(new int(42)));
   auto up = std::move(f.value());