From ca09289d7cdd304e27e6a966681dd958891521e9 Mon Sep 17 00:00:00 2001 From: Sven Over Date: Thu, 18 Feb 2016 05:31:22 -0800 Subject: [PATCH] folly/ApplyTuple.h: fix const-correctness & other issues, simplify Summary:The existing implementation of folly::applyTuple does not support mutable callables (such as mutable lambdas and other functor objects that only implement non-const operator()). This commit adds a few more unit tests and changes the implementation so that new and existing tests pass. Reviewed By: yfeldblum Differential Revision: D2942622 fb-gh-sync-id: 82478f290e9fd2020358ff79ef0a6bcf8a43738c shipit-source-id: 82478f290e9fd2020358ff79ef0a6bcf8a43738c --- folly/ApplyTuple.h | 112 ++++++++++++------------------- folly/test/ApplyTupleTest.cpp | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 72 deletions(-) diff --git a/folly/ApplyTuple.h b/folly/ApplyTuple.h index b2dff742..3a8e14ee 100644 --- a/folly/ApplyTuple.h +++ b/folly/ApplyTuple.h @@ -25,97 +25,65 @@ * ASSERT(x == 24); */ -#ifndef FOLLY_APPLYTUPLE_H_ -#define FOLLY_APPLYTUPLE_H_ +#pragma once -#include #include -#include +#include namespace folly { ////////////////////////////////////////////////////////////////////// namespace detail { +namespace apply_tuple { + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +template +using MakeIndexSequenceFromTuple = + MakeIndexSequence::type>::value>; // This is to allow using this with pointers to member functions, // where the first argument in the tuple will be the this pointer. -template F& makeCallable(F& f) { return f; } -template -auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) { +template +inline constexpr F&& makeCallable(F&& f) { + return std::forward(f); +} +template +inline constexpr auto makeCallable(M(C::*d)) -> decltype(std::mem_fn(d)) { return std::mem_fn(d); } -template -struct DerefSize - : std::tuple_size::type> -{}; - -template struct ExprDoUnpack { - enum { - value = sizeof...(Unpacked) < DerefSize::value - }; -}; - -template struct ExprIsUnpacked { - enum { - value = sizeof...(Unpacked) == DerefSize::value - }; -}; - -// CallTuple recursively unpacks tuple arguments so we can forward -// them into the function. -template -struct CallTuple { - template - static typename std::enable_if::value, - Ret - >::type call(const F& f, Tuple&& t, Unpacked&&... unp) { - typedef typename std::tuple_element< - sizeof...(Unpacked), - typename std::remove_reference::type - >::type ElementType; - return CallTuple::call(f, std::forward(t), - std::forward(unp)..., - std::forward(std::get(t)) - ); - } - - template - static typename std::enable_if::value, - Ret>::type - call(const F& f, Tuple&& /* t */, Unpacked&&... unp) { - return makeCallable(f)(std::forward(unp)...); - } -}; - -// The point of this meta function is to extract the contents of the -// tuple as a parameter pack so we can pass it into std::result_of<>. -template struct ReturnValue; -template -struct ReturnValue> { - typedef typename std::result_of::type type; -}; - +template +inline constexpr auto call(F&& f, Tuple&& t, IndexSequence) + -> decltype( + std::forward(f)(std::get(std::forward(t))...)) { + return std::forward(f)(std::get(std::forward(t))...); } +} // namespace apply_tuple +} // namespace detail + ////////////////////////////////////////////////////////////////////// -template -typename detail::ReturnValue< - typename std::decay::type, - typename std::decay::type ->::type -applyTuple(const Callable& c, Tuple&& t) { - typedef typename detail::ReturnValue< - typename std::decay::type, - typename std::decay::type - >::type RetT; - return detail::CallTuple::call(c, std::forward(t)); +template +inline constexpr auto applyTuple(F&& f, Tuple&& t) + -> decltype(detail::apply_tuple::call( + detail::apply_tuple::makeCallable(std::forward(f)), + std::forward(t), + detail::apply_tuple::MakeIndexSequenceFromTuple{})) { + return detail::apply_tuple::call( + detail::apply_tuple::makeCallable(std::forward(f)), + std::forward(t), + detail::apply_tuple::MakeIndexSequenceFromTuple{}); } ////////////////////////////////////////////////////////////////////// - } - -#endif diff --git a/folly/test/ApplyTupleTest.cpp b/folly/test/ApplyTupleTest.cpp index 7219590c..395cdfed 100644 --- a/folly/test/ApplyTupleTest.cpp +++ b/folly/test/ApplyTupleTest.cpp @@ -166,3 +166,123 @@ TEST(ApplyTuple, Test) { const auto tuple3 = std::make_tuple(1, 2, 3.0); folly::applyTuple(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); +} + +TEST(ApplyTuple, ConstOverloads) { + struct ConstOverloaded { + ConstOverloaded() {} + int operator()() { return 101; } + int operator()() const { return 102; } + }; + + 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); + + // call operator()() const + EXPECT_EQ(folly::applyTuple(const_cast(covl), + std::make_tuple()), + 102); + EXPECT_EQ(folly::applyTuple(std::cref(covl), std::make_tuple()), 102); +} + +TEST(ApplyTuple, RefOverloads) { + struct RefOverloaded { + RefOverloaded() {} + int operator()() & { return 201; } + int operator()() const & { return 202; } + int operator()() && { return 203; } + }; + + RefOverloaded rovl; + + // call operator()() & + EXPECT_EQ(folly::applyTuple(rovl, std::make_tuple()), 201); + EXPECT_EQ(folly::applyTuple(std::ref(rovl), std::make_tuple()), 201); + + // call operator()() const & + EXPECT_EQ(folly::applyTuple(const_cast(rovl), + std::make_tuple()), + 202); + EXPECT_EQ(folly::applyTuple(std::cref(rovl), std::make_tuple()), 202); + + // call operator()() && + EXPECT_EQ(folly::applyTuple(std::move(rovl), std::make_tuple()), 203); +} + +struct MemberFunc { + int x; + int getX() const { return x; } + void setX(int xx) { x = xx; } +}; + +TEST(ApplyTuple, MemberFunction) { + MemberFunc mf; + mf.x = 123; + + // call getter + EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(&mf)), 123); + + // call setter + folly::applyTuple(&MemberFunc::setX, std::make_tuple(&mf, 234)); + EXPECT_EQ(mf.x, 234); + EXPECT_EQ(folly::applyTuple(&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); +} + +TEST(ApplyTuple, MemberFunctionWithConstPointer) { + MemberFunc mf; + mf.x = 234; + + EXPECT_EQ( + folly::applyTuple(&MemberFunc::getX, + std::make_tuple(const_cast(&mf))), + 234); +} + +TEST(ApplyTuple, MemberFunctionWithSharedPtr) { + MemberFunc mf; + mf.x = 234; + + EXPECT_EQ( + folly::applyTuple(&MemberFunc::getX, + std::make_tuple(std::make_shared(mf))), + 234); +} + +TEST(ApplyTuple, MemberFunctionWithUniquePtr) { + MemberFunc mf; + mf.x = 234; + + EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, + std::make_tuple(std::unique_ptr( + new MemberFunc(mf)))), + 234); +} + +TEST(ApplyTuple, Array) { + folly::applyTuple(func, std::array{{1, 2, 3}}); + folly::applyTuple(func, std::array{{1, 2, 3}}); +} + +TEST(ApplyTuple, Pair) { + auto add = [](int x, int y) { return x + y; }; + + EXPECT_EQ(folly::applyTuple(add, std::pair{1200, 34}), 1234); +} -- 2.34.1