GrPPI  0.2
Generic and Reusable Parallel Pattern Interface
Filter pattern

The filter (or stream filter) is a streaming pattern that discards items from a data stream based on a predicate, so that only data items satisfying (keep) or dissatisfying (discard) the predicate are passed to the consumer.

The interface to the filter pattern is provided by functions grppi::keep() and grppi::discard(). As all functions in GrPPI, this functions takes as its first argument an execution policy.

grppi::keep(exec, other_arguments...);
grppi::discard(exec, other_arguments...);

Stream filter variants

There are two variants:

Key elements in stream filter

The central element in a filter is the Predicate. The operation may be any C++ callable entity. This operation, is a unary operation taking a data item and returning value that is contextually convertible to bool. Thus, a predicate pred is any operation, that given a value x of type T, makes the following valid:

if (predicate(item)) { /*...*/ }.
if (!predicate(item)) { /*...*/ }.

A stand-alone filter also has a Generator. The generator may be any C++ callable entity that produces values in a way that allows to signal an indication of end-of stream. For this purpose, the filter requires that the generator produces values of any type that is compatible with a subset of an optional interface:

auto r = generator(); // r is a value generated by a generator.
if (r) { // r is convertible to bool
auto y = *r; // r can be dereferenced.
}

Additionally, a filter may have a Consumer. The consumer may be any C++ callable entity that takes values of the result type of the Operation.

Details on stream filter variants

Stand-alone filter

A stand alone filter has three elements:


Example: Generate a stream of integer numbers and discards odd numbers.

int n = 10;
[&n]() -> std::optional<int> {
n--;
if (n>0) return n;
else return {};
},
[](int x) { return x%2; },
[](int x) {
std::cout << x << " ";
}
);

Composable filter

A composable filter has a single element:

The input values will be generated by the upper level pattern, which will be also responsible for consuming the output values.


Example: A filter stage in a pipeline.

stageA,
stageB,
grppi::keep(exec, [](auto x) { return x.lenght()>4; }),
stageC
);

Note: For brevity we do not show here the details of other stages.

For composing complex patterns, the keep() and discard() functions may be used to create an object that may be supplied to another pattern to build a composed pattern.


Example: A composable filter stage in a pipeline.

auto keep_odd = grppi::keep(exec,
[](auto x) { return x%2; });
stageA,
stageB,
keep_odd,
stageC
);

Note: For brevity we do not show here the details of other stages.