Commit bf814b82 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Separate Try-taking defer and value-taking deferValue in SemiFuture.

Summary: This avoids ambiguity with an auto-parameterised lambda and leads on to a consistent use of deferError in a followup diff.

Reviewed By: yfeldblum

Differential Revision: D7224490

fbshipit-source-id: df410cd97ca0991db8fcb46267dd6a4236f3f6a3
parent 387fc2ca
......@@ -718,7 +718,8 @@ inline Future<T> SemiFuture<T>::toUnsafeFuture() && {
template <class T>
template <typename F>
SemiFuture<typename futures::detail::callableResult<T, F>::Return::value_type>
SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
SemiFuture<T>::defer(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
......@@ -734,6 +735,19 @@ SemiFuture<T>::defer(F&& func) && {
return sf;
}
template <class T>
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
SemiFuture<T>::deferValue(F&& func) && {
return std::move(*this).defer(
[f = std::forward<F>(func)](folly::Try<T>&& t) mutable {
return f(t.template get<
false,
typename futures::detail::Extract<F>::FirstArg>());
});
}
template <class T>
Future<T> Future<T>::makeEmpty() {
return Future<T>(futures::detail::EmptyConstruct{});
......
......@@ -130,6 +130,32 @@ struct callableResult {
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename T, typename F>
struct deferCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, Try<T>&&>::value,
detail::argResult<true, F, Try<T>&&>,
detail::argResult<true, F, Try<T>&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename T, typename F>
struct deferValueCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, T&&>::value,
detail::argResult<false, F, T&&>,
detail::argResult<false, F, T&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename L>
struct Extract : Extract<decltype(&L::operator())> { };
......
......@@ -315,6 +315,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/**
* Defer work to run on the consumer of the future.
* Function must take a Try as a parameter.
* This work will be run eithe ron an executor that the caller sets on the
* SemiFuture, or inline with the call to .get().
* NB: This is a custom method because boost-blocking executors is a
......@@ -323,9 +324,20 @@ class SemiFuture : private futures::detail::FutureBase<T> {
* of driveable executor here.
*/
template <typename F>
SemiFuture<typename futures::detail::callableResult<T, F>::Return::value_type>
SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
defer(F&& func) &&;
/**
* Defer for functions taking a T rather than a Try<T>.
*/
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
deferValue(F&& func) &&;
// TODO: OnError
/// Return a future that completes inline, as if the future had no executor.
/// Intended for porting legacy code without behavioural change, and for rare
/// cases where this is really the intended behaviour.
......
......@@ -478,7 +478,7 @@ TEST(SemiFuture, SimpleDeferWithValue) {
std::atomic<int> innerResult{0};
Promise<int> p;
auto f = p.getSemiFuture().toUnsafeFuture();
auto sf = std::move(f).semi().defer([&](int a) { innerResult = a; });
auto sf = std::move(f).semi().deferValue([&](int a) { innerResult = a; });
p.setValue(7);
// Run "F" here inline in the calling thread
std::move(sf).get();
......@@ -491,7 +491,7 @@ TEST(SemiFuture, ChainingDefertoThenWithValue) {
EventBase e2;
Promise<int> p;
auto f = p.getSemiFuture().toUnsafeFuture();
auto sf = std::move(f).semi().defer([&](int a) {
auto sf = std::move(f).semi().deferValue([&](int a) {
innerResult = a;
return a;
});
......@@ -528,7 +528,7 @@ TEST(SemiFuture, DeferWithinContinuation) {
auto resultF = std::move(f).then([&, p3 = std::move(p2)](int outer) mutable {
result = outer;
return makeSemiFuture<int>(std::move(outer))
.defer([&, p4 = std::move(p3)](int inner) mutable {
.deferValue([&, p4 = std::move(p3)](int inner) mutable {
innerResult = inner;
p4.setValue(inner);
return inner;
......
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