Commit 611cea25 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

DRY template args in Future::thenImplementation

Summary:
[Folly] DRY template args in `Future::thenImplementation`.

Explicitly passing the template arguments which are also the types of non-template arguments is weird. Either limit the explicit template arguments to those which are not also the types of non-template arguments, or pass everything as non-template arguments.

Reviewed By: andriigrynenko

Differential Revision: D10247910

fbshipit-source-id: 64a56c5ea2072803781143b5247145ec2ee09ae0
parent c3afb202
...@@ -279,21 +279,25 @@ FutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept ...@@ -279,21 +279,25 @@ FutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept
// be fixed for MSVC 2017 Update 8. // be fixed for MSVC 2017 Update 8.
// TODO: Remove. // TODO: Remove.
namespace detail_msvc_15_7_workaround { namespace detail_msvc_15_7_workaround {
template <bool isTry, typename State, typename T> template <typename R, std::size_t S>
decltype(auto) invoke(State& state, Try<T>& /* t */) { using IfArgsSizeIs = std::enable_if_t<R::Arg::ArgsSize::value == S, int>;
template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>
decltype(auto) invoke(R, State& state, Try<T>& /* t */) {
return state.invoke(); return state.invoke();
} }
template <bool isTry, typename State, typename T, typename Arg> template <typename R, typename State, typename T, IfArgsSizeIs<R, 1> = 0>
decltype(auto) invoke(State& state, Try<T>& t) { decltype(auto) invoke(R, State& state, Try<T>& t) {
return state.invoke(t.template get<isTry, Arg>()); using Arg0 = typename R::Arg::ArgList::FirstArg;
return state.invoke(t.template get<R::Arg::isTry(), Arg0>());
} }
template <bool isTry, typename State, typename T> template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>
decltype(auto) tryInvoke(State& state, Try<T>& /* t */) { decltype(auto) tryInvoke(R, State& state, Try<T>& /* t */) {
return state.tryInvoke(); return state.tryInvoke();
} }
template <bool isTry, typename State, typename T, typename Arg> template <typename R, typename State, typename T, IfArgsSizeIs<R, 1> = 0>
decltype(auto) tryInvoke(State& state, Try<T>& t) { decltype(auto) tryInvoke(R, State& state, Try<T>& t) {
return state.tryInvoke(t.template get<isTry, Arg>()); using Arg0 = typename R::Arg::ArgList::FirstArg;
return state.tryInvoke(t.template get<R::Arg::isTry(), Arg0>());
} }
} // namespace detail_msvc_15_7_workaround } // namespace detail_msvc_15_7_workaround
...@@ -302,12 +306,11 @@ decltype(auto) tryInvoke(State& state, Try<T>& t) { ...@@ -302,12 +306,11 @@ decltype(auto) tryInvoke(State& state, Try<T>& t) {
// Variant: returns a value // Variant: returns a value
// e.g. f.then([](Try<T>&& t){ return t.value(); }); // e.g. f.then([](Try<T>&& t){ return t.value(); });
template <class T> template <class T>
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
FutureBase<T>::thenImplementation( FutureBase<T>::thenImplementation(F&& func, R) {
F&& func, static_assert(
futures::detail::argResult<isTry, F, Args...>) { R::Arg::ArgsSize::value <= 1, "Then must take zero/one argument");
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
typedef typename R::ReturnsFuture::Inner B; typedef typename R::ReturnsFuture::Inner B;
Promise<B> p; Promise<B> p;
...@@ -351,12 +354,11 @@ FutureBase<T>::thenImplementation( ...@@ -351,12 +354,11 @@ FutureBase<T>::thenImplementation(
this->setCallback_( this->setCallback_(
[state = futures::detail::makeCoreCallbackState( [state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](Try<T>&& t) mutable { std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (!isTry && t.hasException()) { if (!R::Arg::isTry() && t.hasException()) {
state.setException(std::move(t.exception())); state.setException(std::move(t.exception()));
} else { } else {
state.setTry(makeTryWith([&] { state.setTry(makeTryWith([&] {
return detail_msvc_15_7_workaround:: return detail_msvc_15_7_workaround::invoke(R{}, state, t);
invoke<isTry, decltype(state), T, Args...>(state, t);
})); }));
} }
}); });
...@@ -381,12 +383,11 @@ Future<T> chainExecutor(Executor* e, SemiFuture<T>&& f) { ...@@ -381,12 +383,11 @@ Future<T> chainExecutor(Executor* e, SemiFuture<T>&& f) {
// Variant: returns a Future // Variant: returns a Future
// e.g. f.then([](T&& t){ return makeFuture<T>(t); }); // e.g. f.then([](T&& t){ return makeFuture<T>(t); });
template <class T> template <class T>
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
FutureBase<T>::thenImplementation( FutureBase<T>::thenImplementation(F&& func, R) {
F&& func, static_assert(
futures::detail::argResult<isTry, F, Args...>) { R::Arg::ArgsSize::value <= 1, "Then must take zero/one argument");
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
typedef typename R::ReturnsFuture::Inner B; typedef typename R::ReturnsFuture::Inner B;
Promise<B> p; Promise<B> p;
...@@ -402,13 +403,12 @@ FutureBase<T>::thenImplementation( ...@@ -402,13 +403,12 @@ FutureBase<T>::thenImplementation(
this->setCallback_([state = futures::detail::makeCoreCallbackState( this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))]( std::move(p), std::forward<F>(func))](
Try<T>&& t) mutable { Try<T>&& t) mutable {
if (!isTry && t.hasException()) { if (!R::Arg::isTry() && t.hasException()) {
state.setException(std::move(t.exception())); state.setException(std::move(t.exception()));
} else { } else {
// Ensure that if function returned a SemiFuture we correctly chain // Ensure that if function returned a SemiFuture we correctly chain
// potential deferral. // potential deferral.
auto tf2 = detail_msvc_15_7_workaround:: auto tf2 = detail_msvc_15_7_workaround::tryInvoke(R{}, state, t);
tryInvoke<isTry, decltype(state), T, Args...>(state, t);
if (tf2.hasException()) { if (tf2.hasException()) {
state.setException(std::move(tf2.exception())); state.setException(std::move(tf2.exception()));
} else { } else {
...@@ -455,8 +455,7 @@ FutureBase<T>::withinImplementation(Duration dur, E e, Timekeeper* tk) && { ...@@ -455,8 +455,7 @@ FutureBase<T>::withinImplementation(Duration dur, E e, Timekeeper* tk) && {
} }
}; };
using R = futures::detail::callableResult<T, decltype(f)>; using R = futures::detail::callableResult<T, decltype(f)>;
ctx->thisFuture = this->template thenImplementation<decltype(f), R>( ctx->thisFuture = this->thenImplementation(std::move(f), R{});
std::move(f), typename R::Arg());
// Properly propagate interrupt values through futures chained after within() // Properly propagate interrupt values through futures chained after within()
ctx->promise.setInterruptHandler( ctx->promise.setInterruptHandler(
...@@ -1036,8 +1035,7 @@ Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) & { ...@@ -1036,8 +1035,7 @@ Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) & {
p.setTry(std::move(t)); p.setTry(std::move(t));
}; };
using R = futures::detail::callableResult<T, decltype(func)>; using R = futures::detail::callableResult<T, decltype(func)>;
this->template thenImplementation<decltype(func), R>( this->thenImplementation(std::move(func), R{});
std::move(func), typename R::Arg());
// Construct future from semifuture manually because this may not have // Construct future from semifuture manually because this may not have
// an executor set due to legacy code. This means we can bypass the executor // an executor set due to legacy code. This means we can bypass the executor
// check in SemiFuture::via // check in SemiFuture::via
...@@ -1073,8 +1071,7 @@ Future<T>::thenTry(F&& func) && { ...@@ -1073,8 +1071,7 @@ Future<T>::thenTry(F&& func) && {
return std::forward<F>(f)(std::move(t)); return std::forward<F>(f)(std::move(t));
}; };
using R = futures::detail::tryCallableResult<T, decltype(lambdaFunc)>; using R = futures::detail::tryCallableResult<T, decltype(lambdaFunc)>;
return this->template thenImplementation<decltype(lambdaFunc), R>( return this->thenImplementation(std::move(lambdaFunc), R{});
std::move(lambdaFunc), typename R::Arg());
} }
template <class T> template <class T>
...@@ -1088,8 +1085,7 @@ Future<T>::thenValue(F&& func) && { ...@@ -1088,8 +1085,7 @@ Future<T>::thenValue(F&& func) && {
typename futures::detail::valueCallableResult<T, F>::FirstArg>()); typename futures::detail::valueCallableResult<T, F>::FirstArg>());
}; };
using R = futures::detail::tryCallableResult<T, decltype(lambdaFunc)>; using R = futures::detail::tryCallableResult<T, decltype(lambdaFunc)>;
return this->template thenImplementation<decltype(lambdaFunc), R>( return this->thenImplementation(std::move(lambdaFunc), R{});
std::move(lambdaFunc), typename R::Arg());
} }
template <class T> template <class T>
......
...@@ -88,10 +88,15 @@ struct ArgType<> { ...@@ -88,10 +88,15 @@ struct ArgType<> {
typedef void FirstArg; typedef void FirstArg;
}; };
template <bool isTry, typename F, typename... Args> template <bool isTry_, typename F, typename... Args>
struct argResult { struct argResult {
using Function = F;
using ArgList = ArgType<Args...>; using ArgList = ArgType<Args...>;
using Result = invoke_result_t<F, Args...>; using Result = invoke_result_t<F, Args...>;
using ArgsSize = index_constant<sizeof...(Args)>;
static constexpr bool isTry() {
return isTry_;
}
}; };
template <typename T, typename F> template <typename T, typename F>
......
...@@ -425,15 +425,15 @@ class FutureBase { ...@@ -425,15 +425,15 @@ class FutureBase {
// Variant: returns a value // Variant: returns a value
// e.g. f.thenTry([](Try<T> t){ return t.value(); }); // e.g. f.thenTry([](Try<T> t){ return t.value(); });
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
thenImplementation(F&& func, futures::detail::argResult<isTry, F, Args...>); thenImplementation(F&& func, R);
// Variant: returns a Future // Variant: returns a Future
// e.g. f.thenTry([](Try<T> t){ return makeFuture<T>(t); }); // e.g. f.thenTry([](Try<T> t){ return makeFuture<T>(t); });
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
thenImplementation(F&& func, futures::detail::argResult<isTry, F, Args...>); thenImplementation(F&& func, R);
template <typename E> template <typename E>
SemiFuture<T> withinImplementation(Duration dur, E e, Timekeeper* tk) &&; SemiFuture<T> withinImplementation(Duration dur, E e, Timekeeper* tk) &&;
...@@ -1173,8 +1173,7 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1173,8 +1173,7 @@ class Future : private futures::detail::FutureBase<T> {
[[deprecated("use thenValue(auto&&) or thenValue(folly::Unit) instead")]] [[deprecated("use thenValue(auto&&) or thenValue(folly::Unit) instead")]]
typename std::enable_if<is_invocable<F>::value, typename R::Return>::type typename std::enable_if<is_invocable<F>::value, typename R::Return>::type
then(F&& func) && { then(F&& func) && {
return this->template thenImplementation<F, R>( return this->thenImplementation(std::forward<F>(func), R{});
std::forward<F>(func), typename R::Arg());
} }
// clang-format off // clang-format off
......
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