Commit 406098e7 authored by Tom Jackson's avatar Tom Jackson Committed by Facebook Github Bot

uncurry

Summary: Extending ApplyTuple to support this common functional construct, with good forwarding semantics.

Reviewed By: yfeldblum

Differential Revision: D4787560

fbshipit-source-id: 2c740e448e0cb916abe948b79709d5ecd8ba54bb
parent c58b5991
...@@ -113,5 +113,52 @@ inline constexpr auto applyTuple(F&& f, Tuples&&... t) ...@@ -113,5 +113,52 @@ inline constexpr auto applyTuple(F&& f, Tuples&&... t)
detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{}); detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{});
} }
namespace detail {
namespace apply_tuple {
template <class F>
class Uncurry {
public:
explicit Uncurry(F&& func) : func_(std::move(func)) {}
explicit Uncurry(const F& func) : func_(func) {}
template <class Tuple>
auto operator()(Tuple&& tuple) const
-> decltype(applyTuple(std::declval<F>(), std::forward<Tuple>(tuple))) {
return applyTuple(func_, std::forward<Tuple>(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<std::tuple<int, int, int>> rows = ...;
* auto test = [](std::tuple<int, int, int>& 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 <class F>
auto uncurry(F&& f)
-> detail::apply_tuple::Uncurry<typename std::decay<F>::type> {
return detail::apply_tuple::Uncurry<typename std::decay<F>::type>(
std::forward<F>(f));
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
} }
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include <folly/ApplyTuple.h>
#include <folly/gen/String.h> #include <folly/gen/String.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
...@@ -360,3 +361,19 @@ TEST(StringGen, Batch) { ...@@ -360,3 +361,19 @@ TEST(StringGen, Batch) {
EXPECT_EQ(lines, from(chunks) | resplit('\n') | eachTo<std::string>() | EXPECT_EQ(lines, from(chunks) | resplit('\n') | eachTo<std::string>() |
batch(3) | rconcat | as<vector>()); batch(3) | rconcat | as<vector>());
} }
TEST(StringGen, UncurryTuple) {
folly::StringPiece file = "1\t2\t3\n1\t4\t9";
auto rows = split(file, '\n') | eachToTuple<int, int, int>('\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<int, int>('\t');
auto productSum =
rows | map(uncurry([](int x, int y) { return x * y; })) | sum;
EXPECT_EQ(42, productSum);
}
...@@ -314,3 +314,59 @@ TEST(ApplyTuple, MultipleTuples) { ...@@ -314,3 +314,59 @@ TEST(ApplyTuple, MultipleTuples) {
folly::applyTuple( folly::applyTuple(
add, std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3))); 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<int, int>(6, 7)));
EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) {
return x * y;
})(std::pair<int&&, int&&>(6, 7)));
EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) {
return x * y;
})(std::pair<int&&, int&&>(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<std::pair<int, int>> 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;
})));
}
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