#series
36 posts tagged #series.
What compile-time regex costs, measured
Moving work to compile time moves the bill there too. Including the CTRE header costs about three quarters of a second, and each distinct pattern adds to that. The library also declines some PCRE syntax and reports it through an error message that names the offset while wrapping it in template noise.
Quantities and units on the road to C++29
Everything this series showed is also a proposal: P3045, Quantities and units library, targets C++29 with mp-units as its reference implementation, led by Mateusz Pusz of Gdansk, an ISO C++ committee voting member since 2017. The series finale looks at what standardization takes, and at the Gdansk author behind the proposal.
Making your own type allocator-aware needs a second constructor
Put a pmr::string inside your own struct and it will allocate from the default resource unless the struct is allocator-aware: an allocator_type, an optional allocator argument in every constructor, and forwarding to members. This is uses-allocator construction, shown reaching a string nested in a pmr::vector. Episode 7 of the pmr series.
One function that fills any struct from any pattern
A regex with N captures already describes a record with N fields, and C++26 reflection can enumerate a struct's fields. Joining them gives a parse_into<T, Pattern>() that fills any aggregate with no per-struct code, which builds and runs today on the reflection fork.
A lexer that is one pattern and no allocations
An alternation where each branch is a capture group doubles as a token table: whichever capture engaged names the token kind. Built on tokenize, unmatched input stops the scan instead of being skipped, which is what a lexer wants. The whole thing runs at compile time as readily as at run time.
Iterating matches, and a manual that has fallen behind
search_all, split and tokenize cover scanning, separating and lexing, and the difference between the first and the last one decides which you want. The documentation still teaches ctre::range, which the compiler now reports as deprecated, so this episode is written against the header.
The unit travels with the value
Every codebase has a comment like 'speed in m/s' that stopped being true two refactors ago. mp-units plugs quantities into std::format so the unit is printed from the type, with format specs that address the number and the unit separately. Change the unit and the log line follows. Episode 5 of the mp-units series.
A new unit in one line
In 1958 MIT students measured the Harvard Bridge in the body lengths of Oliver Smoot: 364.4 smoots plus one ear. Teaching mp-units the smoot, symbol, exact conversion factor and all, takes one line, and conversions, printing, and derived quantities work immediately. Episode 4 of the mp-units series.
Points are not deltas
The temperature is 21 degrees and the temperature rose by 21 degrees are different quantities, and adding two of the first is meaningless. mp-units models this with the affine space: quantity_point for absolute readings, quantity for changes, and the nonsense operations do not compile. Episode 3 of the mp-units series.
One per second is not one per second
Frequency (Hz), radioactivity (Bq), and angular velocity (rad/s) all share the dimension 1/s, so a dimensions-only units library happily adds a monitor's refresh rate to a radiation reading. mp-units pioneered quantity kinds: same dimension, different meaning, and mixing them does not compile. Episode 2 of the mp-units series.
The unit bug that crashed into Mars
In 1999 the Mars Climate Orbiter was lost because one team's software spoke pound-force seconds and another's expected newton seconds. mp-units, a C++ quantities library, makes that whole bug class uncompilable. First post of a series that follows the library to its C++29 standardization bid.
WG21 now says new library types must ship a pmr alias
The worry that pmr is a C++17 experiment the committee abandoned is backwards. WG21's 2024 policy P3002 makes pmr the default vehicle for new allocating types, P1083 brings resource_adaptor in C++26, and P3153 makes optional allocator-aware. A live resource_adaptor demo bridges a classic allocator. Episode 8 of the pmr series.
Getting values out of a match
Numbered captures, named captures, and structured bindings that destructure a match directly. C++26 lets the binding itself be the condition, which removes the trailing test, though one compiler currently disagrees about that inside constant expressions.
The pmr crash is the buffer that died first
The most common pmr crash rarely comes from a custom resource; it is lifetime. The container deallocates back into its resource on destruction, so the resource must outlive it. A live demo shows the destruction order and the declaration-order rule that keeps it correct. Episode 6 of the pmr series.
Move-assigning pmr containers across arenas deep-copies
Move assignment should be an O(1) pointer steal. For pmr containers with different resources it cannot steal the buffer, so it allocates in the destination and copies every element, turning a move into an O(n) copy the type system cannot warn about. Episode 5 of the pmr series.
Copying a pmr::vector silently drops its allocator
A pmr::vector looks like a value type, but copy construction does not copy its allocator: select_on_container_copy_construction returns the default resource, so a copied container silently allocates from the global heap. The propagation traits make the allocator sticky. Episode 4 of the pmr series.
Does custom allocation still pay off in 2026?
Custom allocation used to buy 44 percent. A 2026 re-run of the classic study finds modern allocators like mimalloc have closed most of that gap, so a pmr arena now earns its place for predictable latency and fragmentation resilience rather than raw throughput. With a live allocation-count demo. Episode 3 of the pmr series.
A pmr::vector on a stack buffer never calls new
The HFT and game-loop promise of pmr, made testable: override operator new to count heap allocations, then fill a pmr::vector from a monotonic_buffer_resource backed by a stack buffer with null_memory_resource upstream. Ten thousand pushes, zero heap calls. Episode 2 of the pmr series.
std::pmr is one abstract class with three functions
The whole std::pmr framework is one abstract class, std::pmr::memory_resource, with three functions to override: allocate, deallocate, is_equal. A dozen-line logging resource shows a pmr::vector's allocations, then a monotonic_buffer_resource placed in front of it collapses them to zero. Episode 1 of the pmr series.
Validating a string before the program exists
CTRE matches run inside constant expressions, so a regex can check and parse a string literal while the compiler is still working. A malformed version string then stops the build rather than surviving into production, and the parsed result is available as a constant.
The fastest loggers do not format on the calling thread
Every fix in this series synchronizes on the calling thread. The fastest loggers do not: callers enqueue a raw record and a background thread formats and writes it. A mini producer-consumer logger, the spdlog/Quill/NanoLog numbers (around 250 ns versus 7 to 11 ns), and the crash-survivability trade-off. Episode 9 of the concurrent I/O series.
Thread-safe is not reentrant, and the difference bites
The Qt handler contract asked for a reentrant function, not just a thread-safe one, and a std::mutex gives you the second without the first. A demo shows a thread-safe counter and a reentrant recursion that needs a recursive_mutex. The precise taxonomy: reentrant implies thread-safe, never the reverse. Episode 8 of the concurrent I/O series.
The C++20 feature that gave CTRE its interface
Writing ctre::match<"[a-z]+"> requires a string literal to be a template argument, which C++20 allowed and every earlier standard did not. The older spelling still works and still compiles under C++17, so the two sit side by side and show exactly what the feature bought.
The hottest line is often not the one worth optimizing
A sampling profiler tells you where time is spent, which is not the same as where speeding things up would help. Causal profiling answers the second question directly, and it routinely disagrees with the first. A tour of the profilers worth knowing in 2026: Coz, Tracy, poop, samply, and plain perf.
You can see the dependency chain without running anything
Before reaching for a profiler, read what the compiler already produced. Two loops that add the same floats compile to eight adds into one register versus four independent accumulators, and llvm-mca will predict the throughput difference from that assembly without executing a single instruction. Episode 4 of the verification series.
RealtimeSanitizer checks the promise that a function never blocks
Mark a function [[clang::nonblocking]] and RealtimeSanitizer verifies at runtime that nothing inside it allocates, locks, or makes a syscall. It is the sanitizer for audio callbacks, control loops, and any deadline-bound code where a hidden malloc is a dropped frame. Episode 3 of the verification series.
The same assertion, as a unit test and as a fuzzer
Google FuzzTest lets one assertion serve as both a bounded property test that runs with your normal suite and a coverage-guided fuzzer you invoke on demand. Written against a deliberately buggy encoder, it found the counterexample without being asked to fuzz at all, and printed a regression test to paste back.
A real GoogleTest suite runs in your browser
GoogleTest is a Compiler Explorer library, so a real test binary builds and runs in the browser and prints the familiar RUN/OK report. That makes a shareable link the cheapest possible way to settle an argument about behavior. Episode 1 of the verification series, on assembling a C++ setup that catches bugs before your users do.
std::endl is a hidden flush, and clang-tidy flags it
std::endl is not a fancy newline; it is a newline plus a flush, and the flush is what costs you. A flush-counting streambuf proves that a newline flushes zero times and std::endl flushes every time. When to want the flush (crash-survivable logs), when not, and why clang-tidy flags it. Episode 7 of the concurrent I/O series.
Measuring std::regex against a compile-time matcher
std::regex has a reputation, and reputations are worth checking. Measured at its most favourable, with the pattern compiled once outside the timed loop, it takes about seventy times longer per match than CTRE. Include the construction that real code usually pays for and the gap widens by another order of magnitude. Episode 1 of a series on compile-time regular expressions and the language features that make them possible.
Ship the C++20 feature and its fallback in one file
The code review that started this series ended with a portability question: use std::osyncstream where it exists, fall back to a mutex where it does not. The feature-test macro __cpp_lib_syncbuf and <version> are the tool for exactly that. One file, the best available tool on each compiler. Episode 6 of the concurrent I/O series.
C++26 rewrote std::print's internals and backported the fix
std::print's per-call atomicity extends to your own types via std::formatter. How it stays both safe and efficient is a C++26 story: P3107 adds locking-aware entry points and a formatter opt-in to avoid deadlock, and ships as a backport into C++23. Episode 5 of the concurrent I/O series.
std::print does not tear the way std::cout does
The fixes so far needed cooperation or a lock. C++23's std::print gives per-call atomicity for free: a single print call never interleaves with another, the way printf never did and std::cout always could. But the guarantee is per-call, so a line split across two calls can still tear. Episode 4 of the concurrent I/O series.
static std::mutex is safe to construct, thanks to magic statics
Before osyncstream, the fix for a shared log sink was a static std::mutex. But is the static mutex itself safe to initialize under threads? Yes, thanks to C++11 magic statics: a function-local static is constructed exactly once even under a stampede. With the mutex-versus-osyncstream trade-off. Episode 3 of the concurrent I/O series.
std::osyncstream makes concurrent output atomic
std::cout garbled its own output across threads. C++20's std::osyncstream fixes it: each thread buffers a line privately and emits it to the stream atomically on destruction. One wrapper, whole lines, with one sharp edge: the guarantee holds only if every writer uses it. Episode 2 of the concurrent I/O series.
Your std::cout logging has no data race and still tears
Qt's message-handler contract says the handler must be reentrant: called from many threads at once. Point it at std::cout and the output tears, even though the C++ standard guarantees no data race. 'No data race' and 'no interleaving' are different promises, and this series closes the gap. Episode 1 of the concurrent I/O series.