https://github.com/mnikander/cpp_sequence
Sequence pipelines in C++. A functional programming-inspired approach to processing data with map, filter, reduce, and more.
https://github.com/mnikander/cpp_sequence
algorithm cpp cpp17 functional-programming generator map-filter-reduce pipeline rappel sequence
Last synced: about 2 months ago
JSON representation
Sequence pipelines in C++. A functional programming-inspired approach to processing data with map, filter, reduce, and more.
- Host: GitHub
- URL: https://github.com/mnikander/cpp_sequence
- Owner: mnikander
- License: mit
- Created: 2024-10-15T18:08:14.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-02-25T11:51:38.000Z (9 months ago)
- Last Synced: 2025-02-25T12:34:16.932Z (9 months ago)
- Topics: algorithm, cpp, cpp17, functional-programming, generator, map-filter-reduce, pipeline, rappel, sequence
- Language: C++
- Homepage:
- Size: 144 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Sequence Library
**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](https://www.youtube.com/watch?v=itnyR9j8y6E) library, it enables the creation of **clear and easily composable data transformations**.
## Features
- **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
## Quick Example
Before diving into details, here’s a simple example demonstrating how to create and run a sequence pipeline:
```cpp
#include "src/sequence.hpp"
using namespace seq;
std::vector result;
auto pipeline = from_iota(
map([](int x) { return x * x; },
take(3,
to_vector(result))));
pipeline.run(); // Generates {0, 1, 4}
```
This pipeline:
1. Generates integers from `0` onwards (`from_iota`)
2. Squares each value (`map`)
3. Takes the first 3 values (`take`)
4. Collects them in `result` (`to_vector`)
---
## Installation & Setup
### **1. Install Dependencies**
```bash
sudo apt install g++ cmake libgtest-dev libbenchmark-dev
```
### **2. Clone the Repository**
```bash
git clone git@github.com:mnikander/cpp_sequence.git
cd cpp_sequence
```
### **3. Build and Run Tests**
```bash
mkdir out && cd out && cmake .. && cd ..
cmake --build out/ && ./out/unit_tests
```
---
## How It Works
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.
### Example: **Map-Filter-Reduce Pipeline**
```cpp
#include "src/sequence.hpp"
using namespace seq;
int result = 0;
auto pipeline = from_iota(
map([](int i) { return i - 3; },
filter([](int i) { return i % 2 == 0; },
reduce(std::plus{}, 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`, `filter`).
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](https://github.com/mnikander/cpp_sequence/tree/main/test)!
> 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"_.
### **Is it fast?**
A [simple benchmark](https://github.com/mnikander/cpp_sequence/blob/main/benchmark/reduce.benchmark.cpp) 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.
### **Why Not Just Use `std::ranges`?**
While `std::ranges` provides powerful sequence transformations, this library offers:
- **Generator-based execution** – Avoids [complex iterator issues](https://www.fluentcpp.com/2019/02/12/the-terrible-problem-of-incrementing-a-smart-iterator/)
- **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**