Flux and the C++20 library that replaces iterators with cursors
A C++20 library for sequence-orientated programming
At a glance
- What is it?
- tcbrindle/flux is a header-only C++20 library for sequence-oriented programming, built on a cursor model rather than STL iterators. The payoff is bounds checking and cursors that cannot dangle, the cost is a different mental model and a project whose only tagged release dates from 2024.
- Who is it for?
- Adopt Flux when your team writes or reviews its own sequence types and adaptors and wants bounds checking and safe cursors without giving up the C++20 ranges interface, since every Flux sequence is also a range. Skip it when the team wants to stay entirely inside STL iterators, or when a single tagged release is a procurement problem, because v0.4.0 on 2024-08-15 is the only GitHub release even though the last push was on 2026-09-15.
- Can I use it commercially?
- Yes. BSL-1.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 40 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The cursor model is the whole design
Flux is a C++20 library for sequence-oriented programming, and the README places it next to C++20 ranges, Python itertools and Rust iterators as something in the same family. The pitch it makes is a list of five promises: a large selection of algorithms and sequence adaptors for building data pipelines, better safety than standard library iterators and ranges, easier definition of your own sequences and adaptors, improved run-time efficiency for some common operations, and compatibility with existing standard library types and concepts.
What makes those claims possible is a single departure from the STL. Flux provides a feature set broadly equivalent to C++20 ranges, but its iteration model is built on cursors rather than iterators. The README states the difference in one line: Flux cursors are a generalisation of array indices, whereas STL iterators are a generalisation of array pointers.
That matters because an iterator is a self-contained object. It knows how to increment itself and how to dereference itself, and it carries its own state. A cursor carries no such knowledge. It is only a position, and every operation that uses it also receives the sequence it belongs to. The README summarises the asymmetry with a deliberately blunt line: STL iterators are smart, but Flux cursors are not.
The pipeline in the README's opening example shows what that buys in practice:
constexpr auto result = flux::ints() // 0,1,2,3,...
.filter(flux::pred::even) // 0,2,4,6,...
.map([](int i) { return i * 2; }) // 0,4,8,12,...
.take(3) // 0,4,8
.sum(); // 12
static_assert(result == 12);The whole chain is evaluated at compile time, and the result is checked by a static assertion.
Four basis operations instead of begin, ==, ++ and *
A Flux sequence exposes four basis operations, and they map cleanly onto the STL operations you already know. flux::first(seq) returns a cursor, which represents a position in the sequence, and for a sequence with N elements there are N+1 possible positions, including the terminal one that sits past the end. flux::is_last(seq, cursor) answers whether a cursor is in that terminal position. flux::inc(seq, cursor) advances a cursor to the next element. flux::read_at(seq, cursor) returns the element at a given position.
Compare those with begin(), iter == end(), ++iter and *iter, which is how the README maps them. The count is identical and the intent is identical. The difference is in the arguments: in the Flux model every call needs both the sequence and the cursor, whereas in the STL model the iterator has to know how to increment and dereference itself.
That single change to the argument list is where the safety properties come from. Because the sequence object is available during increment and dereference operations, Flux can offer inexpensive universal bounds checking for sequences, with a clearly marked opt-out where needed. Because a cursor is useless without its sequence, dangling cursors are impossible by design: if the sequence object is gone, the cursor cannot be used at all.
There is a second-order effect on invalidation. Since a cursor is only a position, like an integer index into an array, invalidation is much less likely when the underlying sequence is modified. If the element at that position no longer exists, the bounds check catches it at the next attempted read instead of leaving a silently wrong pointer. Finally, because element access requires the original sequence, Flux does not have to distinguish mutable iterators from const iterators. The same cursor type serves both, which the README argues makes cursors and sequences considerably simpler to implement than STL iterators and ranges.
From single pass to contiguous, and the bridge back to ranges
Like STL input ranges, basic Flux sequences are assumed to be single-pass by default. A single-pass sequence can be traversed once, which is all you need for a stream or a generator, but it rules out algorithms that look ahead.
Flux therefore provides sequence categories with more capability, each modelled on its STL counterpart. A multipass_sequence allows multiple cursors to iterate over the sequence independently, potentially passing over each position more than once. A bidirectional_sequence is a multipass sequence whose cursors can be decremented as well as incremented. A random_access_sequence is a bidirectional sequence whose cursors can move an arbitrary number of positions in constant time. A contiguous_sequence is a random-access sequence backed by a contiguous in-memory array.
The categories are cumulative in the same way the STL ones are, so choosing a sequence concept is choosing the weakest capability that fits, and the reference implementation is the familiar one.
The bridge back to the standard library matters as much as the categories themselves. The README states that Flux provides STL-compatible iterators so that every Flux sequence is also a C++20 range, meaning existing STL algorithms and range-for loops accept it as they would any other range. That is what keeps Flux from being an island: you can adopt it for the pipelines you want to be checked, keep the rest of your code on the standard library, and interoperate without adapters.
The repository backs this with a substantial set of examples, including example/calendar.cpp, example/histogram.cpp, example/merge_intervals.cpp, example/moving_average.cpp, example/prime_numbers.cpp, example/shortest_path.cpp, example/word_count.cpp and an example/top10/ directory, which suggests the library is exercised against the kind of problems where pipelines are the natural shape.
Three ways to get Flux into a build
Flux is header-only in practice, and the README gives three routes. The simplest is the single header: download the latest automatically generated file from the repository's single_include/flux.hpp path and include it alongside your own sources. That works with any C++ build system, including makefiles and hand-rolled scripts, with no dependency manager involved.
For CMake users, the README shows FetchContent, which downloads the library and keeps it up to date:
include(FetchContent)
FetchContent_Declare(
flux
GIT_REPOSITORY https://github.com/tcbrindle/flux.git
GIT_TAG main # Replace with a git commit id to fix a particular revision
)
FetchContent_MakeAvailable(flux)The comment in that snippet is worth taking seriously, since tracking a branch means your build changes when the branch moves. Pinning a commit identifier is the way to get a reproducible dependency. After that, the target is linked in the usual way:
target_link_libraries(my_target PUBLIC flux::flux)where my_target names the library or executable target you are building against Flux. If you have no CMake project yet, the README points to a separate starter project, flux_cmake_demo, for trying the library out.
The third route is a system package manager. Flux is available in vcpkg and the README gives the command:
vcpkg install fluxThat is the option to pick if your organisation already standardises on vcpkg, since it gives you a versioned package rather than a moving branch.
Compiler support is a real constraint. Flux requires a recent compiler with good support for C++20 and is tested with GCC 12 and newer, LLVM Clang 16 and newer, MSVC 2022, and AppleClang with Xcode 16. Older toolchains are not tested, and since the library leans on concepts, that boundary is not negotiable.
What the library leaves out of reach
Flux covers algorithms, adaptors and the sequence machinery. It does not cover the surrounding C++ ecosystem for you, and two omissions are worth planning around.
The first is your own data structures. A Flux sequence is something you write, with the cursor concepts implemented by hand or by adapting an existing container. The README's claim that Flux improves ease of use in common cases, particularly for defining your own sequences and adaptors, is a claim about how the concept protocol feels to implement, not a claim that existing containers are automatically compatible. Adopting Flux therefore means accepting that you will write sequence types, and the payoff of the simpler cursor model shows up in that code rather than in a drop-in container.
The second is the standard library's own iterator semantics. Because a cursor cannot exist without its sequence, code that stashes iterators into member variables and returns them from a function has to be rewritten in terms of sequence plus cursor pairs. That is a mechanical change but a wide one in a codebase built on the old pattern.
There is also a performance claim that deserves careful reading. The README promises improved run-time efficiency for some common operations, with the qualifier doing real work, and the repository contains a benchmark/ directory, so the project is measuring something. The right conclusion is that Flux is at least as fast on the operations it targets and can be slower on others, since bounds checking is not free in every case.
One release in 2024, commits in 2026, and a Boost licence
The release history is thin in a way that matters for adoption planning. The only GitHub release listed is v0.4.0, published on 2024-08-15. The last push to the repository was on 2026-09-15, so the code is being worked on, but there is no tagged 0.5 or 1.0 to point a version constraint at. A team that pins a dependency by release tag has exactly one anchor point available, and the FetchContent route in the README defaults to tracking the main branch rather than a tag.
For anyone who prefers to move to a stable tag, the practical route is the vcpkg package or a commit identifier in FetchContent, which means you take responsibility for choosing the revision rather than waiting for a release.
The licence is the Boost Software License 1.0, with the text stored as LICENSE_1_0.txt at the repository root and a badge pointing to the Boost licence page. BSL-1.0 is a permissive licence that allows use in proprietary and open source products, so adopting Flux does not oblige a team to open source the surrounding code; reading the licence text and having it reviewed by whoever handles licence questions is the appropriate step, not a legal opinion.
The homepage at tristanbrindle.com/flux/ carries the reference documentation for algorithms and adaptors, and the repository also ships a docs/ directory, an example/ tree, a test/ directory, a tools/ directory and a module/ directory alongside the include/ tree.
Editorial conclusion
Adopt Flux when your team writes or reviews its own sequence types and adaptors and wants bounds checking and safe cursors without giving up the C++20 ranges interface, since every Flux sequence is also a range. Skip it when the team wants to stay entirely inside STL iterators, or when a single tagged release is a procurement problem, because v0.4.0 on 2024-08-15 is the only GitHub release even though the last push was on 2026-09-15. Verify first that your compiler is in the tested set, GCC 12 or newer, LLVM Clang 16 or newer, MSVC 2022 or AppleClang with Xcode 16, and pin the revision in FetchContent rather than tracking the main branch, since the FetchContent example in the README tracks GIT_TAG main.
Frequently asked questions
How do I install the Flux C++ library?
The README gives three routes: download the generated single header from the repository's single_include/flux.hpp path and include it in your sources, use CMake FetchContent to pull the repository, or install the vcpkg package with vcpkg install flux.
What is the difference between a Flux cursor and an STL iterator?
Flux cursors are a generalisation of array indices rather than pointers, and every operation takes both the sequence and the cursor, while STL iterators know how to increment and dereference themselves. The README puts it as STL iterators being smart but Flux cursors not.
Which compilers does Flux support?
Flux requires a recent compiler with good C++20 support and is tested with GCC 12 and newer, LLVM Clang 16 and newer, MSVC 2022 and AppleClang with Xcode 16.
Can I use Flux sequences with standard library algorithms?
Yes. The README states that Flux provides STL-compatible iterators so that every Flux sequence is also a C++20 range, which means existing STL algorithms and range-for loops accept it directly.
What licence does Flux use?
The repository is licensed under the Boost Software License 1.0, with the licence text kept as LICENSE_1_0.txt at the root of the repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tcbrindle-flux)Was this useful?