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

Visit, for monitoring or mutating pipelines

Summary: The pattern `mapped([](auto&& i) { ...; return std::move(i); }` is quite common. Evidently people need some good mechanism to just observe or possibly mutate items in pipelines. Note that this is still lazy, it is only called when the pipeline is run.

Reviewed By: yfeldblum

Differential Revision: D5457196

fbshipit-source-id: 3a892b8895e02dd8fcd6a7fd4d3b27063d1b071f
parent 4b807f9e
...@@ -470,7 +470,7 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> { ...@@ -470,7 +470,7 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
* *
* This type is usually used through the 'map' or 'mapped' helper function: * This type is usually used through the 'map' or 'mapped' helper function:
* *
* auto squares = seq(1, 10) | map(square) | asVector; * auto squares = seq(1, 10) | map(square) | as<std::vector>();
*/ */
template <class Predicate> template <class Predicate>
class Map : public Operator<Map<Predicate>> { class Map : public Operator<Map<Predicate>> {
...@@ -597,7 +597,7 @@ class Filter : public Operator<Filter<Predicate>> { ...@@ -597,7 +597,7 @@ class Filter : public Operator<Filter<Predicate>> {
* *
* auto best = from(sortedItems) * auto best = from(sortedItems)
* | until([](Item& item) { return item.score > 100; }) * | until([](Item& item) { return item.score > 100; })
* | asVector; * | as<std::vector>();
*/ */
template <class Predicate> template <class Predicate>
class Until : public Operator<Until<Predicate>> { class Until : public Operator<Until<Predicate>> {
...@@ -703,6 +703,65 @@ class Take : public Operator<Take> { ...@@ -703,6 +703,65 @@ class Take : public Operator<Take> {
} }
}; };
/**
* Visit - For calling a function on each item before passing it down the
* pipeline.
*
* This type is usually used through the 'visit' helper function:
*
* auto printedValues = seq(1) | visit(debugPrint);
* // nothing printed yet
* auto results = take(10) | as<std::vector>();
* // results now populated, 10 values printed
*/
template <class Visitor>
class Visit : public Operator<Visit<Visitor>> {
Visitor visitor_;
public:
Visit() = default;
explicit Visit(Visitor visitor) : visitor_(std::move(visitor)) {}
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Visitor visitor_;
public:
explicit Generator(Source source, const Visitor& visitor)
: source_(std::move(source)), visitor_(visitor) {}
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
visitor_(value); // not forwarding to avoid accidental moves
body(std::forward<Value>(value));
});
}
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) {
visitor_(value); // not forwarding to avoid accidental moves
return handler(std::forward<Value>(value));
});
}
static constexpr bool infinite = Source::infinite;
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), visitor_);
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), visitor_);
}
};
/** /**
* Stride - For producing every Nth value from a source. * Stride - For producing every Nth value from a source.
* *
......
...@@ -330,6 +330,9 @@ class Sample; ...@@ -330,6 +330,9 @@ class Sample;
class Skip; class Skip;
template <class Visitor>
class Visit;
template <class Selector, class Comparer = Less> template <class Selector, class Comparer = Less>
class Order; class Order;
...@@ -634,6 +637,11 @@ Filter filter(Predicate pred = Predicate()) { ...@@ -634,6 +637,11 @@ Filter filter(Predicate pred = Predicate()) {
return Filter(std::move(pred)); return Filter(std::move(pred));
} }
template <class Visitor = Ignore, class Visit = detail::Visit<Visitor>>
Visit visit(Visitor visitor = Visitor()) {
return Visit(std::move(visitor));
}
template <class Predicate, class Until = detail::Until<Predicate>> template <class Predicate, class Until = detail::Until<Predicate>>
Until until(Predicate pred = Predicate()) { Until until(Predicate pred = Predicate()) {
return Until(std::move(pred)); return Until(std::move(pred));
...@@ -742,7 +750,6 @@ Composed any(Predicate pred = Predicate()) { ...@@ -742,7 +750,6 @@ Composed any(Predicate pred = Predicate()) {
* *
* from(source) | all(pred) == from(source) | filter(negate(pred)) | isEmpty * from(source) | all(pred) == from(source) | filter(negate(pred)) | isEmpty
*/ */
template < template <
class Predicate = Identity, class Predicate = Identity,
class Filter = detail::Filter<Negate<Predicate>>, class Filter = detail::Filter<Negate<Predicate>>,
...@@ -814,6 +821,7 @@ template < ...@@ -814,6 +821,7 @@ template <
UnwrapOr unwrapOr(Fallback&& fallback) { UnwrapOr unwrapOr(Fallback&& fallback) {
return UnwrapOr(std::forward<Fallback>(fallback)); return UnwrapOr(std::forward<Fallback>(fallback));
} }
} // gen } // gen
} // folly } // folly
......
...@@ -475,7 +475,35 @@ TEST(Gen, Until) { ...@@ -475,7 +475,35 @@ TEST(Gen, Until) {
| as<vector<int>>(); | as<vector<int>>();
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
} }
*/ */
}
TEST(Gen, Visit) {
auto increment = [](int& i) { ++i; };
auto clone = map([](int i) { return i; });
{ // apply()
auto expected = 10;
auto actual = seq(0) | clone | visit(increment) | take(4) | sum;
EXPECT_EQ(expected, actual);
}
{ // foreach()
auto expected = 10;
auto actual = seq(0, 3) | clone | visit(increment) | sum;
EXPECT_EQ(expected, actual);
}
{ // tee-like
std::vector<int> x2, x4;
std::vector<int> expected2{0, 1, 4, 9};
std::vector<int> expected4{0, 1, 16, 81};
auto tee = [](std::vector<int>& container) {
return visit([&](int value) { container.push_back(value); });
};
EXPECT_EQ(
98, seq(0, 3) | map(square) | tee(x2) | map(square) | tee(x4) | sum);
EXPECT_EQ(expected2, x2);
EXPECT_EQ(expected4, x4);
}
} }
TEST(Gen, Composed) { TEST(Gen, Composed) {
...@@ -664,7 +692,7 @@ TEST(Gen, FromRValue) { ...@@ -664,7 +692,7 @@ TEST(Gen, FromRValue) {
// reference of a std::vector when it is used as the 'other' for an rvalue // reference of a std::vector when it is used as the 'other' for an rvalue
// constructor. Use fbvector because we're sure its size will be zero in // constructor. Use fbvector because we're sure its size will be zero in
// this case. // this case.
fbvector<int> v({1,2,3,4}); fbvector<int> v({1, 2, 3, 4});
auto q1 = from(v); auto q1 = from(v);
EXPECT_EQ(v.size(), 4); // ensure that the lvalue version was called! EXPECT_EQ(v.size(), 4); // ensure that the lvalue version was called!
auto expected = 1 * 2 * 3 * 4; auto expected = 1 * 2 * 3 * 4;
...@@ -676,11 +704,11 @@ TEST(Gen, FromRValue) { ...@@ -676,11 +704,11 @@ TEST(Gen, FromRValue) {
} }
{ {
auto expected = 7; auto expected = 7;
auto q = from([] {return vector<int>({3,7,5}); }()); auto q = from([] { return vector<int>({3, 7, 5}); }());
EXPECT_EQ(expected, q | max); EXPECT_EQ(expected, q | max);
} }
{ {
for (auto size: {5, 1024, 16384, 1<<20}) { for (auto size : {5, 1024, 16384, 1 << 20}) {
auto q1 = from(vector<int>(size, 2)); auto q1 = from(vector<int>(size, 2));
auto q2 = from(vector<int>(size, 3)); auto q2 = from(vector<int>(size, 3));
// If the rvalue specialization is broken/gone, then the compiler will // If the rvalue specialization is broken/gone, then the compiler will
......
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