From: Tom Jackson Date: Wed, 29 Mar 2017 22:17:25 +0000 (-0700) Subject: uncurry X-Git-Tag: v2017.04.03.00~9 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=406098e73e5c7f4e1e89b6b7e36b865587d509e9;p=folly.git uncurry Summary: Extending ApplyTuple to support this common functional construct, with good forwarding semantics. Reviewed By: yfeldblum Differential Revision: D4787560 fbshipit-source-id: 2c740e448e0cb916abe948b79709d5ecd8ba54bb --- diff --git a/folly/ApplyTuple.h b/folly/ApplyTuple.h index 3046e0fb..cce6fec0 100644 --- a/folly/ApplyTuple.h +++ b/folly/ApplyTuple.h @@ -113,5 +113,52 @@ inline constexpr auto applyTuple(F&& f, Tuples&&... t) detail::apply_tuple::MakeIndexSequenceFromTuple{}); } +namespace detail { +namespace apply_tuple { + +template +class Uncurry { + public: + explicit Uncurry(F&& func) : func_(std::move(func)) {} + explicit Uncurry(const F& func) : func_(func) {} + + template + auto operator()(Tuple&& tuple) const + -> decltype(applyTuple(std::declval(), std::forward(tuple))) { + return applyTuple(func_, std::forward(tuple)); + } + + private: + F func_; +}; +} // namespace apply_tuple +} // namespace detail + +/** + * Wraps a function taking N arguments into a function which accepts a tuple of + * N arguments. Note: This function will also accept an std::pair if N == 2. + * + * For example, given the below code: + * + * std::vector> rows = ...; + * auto test = [](std::tuple& row) { + * return std::get<0>(row) * std::get<1>(row) * std::get<2>(row) == 24; + * }; + * auto found = std::find_if(rows.begin(), rows.end(), test); + * + * + * 'test' could be rewritten as: + * + * auto test = + * folly::uncurry([](int a, int b, int c) { return a * b * c == 24; }); + * + */ +template +auto uncurry(F&& f) + -> detail::apply_tuple::Uncurry::type> { + return detail::apply_tuple::Uncurry::type>( + std::forward(f)); +} + ////////////////////////////////////////////////////////////////////// } diff --git a/folly/gen/test/StringTest.cpp b/folly/gen/test/StringTest.cpp index b417e3f1..d4e14192 100644 --- a/folly/gen/test/StringTest.cpp +++ b/folly/gen/test/StringTest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -360,3 +361,19 @@ TEST(StringGen, Batch) { EXPECT_EQ(lines, from(chunks) | resplit('\n') | eachTo() | batch(3) | rconcat | as()); } + +TEST(StringGen, UncurryTuple) { + folly::StringPiece file = "1\t2\t3\n1\t4\t9"; + auto rows = split(file, '\n') | eachToTuple('\t'); + auto productSum = + rows | map(uncurry([](int x, int y, int z) { return x * y * z; })) | sum; + EXPECT_EQ(42, productSum); +} + +TEST(StringGen, UncurryPair) { + folly::StringPiece file = "2\t3\n4\t9"; + auto rows = split(file, '\n') | eachToPair('\t'); + auto productSum = + rows | map(uncurry([](int x, int y) { return x * y; })) | sum; + EXPECT_EQ(42, productSum); +} diff --git a/folly/test/ApplyTupleTest.cpp b/folly/test/ApplyTupleTest.cpp index e56d85ac..56f4c651 100644 --- a/folly/test/ApplyTupleTest.cpp +++ b/folly/test/ApplyTupleTest.cpp @@ -314,3 +314,59 @@ TEST(ApplyTuple, MultipleTuples) { folly::applyTuple( add, std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3))); } + +TEST(ApplyTuple, UncurryCopyMove) { + std::string separator = "================================\n"; + auto formatRow = folly::uncurry([=](std::string a, std::string b) { + // capture separator by copy + return separator + a + "\n" + b + "\n" + separator; + }); + auto row = std::make_tuple("hello", "world"); + auto expected = separator + "hello\nworld\n" + separator; + EXPECT_EQ(expected, formatRow(row)); + auto formatRowCopy = formatRow; + EXPECT_EQ(expected, formatRowCopy(row)); + auto formatRowMove = std::move(formatRow); + EXPECT_EQ(expected, formatRowMove(row)); + + // capture value moved out from formatRow + EXPECT_NE(expected, formatRow(row)); +} + +TEST(ApplyTuple, Uncurry) { + EXPECT_EQ(42, folly::uncurry([](int x, int y) { + return x * y; + })(std::pair(6, 7))); + EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) { + return x * y; + })(std::pair(6, 7))); + EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) { + return x * y; + })(std::pair(6, 7))); + + std::string long1 = "a long string exceeding small string size"; + std::string long2 = "and here is another one!"; + std::string expected = long1 + long2; + + auto cat = folly::uncurry( + [](std::string a, std::string b) { return std::move(a) + std::move(b); }); + + EXPECT_EQ(expected, cat(std::make_pair(long1, long2))); + EXPECT_FALSE(long1.empty()); + EXPECT_FALSE(long2.empty()); + EXPECT_EQ(expected, cat(std::tie(long1, long2))); + EXPECT_FALSE(long1.empty()); + EXPECT_FALSE(long2.empty()); + EXPECT_EQ( + expected, cat(std::forward_as_tuple(std::move(long1), std::move(long2)))); + EXPECT_TRUE(long1.empty()); + EXPECT_TRUE(long2.empty()); +} + +TEST(ApplyTuple, UncurryStdFind) { + std::vector> v{{1, 9}, {2, 8}, {3, 7}, {4, 6}, {5, 5}}; + EXPECT_EQ( + 3, std::count_if(v.begin(), v.end(), folly::uncurry([](int a, int b) { + return b % a == 0; + }))); +}