Composable and easy-to-use pipelines for sequence processing in C++.
This library provides a lightweight and expressive way to process sequences of values using functional programming concepts. Inspired by Kotlin Sequences and the C++ Rappel library, it enables the creation of clear and easily composable data transformations.
- Composable Functional Pipelines – Chain
map
,filter
,reduce
, and more - Readable Compile-Time Errors – Short, clear error messages for easier debugging
- Avoids Iterator Pitfalls – Uses generators instead of iterators to sidestep C++ iterator complexities
Before diving into details, here’s a simple example demonstrating how to create and run a sequence pipeline:
#include "src/sequence.hpp"
using namespace seq;
std::vector<int> result;
auto pipeline = from_iota(
map<int>([](int x) { return x * x; },
take<int>(3,
to_vector(result))));
pipeline.run(); // Generates {0, 1, 4}
This pipeline:
- Generates integers from
0
onwards (from_iota
) - Squares each value (
map
) - Takes the first 3 values (
take
) - Collects them in
result
(to_vector
)
sudo apt install g++ cmake libgtest-dev libbenchmark-dev
git clone git@github.com:mnikander/cpp_sequence.git
cd cpp_sequence
mkdir out && cd out && cmake .. && cd ..
cmake --build out/ && ./out/unit_tests
This library models a pipeline using three building blocks:
- Source – Generates values (
from_iota
,from_vector
, etc.) - Stage – Transforms values (
map
,filter
,take
,reduce
, etc.) - Sink – Collects results (
to_vector
,to_value
, etc.)
A sequence processes values individually, pushing them through the stages step by step.
#include "src/sequence.hpp"
using namespace seq;
int result = 0;
auto pipeline = from_iota(
map<int>([](int i) { return i - 3; },
filter<int>([](int i) { return i % 2 == 0; },
reduce<int>(std::plus<int>{}, 0,
to_value(result)))));
pipeline.yield(8); // Process first 8 elements, filtering evens
assert(result == 4); // Sum of (-2, 0, 2, 4)
Each stage in the pipeline must explicitly specify its input type (e.g., map<int>
, filter<int>
).
This ensures simple compile-time type checking and results in clear, readable error messages, preventing template-related confusion.
Many more usage examples can be found in the unit tests!
Note: forgetting to specify the input type of stage is a common cause of errors such as "no instance of function template 'reduce' matches the argument list".
A simple benchmark is included, which generates a random vector of integers and sums all the even integers together. Initial benchmarking runs indicate that the sequence implementation takes 2-6 times as long as a handcrafted for-loop.
While std::ranges
provides powerful sequence transformations, this library offers:
- Generator-based execution – Avoids complex iterator issues
- More readable error messages – Explicit input types prevent cryptic template errors
- Minimal boilerplate – Easily chainable and more intuitive syntax
If you’re looking for a simple alternative to iterators with clear syntax and minimal boilerplate, this library is a good fit!
Copyright (c) 2024, Marco Nikander