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