Commit 23817ea8 authored by Aaryaman Sagar's avatar Aaryaman Sagar Committed by Facebook Github Bot

Backport std::apply and remove folly::applyTuple

Summary:
Add a backport of `std::apply` with a similar implementation that makes it easy
to reason about.  Also implement `applyTuple` in terms of `apply`, `invoke` and
`tuple_cat`

The implementation of `apply` has been taken straight from the standard,
reducing chances of churn in incompatible assumptions (if any).  Note that the
definition and declaration of `apply` has been changed from the way it is
specified in the standard for better SFINAE compatibility, if needed, will
followup with a paper proposing the same change in the standard

Reviewed By: spacedentist

Differential Revision: D8248199

fbshipit-source-id: e3e76193a30b1599eca3fe986a33f61addad7a59
parent f6f9d7ea
......@@ -14,17 +14,6 @@
* limitations under the License.
*/
/*
* Defines a function folly::applyTuple, which takes a function and a
* std::tuple of arguments and calls the function with those
* arguments.
*
* Example:
*
* int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
* ASSERT(x == 24);
*/
#pragma once
#include <functional>
......@@ -39,70 +28,22 @@ namespace folly {
//////////////////////////////////////////////////////////////////////
namespace detail {
namespace apply_tuple {
inline constexpr std::size_t sum() {
return 0;
}
template <typename... Args>
inline constexpr std::size_t sum(std::size_t v1, Args... vs) {
return v1 + sum(vs...);
}
template <typename... Tuples>
struct TupleSizeSum {
static constexpr auto value = sum(std::tuple_size<Tuples>::value...);
};
template <typename... Tuples>
using MakeIndexSequenceFromTuple = folly::make_index_sequence<
TupleSizeSum<typename std::decay<Tuples>::type...>::value>;
template <class F, class Tuple, std::size_t... Indexes>
inline constexpr auto call(F&& f, Tuple&& t, folly::index_sequence<Indexes...>)
-> decltype(invoke(
std::forward<F>(f),
std::get<Indexes>(std::forward<Tuple>(t))...)) {
return invoke(
std::forward<F>(f), std::get<Indexes>(std::forward<Tuple>(t))...);
template <class F, class T, std::size_t... I>
constexpr decltype(auto) applyImpl(F&& f, T&& t, folly::index_sequence<I...>) {
return invoke(std::forward<F>(f), std::get<I>(std::forward<T>(t))...);
}
template <class Tuple, std::size_t... Indexes>
inline constexpr auto forwardTuple(Tuple&& t, folly::index_sequence<Indexes...>)
-> decltype(
std::forward_as_tuple(std::get<Indexes>(std::forward<Tuple>(t))...)) {
return std::forward_as_tuple(std::get<Indexes>(std::forward<Tuple>(t))...);
}
} // namespace apply_tuple
} // namespace detail
//////////////////////////////////////////////////////////////////////
/**
* Invoke a callable object with a set of arguments passed as a tuple, or a
* series of tuples
*
* Example: the following lines are equivalent
* func(1, 2, 3, "foo");
* applyTuple(func, std::make_tuple(1, 2, 3, "foo"));
* applyTuple(func, std::make_tuple(1, 2), std::make_tuple(3, "foo"));
*/
template <class F, class... Tuples>
inline constexpr auto applyTuple(F&& f, Tuples&&... t)
-> decltype(detail::apply_tuple::call(
std::forward<F>(f),
std::tuple_cat(detail::apply_tuple::forwardTuple(
std::forward<Tuples>(t),
detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples>{})...),
detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{})) {
return detail::apply_tuple::call(
std::forward<F>(f),
std::tuple_cat(detail::apply_tuple::forwardTuple(
std::forward<Tuples>(t),
detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples>{})...),
detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{});
// mimic: std::apply, C++17
template <typename F, typename Tuple>
constexpr decltype(auto) apply(F&& func, Tuple&& tuple) {
constexpr auto size = std::tuple_size<std::remove_reference_t<Tuple>>::value;
return detail::applyImpl(
std::forward<F>(func),
std::forward<Tuple>(tuple),
folly::make_index_sequence<size>{});
}
namespace detail {
......@@ -116,8 +57,8 @@ class Uncurry {
template <class Tuple>
auto operator()(Tuple&& tuple) const
-> decltype(applyTuple(std::declval<F>(), std::forward<Tuple>(tuple))) {
return applyTuple(func_, std::forward<Tuple>(tuple));
-> decltype(apply(std::declval<F>(), std::forward<Tuple>(tuple))) {
return apply(func_, std::forward<Tuple>(tuple));
}
private:
......@@ -173,8 +114,7 @@ struct Construct {
// mimic: std::make_from_tuple, C++17
template <class T, class Tuple>
constexpr T make_from_tuple(Tuple&& t) {
return applyTuple(
detail::apply_tuple::Construct<T>(), std::forward<Tuple>(t));
return apply(detail::apply_tuple::Construct<T>(), std::forward<Tuple>(t));
}
#endif
......
......@@ -16,7 +16,11 @@
#pragma once
#include <folly/functional/ApplyTuple.h>
#include <folly/Utility.h>
#include <folly/functional/Invoke.h>
#include <tuple>
#include <utility>
namespace folly {
......@@ -29,9 +33,19 @@ struct PartialConstructFromCallable {};
template <typename F, typename Tuple>
class Partial {
private:
F f_;
Tuple stored_args_;
using Indexes = make_index_sequence<std::tuple_size<Tuple>{}>;
template <typename Self, std::size_t... I, typename... Args>
static auto invokeForward(Self&& self, index_sequence<I...>, Args&&... args)
-> decltype(invoke(
std::declval<Self>().f_,
std::get<I>(std::declval<Self>().stored_args_)...,
std::declval<Args>()...)) {
return invoke(
std::forward<Self>(self).f_,
std::get<I>(std::forward<Self>(self).stored_args_)...,
std::forward<Args>(args)...);
}
public:
template <typename Callable, typename... Args>
......@@ -40,37 +54,40 @@ class Partial {
stored_args_(std::forward<Args>(args)...) {}
template <typename... CArgs>
auto operator()(CArgs&&... cargs) & -> decltype(applyTuple(
static_cast<F&>(f_),
static_cast<Tuple&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...))) {
return applyTuple(
static_cast<F&>(f_),
static_cast<Tuple&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...));
auto operator()(CArgs&&... cargs) & -> decltype(invokeForward(
std::declval<Partial&>(),
Indexes{},
std::declval<CArgs>()...)) {
return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);
}
template <typename... CArgs>
auto operator()(CArgs&&... cargs) const& -> decltype(applyTuple(
static_cast<F const&>(f_),
static_cast<Tuple const&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...))) {
return applyTuple(
static_cast<F const&>(f_),
static_cast<Tuple const&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...));
auto operator()(CArgs&&... cargs) const& -> decltype(invokeForward(
std::declval<const Partial&>(),
Indexes{},
std::declval<CArgs>()...)) {
return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);
}
template <typename... CArgs>
auto operator()(CArgs&&... cargs) && -> decltype(applyTuple(
static_cast<F&&>(f_),
static_cast<Tuple&&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...))) {
return applyTuple(
static_cast<F&&>(f_),
static_cast<Tuple&&>(stored_args_),
std::forward_as_tuple(std::forward<CArgs>(cargs)...));
template <typename... As>
auto operator()(As&&... a) && -> decltype(invokeForward(
std::declval<Partial&&>(),
Indexes{},
std::declval<As>()...)) {
return invokeForward(std::move(*this), Indexes{}, std::forward<As>(a)...);
}
template <typename... As>
auto operator()(As&&... as) const&& -> decltype(invokeForward(
std::declval<const Partial&&>(),
Indexes{},
std::declval<As>()...)) {
return invokeForward(std::move(*this), Indexes{}, std::forward<As>(as)...);
}
private:
// the stored callable
F f_;
// the stored arguments, these will be forwarded along with the actual
// argumnets to the callable above
Tuple stored_args_;
};
} // namespace partial
......
......@@ -94,7 +94,7 @@ struct GuardObj : GuardObjBase {
{}
~GuardObj() {
folly::applyTuple(f_, args_);
folly::apply(f_, args_);
}
GuardObj(const GuardObj&) = delete;
......@@ -128,20 +128,19 @@ void move_only_func(Mover&&) {}
TEST(ApplyTuple, Test) {
auto argsTuple = std::make_tuple(1, 2, 3.0);
auto func2 = func;
folly::applyTuple(func2, argsTuple);
folly::applyTuple(func, argsTuple);
folly::applyTuple(func, std::make_tuple(1, 2, 3.0));
folly::applyTuple(makeFunc(), std::make_tuple(1, 2, 3.0));
folly::applyTuple(makeFunc(), argsTuple);
folly::apply(func2, argsTuple);
folly::apply(func, argsTuple);
folly::apply(func, std::make_tuple(1, 2, 3.0));
folly::apply(makeFunc(), std::make_tuple(1, 2, 3.0));
folly::apply(makeFunc(), argsTuple);
std::unique_ptr<Wat> wat(new Wat);
folly::applyTuple(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0));
folly::apply(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0));
auto argsTuple2 = std::make_tuple(wat.get(), 1, 2, 3.0);
folly::applyTuple(&Wat::func, argsTuple2);
folly::apply(&Wat::func, argsTuple2);
EXPECT_EQ(10.0,
folly::applyTuple(&Wat::retVal,
std::make_tuple(wat.get(), 1, 9.0)));
EXPECT_EQ(
10.0, folly::apply(&Wat::retVal, std::make_tuple(wat.get(), 1, 9.0)));
auto test = guard(func, 1, 2, 3.0);
CopyCount cpy;
......@@ -149,31 +148,32 @@ TEST(ApplyTuple, Test) {
auto test3 = guard(anotherFunc, std::cref(cpy));
Overloaded ovl;
EXPECT_EQ(0,
folly::applyTuple(
static_cast<int (Overloaded::*)(int)>(&Overloaded::func),
std::make_tuple(&ovl, 12)));
EXPECT_EQ(
0,
folly::apply(
static_cast<int (Overloaded::*)(int)>(&Overloaded::func),
std::make_tuple(&ovl, 12)));
EXPECT_EQ(
/* do not code-mode to EXPECT_TRUE */ true,
folly::applyTuple(
folly::apply(
static_cast<bool (Overloaded::*)(bool)>(&Overloaded::func),
std::make_tuple(&ovl, false)));
int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
int x = folly::apply(std::plus<int>(), std::make_tuple(12, 12));
EXPECT_EQ(24, x);
Mover m;
folly::applyTuple(move_only_func,
std::forward_as_tuple(std::forward<Mover>(Mover())));
folly::apply(
move_only_func, std::forward_as_tuple(std::forward<Mover>(Mover())));
const auto tuple3 = std::make_tuple(1, 2, 3.0);
folly::applyTuple(func, tuple3);
folly::apply(func, tuple3);
}
TEST(ApplyTuple, Mutable) {
auto argsTuple = std::make_tuple(1, 2, 3.0);
folly::applyTuple([](int a, int b, double c) mutable { func(a, b, c); },
argsTuple);
folly::apply(
[](int a, int b, double c) mutable { func(a, b, c); }, argsTuple);
}
TEST(ApplyTuple, ConstOverloads) {
......@@ -186,15 +186,15 @@ TEST(ApplyTuple, ConstOverloads) {
ConstOverloaded covl;
// call operator()()
EXPECT_EQ(folly::applyTuple(covl, std::make_tuple()), 101);
EXPECT_EQ(folly::applyTuple(std::ref(covl), std::make_tuple()), 101);
EXPECT_EQ(folly::applyTuple(std::move(covl), std::make_tuple()), 101);
EXPECT_EQ(folly::apply(covl, std::make_tuple()), 101);
EXPECT_EQ(folly::apply(std::ref(covl), std::make_tuple()), 101);
EXPECT_EQ(folly::apply(std::move(covl), std::make_tuple()), 101);
// call operator()() const
EXPECT_EQ(folly::applyTuple(const_cast<ConstOverloaded const&>(covl),
std::make_tuple()),
102);
EXPECT_EQ(folly::applyTuple(std::cref(covl), std::make_tuple()), 102);
EXPECT_EQ(
folly::apply(const_cast<ConstOverloaded const&>(covl), std::make_tuple()),
102);
EXPECT_EQ(folly::apply(std::cref(covl), std::make_tuple()), 102);
}
TEST(ApplyTuple, RefOverloads) {
......@@ -208,17 +208,17 @@ TEST(ApplyTuple, RefOverloads) {
RefOverloaded rovl;
// call operator()() &
EXPECT_EQ(folly::applyTuple(rovl, std::make_tuple()), 201);
EXPECT_EQ(folly::applyTuple(std::ref(rovl), std::make_tuple()), 201);
EXPECT_EQ(folly::apply(rovl, std::make_tuple()), 201);
EXPECT_EQ(folly::apply(std::ref(rovl), std::make_tuple()), 201);
// call operator()() const &
EXPECT_EQ(folly::applyTuple(const_cast<RefOverloaded const&>(rovl),
std::make_tuple()),
202);
EXPECT_EQ(folly::applyTuple(std::cref(rovl), std::make_tuple()), 202);
EXPECT_EQ(
folly::apply(const_cast<RefOverloaded const&>(rovl), std::make_tuple()),
202);
EXPECT_EQ(folly::apply(std::cref(rovl), std::make_tuple()), 202);
// call operator()() &&
EXPECT_EQ(folly::applyTuple(std::move(rovl), std::make_tuple()), 203);
EXPECT_EQ(folly::apply(std::move(rovl), std::make_tuple()), 203);
}
struct MemberFunc {
......@@ -232,20 +232,20 @@ TEST(ApplyTuple, MemberFunction) {
mf.x = 123;
// call getter
EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(&mf)), 123);
EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 123);
// call setter
folly::applyTuple(&MemberFunc::setX, std::make_tuple(&mf, 234));
folly::apply(&MemberFunc::setX, std::make_tuple(&mf, 234));
EXPECT_EQ(mf.x, 234);
EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(&mf)), 234);
EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 234);
}
TEST(ApplyTuple, MemberFunctionWithRefWrapper) {
MemberFunc mf;
mf.x = 234;
EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(std::ref(mf))),
234);
EXPECT_EQ(
folly::apply(&MemberFunc::getX, std::make_tuple(std::ref(mf))), 234);
}
TEST(ApplyTuple, MemberFunctionWithConstPointer) {
......@@ -253,8 +253,9 @@ TEST(ApplyTuple, MemberFunctionWithConstPointer) {
mf.x = 234;
EXPECT_EQ(
folly::applyTuple(&MemberFunc::getX,
std::make_tuple(const_cast<MemberFunc const*>(&mf))),
folly::apply(
&MemberFunc::getX,
std::make_tuple(const_cast<MemberFunc const*>(&mf))),
234);
}
......@@ -263,8 +264,8 @@ TEST(ApplyTuple, MemberFunctionWithSharedPtr) {
mf.x = 234;
EXPECT_EQ(
folly::applyTuple(&MemberFunc::getX,
std::make_tuple(std::make_shared<MemberFunc>(mf))),
folly::apply(
&MemberFunc::getX, std::make_tuple(std::make_shared<MemberFunc>(mf))),
234);
}
......@@ -273,47 +274,61 @@ TEST(ApplyTuple, MemberFunctionWithUniquePtr) {
mf.x = 234;
EXPECT_EQ(
folly::applyTuple(&MemberFunc::getX,
std::make_tuple(std::make_unique<MemberFunc>(mf))),
folly::apply(
&MemberFunc::getX, std::make_tuple(std::make_unique<MemberFunc>(mf))),
234);
}
TEST(ApplyTuple, Array) {
folly::applyTuple(func, std::array<int, 3>{{1, 2, 3}});
folly::applyTuple(func, std::array<double, 3>{{1, 2, 3}});
folly::apply(func, std::array<int, 3>{{1, 2, 3}});
folly::apply(func, std::array<double, 3>{{1, 2, 3}});
}
TEST(ApplyTuple, Pair) {
auto add = [](int x, int y) { return x + y; };
EXPECT_EQ(folly::applyTuple(add, std::pair<int, int>{1200, 34}), 1234);
EXPECT_EQ(folly::apply(add, std::pair<int, int>{1200, 34}), 1234);
}
TEST(ApplyTuple, MultipleTuples) {
auto add = [](int x, int y, int z) { return x * 100 + y * 10 + z; };
EXPECT_EQ(123, folly::applyTuple(add, std::make_tuple(1, 2, 3)));
EXPECT_EQ(123, folly::apply(add, std::make_tuple(1, 2, 3)));
EXPECT_EQ(
123, folly::applyTuple(add, std::make_tuple(1, 2, 3), std::make_tuple()));
123,
folly::apply(
add, std::tuple_cat(std::make_tuple(1, 2, 3), std::make_tuple())));
EXPECT_EQ(
123, folly::applyTuple(add, std::make_tuple(1, 2), std::make_tuple(3)));
123,
folly::apply(
add, std::tuple_cat(std::make_tuple(1, 2), std::make_tuple(3))));
EXPECT_EQ(
123, folly::applyTuple(add, std::make_tuple(1), std::make_tuple(2, 3)));
123,
folly::apply(
add, std::tuple_cat(std::make_tuple(1), std::make_tuple(2, 3))));
EXPECT_EQ(
123, folly::applyTuple(add, std::make_tuple(), std::make_tuple(1, 2, 3)));
123,
folly::apply(
add, std::tuple_cat(std::make_tuple(), std::make_tuple(1, 2, 3))));
EXPECT_EQ(
123,
folly::applyTuple(
add, std::make_tuple(1, 2, 3), std::make_tuple(), std::make_tuple()));
folly::apply(
add,
std::tuple_cat(
std::make_tuple(1, 2, 3), std::make_tuple(), std::make_tuple())));
EXPECT_EQ(
123,
folly::applyTuple(
add, std::make_tuple(1), std::make_tuple(2), std::make_tuple(3)));
folly::apply(
add,
std::tuple_cat(
std::make_tuple(1), std::make_tuple(2), std::make_tuple(3))));
EXPECT_EQ(
123,
folly::applyTuple(
add, std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3)));
folly::apply(
add,
std::tuple_cat(
std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3))));
}
TEST(ApplyTuple, UncurryCopyMove) {
......
......@@ -566,7 +566,7 @@ struct IsAvalanchingHasher<hasher<std::pair<T1, T2>>, K> : std::true_type {};
template <typename... Ts>
struct hasher<std::tuple<Ts...>> {
size_t operator()(const std::tuple<Ts...>& key) const {
return applyTuple(Hash(), key);
return apply(Hash(), key);
}
};
......
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