Summary: makeFutureWith executes a function and returns a future containing
set to the function's return value. This diff adds a specialisation
for the case when the function returns some type Future<T>. Instead
of returning Future<Future<T>>, makeFutureWith now just passes
on the future that the function returned, which may or may not have
a value set at this time. In case the function throws,
makeFutureWith returns a Future<T> containing the exception.
With this diff, the following two lines produce equivalent results:
auto f1 = makeFutureWith(func);
auto f2 = makeFuture().then(func);
except for the fact that f2 requires an additional temporary
Future<Unit> to be created and destroyed.
Reviewed By: @fugalh
Differential Revision:
D2388335
return makeFuture(Unit{});
}
+// makeFutureWith(Future<T>()) -> Future<T>
template <class F>
-auto makeFutureWith(F&& func)
- -> Future<typename Unit::Lift<decltype(func())>::type> {
- using LiftedResult = typename Unit::Lift<decltype(func())>::type;
+typename std::enable_if<isFuture<typename std::result_of<F()>::type>::value,
+ typename std::result_of<F()>::type>::type
+makeFutureWith(F&& func) {
+ using InnerType =
+ typename isFuture<typename std::result_of<F()>::type>::Inner;
+ try {
+ return func();
+ } catch (std::exception& e) {
+ return makeFuture<InnerType>(
+ exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ return makeFuture<InnerType>(exception_wrapper(std::current_exception()));
+ }
+}
+
+// makeFutureWith(T()) -> Future<T>
+// makeFutureWith(void()) -> Future<Unit>
+template <class F>
+typename std::enable_if<
+ !(isFuture<typename std::result_of<F()>::type>::value),
+ Future<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type
+makeFutureWith(F&& func) {
+ using LiftedResult =
+ typename Unit::Lift<typename std::result_of<F()>::type>::type;
return makeFuture<LiftedResult>(makeTryWith([&func]() mutable {
return func();
}));
/** Make a completed void Future. */
Future<Unit> makeFuture();
-/** Make a completed Future by executing a function. If the function throws
- we capture the exception, otherwise we capture the result. */
+/**
+ Make a Future by executing a function.
+
+ If the function returns a value of type T, makeFutureWith
+ returns a completed Future<T>, capturing the value returned
+ by the function.
+
+ If the function returns a Future<T> already, makeFutureWith
+ returns just that.
+
+ Either way, if the function throws, a failed Future is
+ returned that captures the exception.
+
+ Calling makeFutureWith(func) is equivalent to calling
+ makeFuture().then(func).
+*/
+
+// makeFutureWith(Future<T>()) -> Future<T>
+template <class F>
+typename std::enable_if<isFuture<typename std::result_of<F()>::type>::value,
+ typename std::result_of<F()>::type>::type
+makeFutureWith(F&& func);
+
+// makeFutureWith(T()) -> Future<T>
+// makeFutureWith(void()) -> Future<Unit>
template <class F>
-auto makeFutureWith(F&& func)
- -> Future<typename Unit::Lift<decltype(func())>::type>;
+typename std::enable_if<
+ !(isFuture<typename std::result_of<F()>::type>::value),
+ Future<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type
+makeFutureWith(F&& func);
/// Make a failed Future from an exception_ptr.
/// Because the Future's type cannot be inferred you have to specify it, e.g.
EXPECT_TYPE(makeFutureWith(fun), Future<int>);
EXPECT_EQ(42, makeFutureWith(fun).value());
+ auto funf = [] { return makeFuture<int>(43); };
+ EXPECT_TYPE(makeFutureWith(funf), Future<int>);
+ EXPECT_EQ(43, makeFutureWith(funf).value());
+
auto failfun = []() -> int { throw eggs; };
EXPECT_TYPE(makeFutureWith(failfun), Future<int>);
+ EXPECT_NO_THROW(makeFutureWith(failfun));
EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);
+ auto failfunf = []() -> Future<int> { throw eggs; };
+ EXPECT_TYPE(makeFutureWith(failfunf), Future<int>);
+ EXPECT_NO_THROW(makeFutureWith(failfunf));
+ EXPECT_THROW(makeFutureWith(failfunf).value(), eggs_t);
+
EXPECT_TYPE(makeFuture(), Future<Unit>);
}