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