The C++ Alliance
cppalliance.org.web.brid.gy
The C++ Alliance
@cppalliance.org.web.brid.gy
The C++ Alliance is dedicated to helping the C++ programming language evolve. We see it developing as an ecosystem of open source libraries and as a growing […]

[bridged from https://cppalliance.org/ on the web: https://fed.brid.gy/web/cppalliance.org ]
From Prototype to Product: MrDocs in 2025
In 2024, the project was a **fragile prototype**. It documented Boost.URL, but the **CLI** , **configuration** , and **build process** were unstable. Most teams could not run it without direct help from the core group. That unstable baseline is the starting point for this report. In 2025, we moved the codebase to **minimum-viable-product** shape. I led the releases that stabilized the pipeline, **aligned the configuration model** , and documented the work in this report to support a smooth **leadership transition**. This post summarizes the **2024 gaps** , the **2025 fixes** , and the **directions** for the next phase. * System Overview * 2024: Lessons from a Fragile Prototype * 2025: From Prototype to MVP * v0.0.3: Enforcing Consistency * v0.0.4: Establishing the Foundation * v0.0.5: Stabilization and Public Readiness * 2026: Beyond the MVP * Strategic Prioritization * Reflection * Metadata * Extensions and Plugins * Dependency Resilience * Follow-up Issues for v0.0.6 * Acknowledgments * Conclusion ## System Overview MrDocs is a C++ documentation generator built on **Clang** and **LLVM**. It parses source with full language fidelity, links declarations to their comments, and produces reference documentation that reflects real program structure—**templates** , **constraints** , and **overloads** included. > Traditional tools often approximate the AST. MrDocs uses the AST directly, so documentation matches the code and modern C++ features render correctly. Unlike single-purpose generators, MrDocs separates the **corpus** (semantic data) from the **presentation layer**. Projects can choose among multiple **output formats** or extend the system entirely: supply **custom Handlebars templates** or script new generators using the **plugin system**. The corpus is represented in the generators as a **rich JSON-like DOM** , enabling integration with other **build systems** , **documentation frameworks** , or **IDEs**. From the user’s perspective, MrDocs behaves like a **well-engineered CLI utility**. It accepts **configuration files** , supports **relative paths** , includes **deterministic build options** , and reports **warnings** in a controlled, **compiler-like** fashion. For C++ teams transitioning from **Doxygen** , the **command structure** is somewhat familiar, but the **internal model** is designed for **reproducibility** and **correctness**. Our goal is not just to render **reference pages** but to provide a **reliable pipeline** that any C++ project seeking **modern documentation infrastructure** can adopt. ## 2024: Lessons from a Fragile Prototype MrDocs entered 2024 as a proof of concept built for Boost.URL. It could walk one or two curated codebases and produce Antora pages, but the workflow stopped there. The CLI exposed only the scenarios we needed. Configuration options lived in internal notes. The only dependable build path was the script sequence we used inside the Alliance. External users hit errors and missing options almost immediately. **Stability was just as fragile:** We had no **sanitizers** , no **warnings-as-errors** , and inconsistent **CI hardware**. The binaries crashed as soon as they saw unfamiliar code. The pipeline worked only when the input looked like Boost.URL. Point it at different templates or a library with heavy Concepts usage and it would segfault before extraction finished. Each feature landed as a custom patch, so logic duplicated across generators, and fixing one path broke another. **Early releases:** Release `v0.0.1` captured that prototype: the early Handlebars engine, the HTML generator, the DOM refactor, and a list of APIs that only the core team could drive. `v0.0.2` added structured configuration, automatic `compile_commands.json`, and better SFINAE handling, but the tool was still insider-only. **Leadership transition:** Late in 2024 I became project lead with two priorities: **document the gaps** and describe the **true limits** of the system. That set the 2025 baseline—a functional prototype that needed **coherence** , **reproducibility** , and **trust** before it could call itself a product. **What 2025 later fixed were the weaknesses we saw here:** configuration coherence, generator unification, schema validation, and warning governance were all missing. The CLI, configuration files, and code drifted from each other. Generators evolved independently with duplicated templates and inconsistent naming. Editors had no schema to lean on, so validation and completion were impossible. Extraction rules were ad hoc, which made the output incomplete. CI ran on an improvised matrix with no caching, sanitizers, or coverage, so regressions slipped through. That was the starting point. > Summary: 2024 produced a working demo, not a reproducible system. Each success exposed another weak link and clarified what had to change in 2025. In short: * 2024 left us with a working prototype but no coherent architecture. * The system could demonstrate the concept, but not sustain or reproduce it. * Every improvement exposed another weak link, and every success demanded more structure than the system was built to handle. * It was a year of learning by exhaustion—and setting the stage for everything that came next. # 2025: From Prototype to MVP I started the year with a gap analysis that compared MrDocs to other C++ documentation pipelines. From that review I defined the minimum viable product and three priority tracks. **Usability** covered workflows and surface area that make adoption simple. **Stability** covered deterministic behavior, **warning governance** , and CI discipline. **Foundation** covered configuration and data models that keep code, flags, and documentation aligned. The 2025 releases followed those tracks and turned MrDocs from a proof of concept into a tool that other teams can adopt. * **v0.0.3 — Consistency.** We replaced ad-hoc behavior with a coherent system: a single source of truth for configuration kept CLI, config files, and docs in sync; generators and templates were unified so changes propagate by design; core semantic extraction (e.g., concepts, constraints, SFINAE) became reliable; and CI hardened around reproducible, tested outputs across HTML and Antora. * **v0.0.4 — Foundation.** We introduced precise warning controls and a family of `extract-*` options to match established tooling, added a JSON Schema for configuration (enabling editor validation/autocomplete), delivered a robust reference system and Javadoc metadata integration, brought initial inline formatting to generators, and simplified onboarding with a cross-platform bootstrap script. CI gained sanitizers, coverage checks, modern compilers, and relocatable builds. * **v0.0.5 — Stabilization.** We redesigned documentation metadata to support recursive inline elements, enforced safer polymorphic types with optional references and non-nullable patterns, and added user-facing improvements (sorting, automatic compilation database detection, quick reference indices, improved namespace/overload grouping, LLDB formatters). The website and documentation UI were refreshed for accessibility and responsiveness, new demos (including self-documentation) were published, and CI was further tightened with stricter policies and cross-platform bootstrap enhancements. Together, these releases executed the roadmap derived from the initial gap analysis: they **aligned** the moving parts, **closed** the most important capability gaps, and delivered a **stable foundation** that future work can extend without re-litigating fundamentals. ## v0.0.3: Enforcing Consistency `v0.0.3` is where MrDocs stopped being a collection of one-off special cases and became a coherent system. Before this release, features landed in a single generator and drifted from the others; extraction handled only the narrowly requested pattern and crashed on nearby ones; and options were inconsistent—some hard-coded, some missing from CLI/config, with no mechanism to keep code, docs, and flags aligned. **What changed:** The `v0.0.3` release fixes this foundation. We introduced a single source of truth for **configuration options** with TableGen-style metadata: docs, the config file, and the CLI always stay in sync. We added essential Doxygen-like options to make basic projects immediately usable and filled obvious gaps in symbols and doc comments. We implemented metadata extraction for **core symbol types** and their information—such as template constraints, **concepts** , and **automatic SFINAE** detection. We **unified generators** and templates so changes propagate by design, added **tagfile support** and “lightweight reflection” to documentation comments as **lazy DOM objects** and arrays, and **extended Handlebars** to power the new generators. These features allowed us to create the initial version of the **website** and ensure the documentation is always in sync. **Build and testing discipline:** CI, builds, and tests were hardened. All generators were now tested, **LLVM caching** systems improved, and we launched our first **macOS release** (important for teams working on Antora UI bundles). All of this long tail of performance, correctness, and safety work turned “works on my machine” into repeatable, adoptable output across HTML and Antora. `v0.0.3` was the inflection point. For the first time, developers could depend on consistent configuration, **shared templates** , and predictable behavior across generators. It aligned internal tools, eliminated duplicated effort, and replaced trial-and-error debugging with **reproducible builds**. Every improvement in later versions built on this foundation. Categorized improvements for v0.0.3 * **Configuration Options** : enforcing consistency, reproducible builds, and transparent reporting * Enforce configuration options are in sync with the JSON source of truth (a1fb8ec6, 9daf71fe) * File and symbol filters (1b67a847, b352ba22) * Reference and symbol configuration (a3e4477f, 30eaabc9) * Extraction options (41411db2, 1214d94b) * Reporting options (f994e47e, 0dd9cb45) * Configuration structure (c8662b35, dcf5beef, 4bd3ea42) * CLI workflows (a2dc4c78, 3c0f90df) * Warnings (4eab1933, 5e586f2b, 0e2dd713) * SettingsDB (225b2d50, 51639e77) * Deterministic configuration (b5449741) * Global configuration documentation (ec3dbf5c) * **Generators** : unification, new features, and early refactoring * Antora/HTML generator consistency (e674182f, 82e86a6c, 9154b9c5) * HTML generator improvements (a28cb2f7, 064ce55a, 5f6665d8) * Documentation for generators (2382e8cf, 646a1e5b) * Supporting new output formats (58a79f74, 271dde57, 9d9f6652) * Handlebars improvements (ebf4dbeb, be76fc07) * Generator tooling (00fc84cf, 6a69747d) * Navigation helpers (fdccad42) * DOM optimizations (9b41d2e4) * **Libraries and metadata** : unification, fixes, and extraction enhancements * Info node visitor and traversal improvements (be86a08d, 58ab5a5e) * Metadata consistency (544ee37d, 62f8a2bd, bd9c704f) * Template and concept support (4b0b4a71, 57cf74de, 92aa76a4) * Symbol resolution and references (f64d4a06, aa9333d4) * Documentation improvements (5d3f21c8) * **Website and Documentation** : turning features into a showcase and simplifying workflows * Create website (05400c3c, 8fba2020) * Use the new features to create an HTML panel demos workflow (12ceadee, d38d3e1a, c46c4a91) * Unify Antora author mode playbook (999ea4f3) * Generator use cases and trade-offs (2307ca6a) * Correctness and simplification (4d884f43, 55214d72, b078bead, d8b7fcf4, 96484836, 62f361fb) * **Build, Testing, and Releases** : strengthening CI, improving LLVM caching workflow, and stabilizing releases * Templates are tested with golden tests (2bc09e65, 9eece731) * LLVM caches and runners improvements (4c14e875, bd54dc7c, 3d92071a, 8537d3db, f3b33a47, 5982cc7e, 93487669) * Enable macOS workflow (390159e3) * Stabilize artifacts (5e0f628e, d1c3566e, 62736e45) * Tests support individual file inputs, which improved local tests considerably (75b1bc52) * Performance, correctness, and safety (a820ad79, 43e5f252, a382820f, fbcb5b2d, 6a2290cb, 49f4125f) ## v0.0.4: Establishing the Foundation `v0.0.4` completed the core capabilities we need for production. With the moving parts aligned in `v0.0.3`, this release focused on the fundamentals. It added consistent **warning governance** , **extraction controls** that match established tools, **schema support** for IDE auto-completion, a complete **reference system** , and initial **inline formatting** in the generators. The **bootstrap script** became a one-step path to a working build. We also hardened the pipeline with modern **CI** practices—sanitizers, coverage integration, and standardized presets. Categorized improvements for v0.0.4 * **Configuration and Extraction** : structured configuration, extraction controls, and schema validation * Configuration schema (d9517e1d, 5f846c1c, ffa0d1a6) * Extraction filters (0a60bb98, a7d7714d) * Reference configuration (d18a8ab3) * Documentation metadata (6676c1e8) * **Warnings and Reporting** : consistent governance with CLI parity * Warning controls (2a29f0a0, 6d3c1f47) * Extract options (`extract-{public,protected,private,inline}`) (aa5a6be3) * CLI defaults (d85439c3) * **Generators** : Javadoc, inline formatting, and reference improvements * Documentation reference system (4b430f9b, 73489e2b) * Javadoc metadata (8dd3af67, f7e59d4c) * Inline formatting (5c7490a3, d1d80745) * XML generator alignment (9867e0d2, 0f890f2c) * **Build and CI** : sanitizers, coverage, and reproducible builds * Sanitizer integration (6257c747, 88954d7f) * Coverage reporting (bf195759) * Relocatable build (`std::format`) (7b871032) * Bootstrap modernization (3eec9a48, 71afb87b, 524e7923) ## v0.0.5: Stabilization and Public Readiness `v0.0.5` marked the transition toward a **sustained development model** and prepared the project for **handoff**. This release focused on **presentation** , **polish** , and **reliability** —ensuring that MrDocs was ready not only for internal use but for public visibility. During this period, we expanded the set of **public demos** , refined the **website and documentation** , and stabilized the **infrastructure** to support a growing user base. The goal was to leave the project in a state where it could continue evolving smoothly, with a stable core, clear development practices, and a professional public face. **Community and visibility** : Beyond the commits, this release reflected broader **activity around the project**. We generated and published several **new demos** , many of which revealed **integration issues** that were subsequently fixed. As more external users began adopting MrDocs, the **feedback loop accelerated** : bug reports, feature requests, and real-world **edge cases** guided much of the work. New contributors joined the team, collaboration became more distributed, and visibility increased. Around the same time, I introduced MrDocs to developers at **CppCon 2025** , where it received strong feedback from library authors testing it on their own projects. The tool was beginning to gain recognition as a **viable, modern alternative to Doxygen**. **Technical progress** : This release focused on correctness. We redesigned the documentation comment data structures to support **recursive inline elements** and render **Markdown and HTML-style formatting** correctly. We moved to **non-nullable polymorphic types** and **optional references** so that invariants fail at compile time rather than at runtime. User-facing updates included new **sorting options** , **automatic compilation database detection** , a **quick reference index** , broader namespace and overload grouping, and **LLDB formatters** for Clang and MrDocs symbols. We **refreshed the website and documentation UI** for accessibility and responsiveness, added new **demos** (including the MrDocs self-reference), and tightened CI with more sanitizers, stricter warning policies, and cross-platform bootstrap improvements. Together, these improvements completed the transition from a **developing prototype** to a **dependable product**. `v0.0.5` established a **stable foundation** for others to build on—**polished** , **documented** , and **resilient** —so future releases could focus on extending capabilities rather than consolidating them. With this release, the project reached a point where the **handoff could occur naturally** , closing one chapter and opening another. Categorized improvements for v0.0.5 * **Metadata** : documentation inlines and safety improvements * Recursive documentation inlines (51e2b655) * Consistent sorting options for members and namespaces (`sort-members-by`, `sort-namespace-members-by`) (f0ba28dd, a0f694dc) * Non-nullable polymorphic types and optional references (c9f9ba13, 8ef3ffaf, bd3e1217, afa558a6, 6ba8ef6b) * Consistent metadata class family hierarchy pattern (6d495497) * MrDocsSettings includes automatic compilation database support (9afededb, a1f289de) * Quick reference index (68e029c1, 940c33f4) * Namespace/using/overloads grouping includes using declarations and overloads as shadows (69e1c3bc, d722b7d0, 2b59269c) * Conditional `explicit` clauses in templated methods (2bff4e2f) * Destructor overloads supported in class templates (336ad319) * Using declarations include all shadow variants (88a1cebf, 9253fd8f, a7d5cf6a) * `show-enum-constants` option (07b69e1c) * Custom LLDB formatters for Clang and MrDocs symbols (069bd8f4, f83eca17, 1b39fdd7, aefc53c7) * Performance, correctness, and safety (d1788049, 3bd94cff, 8a811560, 3ff37448, ad1e7baa, b10b8aa3, 482c0be8, d66da796, ec8daa11, 5234b67c, 5e879b10, 35e14c93, d5a28a89, 6878c199, 21ce3e74, 2da2081b, b528ae11) * **Website and Documentation** : new demos and a new website * New demos (cfa9eb7d, 1b930b86, c18be83e, 177fae4a, 33275050) * Website and documentation refresh (35e14c93, a6437742) * Self-documentation (f2a5f77e) * Antora enhancements (5ed0f48f) * **Build, Testing, and Releases** : improvements and hardening CI * Toolchain and CI hardening (6257c747, 88954d7f, bf195759, ba0dcfd3) * Bootstrap improvements (3eec9a48, 71afb87b, 524e7923, 4b79ef41, 7d27204e, 988e9ebc, 94a5b799, be7332cf, 4d705c96, f48bbd2f, f9363461) * Performance, correctness, and safety (5aa714b2, 469f41ee, 629f1848, 2f0dd8c1, acf7c107) # 2026: Beyond the MVP MrDocs now ships a working MVP, but significant **foundational work** remains. The priority framework is the same: start with **gap analysis** , shape an **MVP** (or now just a viable product), and rank follow-on work against that baseline. In 2025 we invested in **presentation** earlier than **infrastructure**. That inversion still raises costs: each foundational change forces rework across user-facing pieces. I do not know how the leadership model will evolve in 2026. The team might keep a single coordinator or move to shared stewardship. Regardless, the project only succeeds if we continue investing in **foundational capabilities**. The steps below outline the **recommendations** I believe will help keep MrDocs **sustainable over the long term**. ## Strategic Prioritization Aligning **priorities** is itself the highest priority. At the start of my tenure as project lead we followed a strict sequence—**gap analysis** , then an **MVP** , then a set of **priorities** —but that model exposed limitations once work began to land. The **issue tracker** does not reflect how priorities relate to each other, and as individual tickets close the priority stack does not adjust automatically. The project’s **complexity** now amplifies the risk: without a clear view of **dependencies** we can assign a high-value engineer to a task that drags several teammates into the same bottleneck, resulting in net-negative progress. Defining priorities therefore includes understanding the team’s **skills** , mapping how they **collaborate** , and making sure no one becomes a **sink** that blocks everyone else. **Alignment** across roles remains essential so the plan reflects the people who actually execute it. The **tooling** already exists to put this into practice. **GitHub** now lets us mark issues as **blocked by** or **blocking** others and to model **parent/child relationships**. We can use those relationships to **reorganize the priorities programmatically**. Once the relationships are encoded, **priorities gain semantic meaning** because we can explain why a small ticket matters in the larger story. Priorities become the **byproduct of higher-level goals** — narratives about the product—rather than a short-term **static wish list** of individual features. We also need to strengthen the **operational tools** that keep the team coordinated. **Coverage** in CI is still far below our other C++ Alliance projects, and the gap shows up as crashes whenever a new library explores an untested path in the codebase. Improving coverage is a priority in its own right. We can pair that effort with **automation** and **analysis tools** like **ReviewDog** to accelerate code-review feedback, **Danger.js** to enforce pull-request policies, **CodeClimate** or similar services for **static analysis** , and **clang-tidy** checks to catch issues earlier. Finally, we can invite other collaborators to revisit the **gap analysis** and **MVP** , including C++Alliance colleagues who specialize in **marketing**. Their perspective will help us assign priorities that reflect both **technical dependencies** and the project’s **broader positioning**. ## Reflection The corpus keeps drifting out of sync because every important path in MrDocs duplicates representation by hand. Almost every subsystem reflects data from one format to another, and almost every internal operation traverses those structures. Each time we adjust a field we have to edit dozens of call sites, and even small mistakes create inconsistent state—different copies of the “truth” that evolve independently. Reflection eliminates this churn. If we can describe the corpus once and let the code iterate over those descriptions, the boilerplate disappears, the traversals remain correct, and we stop fighting the same battle. A lightweight option would be to enforce the corpus from JSON the way we treat configuration, but the volume of metadata in AST makes that impractical. Instead, we lean on **compile-time reflection utilities** such as **Boost.Describe** and **Boost.mp11**. With those libraries we can convert the corpus to any representation, and each generator—including future **binary** or **JSON** targets—sees the same schema automatically. MrDocs can even emit the schema that powers each generator, keeping the schema, DOM, and documentation in sync. This approach also fixes the long-standing lag in the **XML generator** , where updates have historically been manual and error-prone. **Use cases:** We can start by describing the **Symbols** , **Javadoc** , and related classes, shipping each refactor as a dedicated PR so reviews stay contained. Each description removes custom specializations, reverts to `= default` where possible, and replaces old logic with **static asserts** that enforce invariants. We generalize the main merge logic first, then update callers such as the **AST visitor** that walks `RecordTranche`, ensuring the **Javadoc structure** matches the new descriptions. A `MRDOCS_DESCRIBE_DERIVED` helper can enumerate derived classes so every visit routine becomes generic. Once the C++ side is described, we rebuild the lazy DOM objects on top of Describe so their types mirror the DOM layout directly. Redundant non-member function like `tag_invoke`, `operator⇔`, `toString`, and `merge` collapse into **shared implementations** that use traits only when real customization is required. New generators—binary, JSON, or otherwise—drop in with minimal code because the schema and traversal logic already exist. The XML generator stops maintaining a private representation and simply reads the described elements. We can finally standardize **naming conventions** (kebab-case or camelCase) because the schema enforces them. Generating the **Relax NG Compact** file becomes just another output produced from the same description. A metadata walker can then discover auxiliary objects and emit **DOM documentation automatically**. As a side effect of integrating Boost.mp11, we can extend the `tag_invoke` context protocol with tuple-based helpers for `mrdocs::FromValue`, further narrowing the gap between concrete and DOM objects. ## Metadata MrDocs still carries metadata gaps that are too large to ignore. The subsections below highlight the three extraction areas that demand sustained effort; each of them blocks the rest of the system from staying consistent. **Recursive blocks and inlines.** Release 0.0.5 introduced the data structures for recursive Javadoc elements, but we still do not parse those structures. The fix is straightforward in concept—extend the CommonMark-based parser so every block and inline variant becomes a first-class node—but the implementation is long because there are many element types. We can chip away incrementally by opening issues and sub-issues, tackling one structure per PR, and starting with block elements before moving to inlines. The existing `parse_{rule}` helpers already contain the mechanics; we just need to wire each rule into the new Javadoc nodes. **Legible names.** The current name generator appends hash fragments to differentiate symbols lazily, which makes references unstable and awkward. We need a stable allocator that remembers which symbols claimed which names. The highest-priority symbol should receive the base name, and suffixes should cascade to less critical overloads so the visible entries stay predictable. Moving the generator into the extraction phase and storing the assignments there ensures anchors remain stable, lets us update artifacts such as the Boost.URL tagfile, and produces names that actually read well. **Populate expressions.** Whenever the extractor fails to recognize an expression, it falls back to the raw source string. That shortcut prevents us from applying the usual transformations, especially inside requires-expressions where implementation-defined symbols appear. We should introduce typed representations for the constructs we already understand and continue to store strings for the expressions we have not modeled yet. As coverage grows, more expressions flow through the structured pipeline, and the remaining string-based nodes shrink to the truly unknown cases. ## Extensions and Plugins **Extensions** and **plugins** aim at the same outcome—letting projects **customize MrDocs** —but they operate at different layers. Extensions run **inside the application** , usually through **interpreters** we bundle. We already ship **Lua** and **Duktape** , yet today they only power a handful of **Handlebars helpers**. The plan is to widen that surface: add more interpreters where it makes sense, extend helper support so extensions can participate in **escaping** and **formatting** , and give extensions the ability to **consume the entire corpus**. With that access, an extension can list every symbol, emit metadata in formats we do not yet support, or transform the corpus before it reaches a native generator. The same mechanism enables **quality-of-life utilities** , such as a generator extension that checks whether a library’s public API changed according to a policy defined in code. **Plugins** , by contrast, are **compiled artifacts**. They unlock similar customization goals, but their **ABI must stay stable** , and platform differences mean a plugin built on one system will not run on another. To keep the surface manageable we should expose a **narrow wrapper** : pass plugins a set of **DOM proxies** so they never depend on the underlying **Info classes** , use **traits** or **versioned interfaces** to handle incompatibilities, and **plan the API carefully** before release. **Python** remains a middle path that bridges both approaches. By documenting a **Python extension layer** , we provide interpreted helpers that are easy to write, support Handlebars customization, and give teams a quick way to automate workflows before they invest in a compiled plugin. ## Dependency Resilience Working with **dependent libraries** is still the most fragile part of the MrDocs workflow. **Environments drift** , **transitive dependencies change** without notice, and heavyweight projects force us to install **toolchains** we do not actually need. In **Boost.URL** alone we watch upstream Boost libraries evolve every few weeks; sometimes the code truly breaks, but just as often a new release exercises an untested path in MrDocs and triggers a crash because our **coverage** is still thin. Other ecosystems push the cost even higher: documenting a library that depends on **LLVM** can turn a three-second render into an hours-long process because the **headers** are generated at build time, so we must compile and install LLVM merely to obtain include files. **CI environments** regularly fail for the same reason. We already experimented with **mitigation strategies** and should refine them rather than abandon the ideas. Shipping a **curated standard library** with MrDocs removes one entire category of instability. The option will soon be disabled by default, but users can still enable it or even combine it with the system library when **reproducibility** matters more than **absolute fidelity**. This mirrors how **Clang** ships **libc++** ; it does not allow invalid code, it simply guarantees a known baseline. On top of that, we have preliminary support for **user-defined stubs**. **Configuration files** can provide short descriptions of expected symbols from hard-to-build dependencies, and MrDocs can **inject those during extraction**. For predictable patterns we can **auto-generate stubs** when the user opts in, synthesizing symbols rather than failing immediately. None of this accepts invalid code—the compiler still diagnoses real errors—but it shields projects from breakage when a **transitive dependency** tweaks implementation details or when generated headers are unavailable. The features remain **optional** , so teams can disable synthesis to debug the underlying issue and still benefit from the faster path when schedules are tight. Even if the project moves in another direction we should **document the proposal** and remove the existing stub hooks deliberately rather than letting them linger undocumented. The payoffs are clear. **Boost libraries** could generate documentation without cloning the entire super-project, relying on **SettingsDB** to produce a **compilation database** and skipping **CMake** entirely. MrDocs itself could publish reference docs without building **LLVM** because the required symbols would come from curated stubs. **Release engineering** would stop grinding to a halt every time a transitive dependency changes, and developers would regain hours currently spent firefighting. These are the **stability** and **reproducibility** gains we need if we want MrDocs to be the **default tooling** for large C++ ecosystems. ## Follow-up Issues for v0.0.6 To keep this post focused on the big-picture transition, I spun the tactical tasks into GitHub issues for the 0.0.6 milestone. They’re queued up and ready for execution whenever the team circles back to implementation. List of follow-up issues for v0.0.6 * #1081 Support custom stylesheets in the HTML generator * #1082 Format-agnostic Handlebars generator extension * #1083 Allow SettingsDB to describe a single source file * #1084 Guard against invalid source links * #1085 Complete tests for all using declaration forms * #1086 Explore a recursive project layout * #1087 Convert ConfigOptions.json into a schema file * #1088 Separate parent context and parent page * #1089 List deduction guides on the record page * #1090 Expand coverage for Friends * #1091 Remove dependency symbols after finalization * #1092 Review Bash Commands Parser * #1093 Review NameInfoVisitor * #1094 Improve overload-set documentation * #1095 CI uses the bootstrap script * #1096 Connect Antora extensions * #1097 Handlebars: optimize render state * #1098 Handlebars: explore template compilation * #1099 Handlebars: investigate incremental rendering # Acknowledgments **Matheus Izvekov** and **Krystian Stasiowski** kept the Clang integration moving. Their expertise cleared issues that would have stalled us. **Gennaro Prota** and **Fernando Pelliccioni** handled the maintenance load that kept the project on schedule. They took on the long tasks and followed them through. **Robert Beeston** and **Julio Estrada** delivered the public face of MrDocs. The site we ship today exists because they turned open-ended goals into a complete experience. **Vinnie Falco** , **Louis Tatta** , and **Sam Darwin** formed the backbone of my daily support. **Vinnie** trusted the direction and backed the plan when decisions were difficult. **Louis** made sure I had space to return after setbacks. **Sam** kept the Alliance infrastructure running so the team always had what it needed. **Ruben Perez** , **Klemens Morgenstern** , **Peter Dimov** , and **Peter Turcan** offered candid feedback whenever we needed another perspective. Their observations sharpened the product and kept collaboration positive. **Joaquín M López Muñoz** and **Arnaud Bachelier** guided me through the people side of leadership. Their advice turned complex situations into workable plans. Working alongside everyone listed here has been a privilege. Their contributions made this year possible. # Conclusion The 2025 releases unified the generators, locked the configuration model, added sanitizers and coverage to CI, and introduced features that make the tool usable outside Boost.URL. The project is ready for new contributors because they can extend the code without rebuilding the basics, and downstream teams can run the CLI on large codebases and expect predictable output. While we delivered those releases, I learned that engineering progress depends on steady communication. Remote discussions often sound negative even when people agree on the goals, so I schedule short check-ins, add light signals like emojis, and keep space for conversations that are not task-driven. I also protect time to listen and ask for help when the workload gets heavy; if I lose that time, every deadline slips anyway. **Final Reflections** * Technical conversations start negative by default, so add clear signals when you agree or appreciate the work. * Assume terse feedback comes from the medium, not the person, and respond with patience. * Keep informal connection habits—buddy calls, breaks, or quick chats—to maintain trust. * Look after your own health and use outside support when needed. * Never allow the schedule to block real listening time; reset your calendar when that happens.
cppalliance.org
October 30, 2025 at 2:59 AM
Making the Clang AST Leaner and Faster
Modern C++ codebases — from browsers to GPU frameworks — rely heavily on templates, and that often means _massive_ abstract syntax trees. Even small inefficiencies in Clang’s AST representation can add up to noticeable compile-time overhead. This post walks through a set of structural improvements I recently made to Clang’s AST that make type representation smaller, simpler, and faster to create — leading to measurable build-time gains in real-world projects. * * * A couple of months ago, I landed a large patch in Clang that brought substantial compile-time improvements for heavily templated C++ code. For example, in stdexec — the reference implementation of the `std::execution` feature slated for C++26 — the slowest test (`test_on2.cpp`) saw a **7% reduction in build time**. Also the Chromium build showed a **5% improvement** (source). At a high level, the patch makes the Clang AST _leaner_ : it reduces the memory footprint of type representations and lowers the cost of creating and uniquing them. These improvements will ship with **Clang 22** , expected in the next few months. * * * ## How elaboration and qualified names used to work Consider this simple snippet: namespace NS { struct A {}; } using T = struct NS::A; The type of `T` (`struct NS::A`) carries two pieces of information: 1. It’s _elaborated_ — the `struct` keyword appears. 2. It’s _qualified_ — `NS::` acts as a _nested-name-specifier_. Here’s how the AST dump looked before this patch: ElaboratedType 'struct NS::A' sugar `-RecordType 'test::NS::A' `-CXXRecord 'A' The `RecordType` represents a direct reference to the previously declared `struct A` — a kind of _canonical_ view of the type, stripped of syntactic details like `struct` or namespace qualifiers. Those syntactic details were stored separately in an `ElaboratedType` node that wrapped the `RecordType`. Interestingly, an `ElaboratedType` node existed even when no elaboration or qualification appeared in the source (example). This was needed to distinguish between an explicitly unqualified type and one that lost its qualifiers through template substitution. However, this design was expensive: every `ElaboratedType` node consumed **48 bytes** , and creating one required extra work to uniquify it — an important step for Clang’s fast type comparisons. * * * ## A more compact representation The new approach removes `ElaboratedType` entirely. Instead, elaboration and qualifiers are now stored **directly inside`RecordType`**. The new AST dump for the same example looks like this: RecordType 'struct NS::A' struct |-NestedNameSpecifier Namespace 'NS' `-CXXRecord 'A' The `struct` elaboration now fits into previously unused bits within `RecordType`, while the qualifier is _tail-allocated_ when present — making the node variably sized. This change both shrinks the memory footprint and eliminates one level of indirection when traversing the AST. * * * ## Representing `NestedNameSpecifier` `NestedNameSpecifier` is Clang’s internal representation for name qualifiers. Before this patch, it was represented by a pointer (`NestedNameSpecifier*`) to a uniqued structure that could describe: 1. The global namespace (`::`) 2. A named namespace (including aliases) 3. A type 4. An identifier naming an unknown entity 5. A `__super` reference (Microsoft extension) For all but cases (1) and (5), each `NestedNameSpecifier` also held a _prefix_ — the qualifier to its left. For example: Namespace::Class::NestedClassTemplate<T>::XX This would be stored as a linked list: [id: XX] -> [type: NestedClassTemplate<T>] -> [type: Class] -> [namespace: Namespace] Internally, that meant **seven allocations** totaling around **160 bytes** : 1. `NestedNameSpecifier` (identifier) – 16 bytes 2. `NestedNameSpecifier` (type) – 16 bytes 3. `TemplateSpecializationType` – 48 bytes 4. `QualifiedTemplateName` – 16 bytes 5. `NestedNameSpecifier` (type) – 16 bytes 6. `RecordType` – 32 bytes 7. `NestedNameSpecifier` (namespace) – 16 bytes The real problem wasn’t just size — it was the _uniquing cost_. Every prospective node has to be looked up in a hash table for a pre-existing instance. To make matters worse, `ElaboratedType` nodes sometimes leaked into these chains, which wasn’t supposed to happen and led to several long-standing bugs. * * * ## A new, smarter `NestedNameSpecifier` After this patch, `NestedNameSpecifier` becomes a **compact, tagged pointer** — just one machine word wide. The pointer uses 8-byte alignment, leaving three spare bits. Two bits are used for kind discrimination, and one remains available for arbitrary use. When non-null, the tag bits encode: 1. A type 2. A declaration (either a `__super` class or a namespace) 3. A namespace prefixed by the global scope (`::Namespace`) 4. A special object combining a namespace with its prefix When null, the tag bits instead encode: 1. An empty nested name (the terminator) 2. The global name 3. An invalid/tombstone entry (for hash tables) Other changes include: * The “unknown identifier” case is now represented by a `DependentNameType`. * Type prefixes are handled directly in the type hierarchy. Revisiting the earlier example, after the patch its AST dump becomes: DependentNameType 'Namespace::Class::NestedClassTemplate<T>::XX' dependent `-NestedNameSpecifier TemplateSpecializationType 'Namespace::Class::NestedClassTemplate<T>' dependent `-name: 'Namespace::Class::NestedClassTemplate' qualified |-NestedNameSpecifier RecordType 'Namespace::Class' | |-NestedNameSpecifier Namespace 'Namespace' | `-CXXRecord 'Class' `-ClassTemplate NestedClassTemplate This representation now requires only **four allocations (156 bytes total):** 1. `DependentNameType` – 48 bytes 2. `TemplateSpecializationType` – 48 bytes 3. `QualifiedTemplateName` – 16 bytes 4. `RecordType` – 40 bytes That’s almost half the number of nodes. While `DependentNameType` is larger than the previous 16-byte “identifier” node, the additional space isn’t wasted — it holds cached answers to common queries such as “does this type reference a template parameter?” or “what is its canonical form?”. These caches make those operations significantly cheaper, further improving performance. * * * ## Wrapping up There’s more in the patch than what I’ve covered here, including: * `RecordType` now points directly to the declaration found at creation, enriching the AST without measurable overhead. * `RecordType` nodes are now created lazily. * The redesigned `NestedNameSpecifier` simplified several template instantiation transforms. Each of these could warrant its own write-up, but even this high-level overview shows how careful structural changes in the AST can lead to tangible compile-time wins. I hope you found this deep dive into Clang’s internals interesting — and that it gives a glimpse of the kind of small, structural optimizations that add up to real performance improvements in large C++ builds.
cppalliance.org
October 30, 2025 at 3:00 AM
Conan Packages for Boost
Back in April my former colleague Christian Mazakas has announced his work on registry of nightly Boost packages for vcpkg. That same month Conan developers have introduced a new feature that significantly simplified providing of an alternative Conan package source. These two events gave me an idea to create an index of nightly Boost packages for Conan. ## Conan Remotes Conan installs packages from a _remote_ , which is usually a web server. When you request a package in a particular version range, the remote determines if it has a version that satisfies that range, and then sends you the package recipe and, if possible, compatible binaries for the package. Local-recipes-index is a new kind of Conan remote that is not actually a remote server and is just a local directory hierarchy of this kind: recipes ├── pkg1 │ ├── all │ │ ├── conandata.yml │ │ ├── conanfile.py │ │ └── test_package │ │ └── ... │ └── config.yml └── pkg2 ├── all │ ├── conandata.yml │ ├── conanfile.py │ └── test_package │ └── ... └── config.yml The directory structure is based on the Conan Center’s underlying GitHub project. In actuality only the `config.yml` and `conanfile.py` files are necessary. The former tells Conan where to find the package recipes for each version (and hence determines the set of available versions), the latter is the package recipe. In theory there could be many subdirectories for different versions, but in reality most if not all packages simply push all version differences into data files like `conandata.yml` and select the corresponding data in the recipe script. My idea in a nutshell was to set up a scheduled CI job that each day would run a script that takes Boost superproject’s latest commits from `develop` and `master` branches and generates a local-recipes-index directory hierarchy. Then to have recipes directories coming from different branches merged together, and the result be merged with the results of the previous run. Thus, after a while an index of Boost snapshots from each day would accumulate. ## Modular Boost The project would have been fairly simple if my goal was to _just_ provide nightly packages for Boost. Simply take the recipe from the Conan Center project and replace getting sources from a release archive with getting sources from GitHub. But I also wanted to package every Boost library separately. This is generally known as modular Boost packages (not to be confused with Boost C++ modules). There is an apparent demand for such packages, and in fact this is exactly how vcpkg users consume Boost libraries. In addition to the direct results—the Conan packages for Boost libraries—such project is a great test of the _modularity_ of Boost. Whether each library properly spells out all of its dependencies, whether there’s enough associated metadata that describes the library, whether the project’s build files are usable without the superproject, and so on. Conan Center (the default Conan remote) does not currently provide modular Boost packages, only packages for monolithic Boost (although it provides options to disable building of specific libraries). Due to that I decided to generate package recipes not only for nightly builds, but for tagged releases too. Given that, the core element of the project is the script that creates the index from a Boost superproject _Git ref_ (branch name or tag). Each library is a git submodule of the superproject. Every superproject commit contains references to specific commits in submodules’ projects. The script checks out each such commit, determines the library’s dependencies and other properties important for Conan, and outputs `config.yml`, `conanfile.py`, `conandata.yml`, and `test_package` contents. ## Versions As previously mentioned, `config.yml` contains a list of supported versions. After one runs the generator script that file will contain exactly one version. You might ask, what exactly is that version? After some research I ended up with the scheme `MAJOR.MINOR.0-a.B+YY.MM.DD.HH.mm`, where: * `MAJOR.MINOR.0` is the _next_ Boost release version; * `a` implies an alpha-version pre-release; * `B` is `m` for the `master` branch and `d` for the `develop` branch; * `YY.MM.DD.HH.mm` is the authorship date and time of the source commit. For example, a commit authored at 12:15 on 15th of August 2025 taken from the `master` branch before Boost 1.90.0 was released would be represented by the version `1.90.0-a.m+25.08.15.12.15`. The scheme is an example of semantic versioning. The part between the hyphen and the plus specifies a pre-release, and the part following the plus identifies a specific build. All parts of the version contribute to the versions order after sorting. Importantly, pre-releases are ordered _before_ the release they predate, which makes sense, but isn’t obvious from the first glance. I originally did not plan to put commit time into the version scheme, as the scheduled CI job only runs once a day. But while working on the project, I also had the package index updated on pushes into the `master` branch, which overwrote previously indexed versions, and that was never the intention. Also, originally the pre-release part was just the name of the branch, which was good enough to sort `master` and `develop`. But with the scope of the project including actual Boost releases and betas, I needed beta versions to sort after `master` and `develop` versions, but before releases, hence I made them alpha versions explicitly. One may ask, why do I even care about betas? By having specific beta versions I want to encourage more people to check out Boost libraries in beta state and find the bugs early on. I hope that if obtaining a beta version is as easy as simply changing one string in a configuration file, more people will check them and that would reduce the amount of bugs shipped in Boost libraries. ## Conan Generators One of the most important Conan features in my opinion is its support for any build system rather than for a limited selection of them. This is done via _generators_ —utilities that Convert platform description and dependency data into configuration files for build systems. In Conan 2.x the regular approach is to have a set of 2 generators for a given build system. The main one is a dependencies generator, which creates files that tell the build system how to find dependencies. For example, if you are familiar with CMake, the `CMakeDependencies` generator creates config modules for every dependency. The other one is a toolchain generator. Those convert platform information into build system configuration files which determine the compiler, computer architecture, OS, and so on. Using CMake as an example again, the `CMakeToolchain` generator creates a toolchain file. The reason for the split into 2 generators is that there are cases when you use only one of them. For example, if you don’t have any dependencies, you don’t need a dependencies generator. And when you are working on a project, you might already have the necessary build system configuration files, so you don’t need a toolchain generator. For my project I needed both for Boost’s main build system, b2. Boost can also be built with CMake, but that’s still not officially supported, and is tested less rigorously. Unfortunately, Conan 2.x doesn’t currently have in-built support for b2. It had it in Conan 1.x, but with the major version increase they’ve removed most of the old generators, and the PR to add it back did not go anywhere. So, I had to implement those 2 generators for b2. Luckily, Conan supports putting such Conan extensions into packages. So, now the package index generation script also creates a package with b2 generators. ## The Current State and Lessons Learned The work is still in its early stage, but the project is in a somewhat usable state already. It is currently located here (I plan to place it under boostorg GitHub organisation with the Boost community’s approval, or, failing that, under cppalliance organisation). You can clone the project and install and use some of the Boost libraries, but not all. I have tested that those libraries build and work on Windows, Linux, and macOS. The b2 generators are almost feature complete at this point. My future work will be mostly dedicated to discovering special requirements of the remaining libraries and working out ways to handle them. The most interesting problems are handling projects with special “options” (e.g. Boost.Context usually has to be told what the target platform ABI and binary format are), and handling the few external dependencies (e.g. zlib and ICU). Another interesting task is handling library projects with several binaries (e.g. Boost.Log) and dealing with the fact that libraries can change from being compiled to being header-only (yes, this does happen). There were also several interesting findings. At first I tried determining dependencies from the build scripts. But that turned out to be too brittle, so in the end I decided to use `depinst`, the tool Boost projects use in CI to install dependencies. This is still a bit too simplistic, as libraries can have optional and platform dependencies. But I will have to address this later. Switching to `depinst` uncovered that in Boost 1.89.0 a circular dependency appeared between Boost.Geometry and Boost.Graph. This is actually a big problem for package managers, as they have to build all dependencies for a project before building it, and before that do the same thing for each of the dependencies, and this creates a paradoxical situation where you need to build the project before you build that same project. To make such circular dependencies more apparent in the future, I’ve added a flag to `depinst` that makes it exit with an error if a cycle is discovered. Overall, I think Boost modularisation is going fairly well. Every library I’ve tried yet builds correctly without the superproject present. I hope to finish the project soon, preferably before the 1.90.0 release. After that there’s still an interesting possible addition. Christian’s vcpkg registry mentioned in the very beginning also had a package for a candidate library, so that people could easily install it and try it out during the review period. My package index could in the future also do that. Hopefully that will motivate more people to participate in Boost reviews.
cppalliance.org
October 21, 2025 at 2:56 AM
Writing Docs with Visuals and Verve
In a past life I worked in the computer journalism business, and learnt over time what attracts people to read a page. Lot’s of things are important, the font used, the spacing between letters and lines and paragraphs, even the width of a column of text is super-important for readability (so the eye does not lose track of the line it is on). Other stuff is important to, readers, especially technical readers, love tables. A table of all the networking libraries available in Boost for example, becomes a reassuring point of reference, as opposed to a bunch of text listing the libraries. Two of the most important factors in drawing readers in are headlines and images. It can take some grey matter and experience and skill to come up with a catchy headline (“Phew - what a scorcher” - is a famous example of a tabloid headline after a super hot day!). The more I worked in journalism the more I appreciated all the skills involved - and those I had and those I had not! When it comes to images, I decided to add at least one image to all the Scenarios in the Boost User Guide (Finance, Networking, Simulation, among others). One of the skills I do not have is that of an artist - so these images mostly had to come from elsewhere. Not all though, for the deformation example in the Simulation scenario, I came up with the following image - which works I guess! For other images I used AI to come up with some text based diagrams, which do work well as “images” for a technical readership. For example, the following simple flow for a Message Queue shows what is going on, Receiver 3 picking up all the inappropriately addressed messages, as well as its own. Other images required some research. I must admit I did not know the difference between a _petal_ and a _sepal_ until I did some research on the iris data used in the Machine Learning scenario. The following image is a composite of a picture taken by my wife of an iris in a New Mexico volcanic caldera, and a diagram generated by AI. Now I know, the sepals are the dangly bits I would have called petals before engaging in this research. Hopefully these images will draw in readers and entice them to try out the scenarios, and then continue their programming journey with the support of Boost libraries. Another topic I dug into this quarter is to find examples of good and bad practices, and then to tabularize them (remember, tables are trusted references…). I started with error messages. Here is an example of a tedious error message: `error C2679: binary '=': no operator found which takes a right-hand operand of type 'boost::gregorian::date' (or there is no acceptable conversion)` Why is it tedious? Because it is verbose and yet still doesn’t say what the user did wrong, not even the library name is in the message. A shorter, sharper and more helpful message would be: `boost::date_time::invalid_date: "2025-02-30" is not a valid Gregorian date` This message contains the library name (date_time), and the invalid input. Error message content should be a high-priority issue for API developers. Another topic added to Best Practices is simply API design itself. The Boost filesystem library contains some good examples of clear design, for example - here is a section from the Contributor Guide: Of course there is an element of style, taste, personal preferences in all these issues. It is totally OK for APIs to reflect those traits of the developers, this guide is there as a check point - a resource to read over and reflect on when evaluating your own work. Talking of AI - and who isn’t? - I requested the Cpp Alliance give me an image-creating AI API account, so I could add a section to the AI Client scenario on creating images. It is a lot of fun asking an AI to create an image, though you would be correct in thinking you probably could have come up with better yourself! For example, check out this exchange: `Enter your request (ASCII diagram or text) or 'exit': Can you draw an ASCII diagram of a speedboat?` `Assistant Response: Sure! Here's a simple ASCII representation of a speedboat:` __/__ _____/_____|_____ \ / ~~~~~~~~~~~~~~~~~~~~~ On a tad more serious note, I added topics on Reflection and Diagnostics to the User Guide. And I scan the Slack conversations for words or phrases that are new to me, and add them to the User Guide Glossary - which is a fun document on its own account. Even a document as potentially dull as a glossary can be fun to create and fun to read. Everybody likes to be entertained when they are reading, it takes the grind out of the experience.
cppalliance.org
October 21, 2025 at 2:56 AM
DynamicBitset Reimagined: A Quarter of Flexibility, Cleanup, and Modern C++
Over the past three months, I’ve been immersed in a deep and wide-ranging overhaul of the Boost.DynamicBitset library. What started as a few targeted improvements quickly evolved into a full-scale modernization effort—touching everything from the underlying container to iterator concepts, from test coverage to documentation style. More than 170 commits later, the library is leaner, more flexible, and better aligned with modern C++ practices. ## Making the core more flexible The most transformative change this quarter was allowing users to choose the underlying container type for `dynamic_bitset`. Until now, the implementation assumed `std::vector`, which limited optimization opportunities and imposed certain behaviors. By lifting that restriction, developers can now use alternatives like `boost::container::small_vector`, enabling small buffer optimization and more control over memory layout. This change had ripple effects throughout the codebase. I had to revisit assumptions about contiguous storage, update operators like `<<=`, `>>=`, and ensure that reference stability and iterator behavior were correctly handled. ## Introducing C++20 iterators One of the more exciting additions this quarter was support for C++20-style iterators. These new iterators conform to the standard iterator concepts, making `dynamic_bitset` more interoperable with modern algorithms and range-based utilities. I added assertions to ensure that both the underlying container and `dynamic_bitset` itself meet the requirements for bidirectional iteration. These checks are enabled only when compiling with C++20 or later, and they help catch subtle mismatches early—especially when users plug in custom containers. ## Saying goodbye to legacy workarounds With modern compilers and standard libraries, many old workarounds are no longer needed. I removed the `max_size_workaround()` after confirming that major implementations now correctly account for allocators in `max_size()`. I also dropped support for obsolete compilers like MSVC 6 and CodeWarrior 8.3, and for pre-standard iostreams, cleaned up outdated macros, and removed compatibility layers for pre-C++11 environments. These removals weren’t just cosmetic—they simplified the code and made it easier to reason about. In many places, I replaced legacy constructs with standard features like `noexcept` and `std::move()`. ## constexpr support When it is compiled as C++20 or later, almost all functions in DynamicBitset are now `constexpr`. ## Dropping obsolete dependencies As part of the cleanup effort, I also removed several outdated dependencies that were no longer justified. These included Boost.Integer (previously used by `lowest_bit()`), `core/allocator_access.hpp`, and various compatibility headers tied to pre-C++11 environments. This not only reduces compile-time overhead and cognitive load, but also makes the library easier to audit and maintain. ## Strengthening the test suite A part of this quarter’s work was expanding and refining the test coverage. I added new tests for `flip()`, `resize()`, `swap()`, and `operator!=()`. I also ensured that input iterators are properly supported in `append()`, and verified that `std::hash` behaves correctly even when two bitsets share the same underlying container but differ in size. Along the way, I cleaned up misleading comments, shortened overly complex conditions, and removed legacy test code that no longer reflected the current behavior of the library. The result is a test suite that’s more robust, more meaningful, and easier to maintain. ## Documentation that speaks clearly I’ve always believed that documentation should be treated as part of the design, not an afterthought. This quarter, I ported the existing documentation to MrDocs and Antora, while fixing and improving a few bits in the process. This uncovered a few MrDocs bugs, some of which remain—but I’m hopeful. I also spent time harmonizing the style and structure of the library’s comments and docstrings. I chose to document iterator categories rather than exposing concrete types, which keeps the interface clean and focused on behavior rather than implementation details. ## New member functions and smarter implementations This quarter also introduced several new member functions that expand the expressiveness and utility of `dynamic_bitset`: * `push_front()` and `pop_front()` allow bit-level manipulation at the front of the bitset, complementing the existing back-oriented operations. * `find_first_off()` and `find_next_off()` provide symmetric functionality to their `find_first()` counterparts, making it easier to locate unset bits. * A constructor from `basic_string_view` was added for C++17 and later, improving interoperability with modern string APIs. Alongside these additions, I revisited the implementation of several existing members to improve performance and clarity: * `push_back()` and `pop_back()` were streamlined for better efficiency. * `all()` and `lowest_bit()` were simplified and optimized, with the latter also shedding its dependency on Boost.Integer. * `append()` was fixed to properly support input iterators and avoid redundant checks. ## Minor but impactful cleanups A large number of small edits improved correctness, readability, and maintainability: * Fixed the stream inserter to set `badbit` if an exception is thrown during output. * Changed the stream extractor to rethrow any exceptions coming from the underlying container. * Reordered and cleaned up all #include sections to use the “” form for Boost includes where appropriate and to keep include groups sorted. * Removed an example timing benchmark that was misleading and a number of unneeded comments and minor typos across code and docs. These edits reduce noise and make code reviews and maintenance more pleasant. ## Reflections Looking back, this quarter reminded me of the value of revisiting assumptions. Many of the workarounds and constraints that once made sense are now obsolete. By embracing modern C++ features and simplifying where possible, we can make libraries like `dynamic_bitset` more powerful and more approachable. It also reinforced the importance of clarity—both in code and in documentation. Whether it’s a test case, a comment, or a public API, precision and consistency go a long way. The work continues, but the foundation is stronger than ever. If you’re using `dynamic_bitset` or thinking about integrating it into your project, I’d love to hear your feedback.
cppalliance.org
October 21, 2025 at 2:56 AM
Working on Boost.Bloom roadmap
During Q3 2025, I’ve been working in the following areas: ### Boost.Bloom Boost.Bloom has been officially released in Boost 1.89. I’ve continued working on a number of roadmap features: * Originally, some subfilters (`block`, `fast_multiblock32` and `fast_multiblock64)` implemented lookup in a branchful or early-exit way: as soon as a bit checks to zero, lookup terminates (with result `false`). After extensive benchmarks, I’ve changed these subfilters to branchless execution for somewhat better performance (PR#42). Note that `boost::bloom::filter<T, K, ...>` is still branchful for `K` (the number of subfilter operations per element): in this case, branchless execution involves too much extra work and does not compensate for the removed branch speculation. Ivan Matek helped with this investigation. * Added bulk-mode operations following a similar approach to what we did with Boost.Unordered concurrent containers (PR#42). * I’ve been also working on a proof of concept for a dynamic filter where the _k_ and/or _k’_ values can be specified at run time. As expected, the dynamic filter is slower than its static counterpart, but benchmarks show that execution times can increase by up to 2x for lookup and even more for insertion, which makes me undecided as to whether to launch this feature. An alternative approach is to have a `dynamic_filter<T>` be a wrapper over a virtual interface whose implementation is selected at run time from a static table of implementations based on static `filter<T, K>` with `K` between 1 and some maximum value (this type erasure technique is described, among other places, in slides 157-205 of Sean Parent’s C++ Seasoning talk): performance is much better, but this approach also has drawbacks of its own. * Reviewed a contribution fom Braden Ganetsky to make the project’s `CMakeLists.txt` more Visual Studio-friendly (PR#33). ### Boost.Unordered * Reviewed PR#316. ### Boost.MultiIndex * Reviewed PR#83, PR#84. ### Boost.Flyweight * Fixed an internal compile error that manifested with newer compilers implementing P0522R0 (PR#23). * Reviewed PR#22. ### Boost.PolyCollection * Reviewed PR#32. ### Boost website * Filed issues #1845, #1846, #1851, #1858, #1900, #1927, #1936, #1937. * Helped with the transition of the global release notes procedure to one based on the new website repo exclusively (PR#508, PR#510). This procedure is expected to launch in time for the upcoming Boost 1.90 release. ### Boost promotion * Prepared and posted around 10 messages on Boost’s X account and Reddit. The activity on social media has grown considerably thanks to the dedication of Rob Beeston and others. ### Support to the community * Helped Jean-Louis Leroy get Drone support for the upcoming Boost.OpenMethod library (PR#39). * Supporting the community as a member of the Fiscal Sponsorhip Committee (FSC).
cppalliance.org
October 21, 2025 at 2:56 AM
Levelling up Boost.Redis
I’ve really come to appreciate Boost.Redis design. With only three asynchronous primitives it exposes all the power of Redis, with features like automatic pipelining that make it pretty unique. Boost.Redis 1.90 will ship with some new exciting features that I’ll cover in this post. ## Cancelling requests with asio::cancel_after Boost.Redis implements a number of reliability measures, including reconnection. Suppose that you attempt to execute a request using `async_exec`, but the Redis server can’t be contacted (for example, because of a temporary network error). Boost.Redis will try to re-establish the connection to the failed server, and `async_exec` will suspend until the server is healthy again. This is a great feature if the outage is transitory. But what would happen if the Redis server is permanently down - for example, because of deployment issue that must be manually solved? The user will see that `async_exec` never completes. If new requests continue to be issued, the program will end up consuming an unbound amount of resources. Starting with Boost 1.90, you can use `asio::cancel_after` to set a timeout to your requests, preventing this from happening: // Compose your request redis::request req; req.push("SET", "my_key", 42); // If the request doesn't complete within 30s, consider it as failed co_await conn.async_exec(req, redis::ignore, asio::cancel_after(30s)); For this to work, `async_exec` must properly support per-operation cancellation. This is tricky because Boost.Redis allows executing several requests concurrently, which are merged into a single pipeline before being sent. For the above to useful, cancelling one request shouldn’t affect other requests. In Asio parlance, `async_exec` should support partial cancellation, at least. Cancelling a request that hasn’t been sent yet is trivial - you just remove it from the queue and call it a day. Cancelling requests that are in progress is more involved. We’ve solved this by using “tombstones”. If a response encounters a tombstone, it will get ignored. This way, cancelling `async_exec` has always an immediate effect, but the connection is kept in a well-defined state. ## Custom setup requests Redis talks the RESP3 protocol. But it’s not the only database system that speaks it. We’ve recently learnt that other systems, like Tarantool DB, are also capable of speaking RESP3. This means that Boost.Redis can be used to interact with these systems. At least in theory. In Boost 1.89, the library uses the `HELLO` command to upgrade to RESP3 (Redis’ default is using the less powerful RESP2). The command is issued as part of the reconnection loop, without user intervention. It happens that systems like Tarantool DB don’t support `HELLO` because they don’t speak RESP2 at all, so there is nothing to upgrade. This is part of a larger problem: users might want to run arbitrary commands when the connection is established, to perform setup tasks. This might include `AUTH` to provide credentials or `SELECT` to choose a database index. Until now, all you could do is configure the parameters used by the `HELLO` command. Starting with Boost 1.90, you can run arbitrary commands at connection startup: // At startup, don't send any HELLO, but set up authentication and select a database redis::request setup_request; setup_request.push("AUTH", "my_user", "my_password"); setup_request.push("SELECT", 2); redis::config cfg { .use_setup = true, // use the custom setup request, rather than the default HELLO command .setup = std::move(setup_request), // will be run every time a connection is established }; conn.async_run(cfg, asio::detached); This opens the door simplifying code using PubSub. At the moment, such code needs to issue a `SUBSCRIBE` command every time a reconnection happens, which implies some tricks around `async_receive`. With this feature, you can just add a `SUBSCRIBE` command to your setup request and forget. This will be further explored in the next months, since `async_receive` is currently aware of reconnections, so it might need some extra changes to see real benefits. ## Valkey support Valkey is a fork from Redis v7.3. At the time of writing, both databases are mostly interoperable in terms of protocol features, but they are being developed separately (as happened with MySQL and MariaDB). In Boost.Redis we’ve committed to supporting both long-term (at the moment, by deploying CI builds to test both). ## Race-free cancellation It is very easy to introduce race conditions in cancellation with Asio. Consider the following code, which is typical in libraries that predate per-operation cancellation: struct connection { asio::ip::tcp::socket sock; std::string buffer; struct echo_op { connection* obj; asio::coroutine coro{}; template <class Self> void operator()(Self& self, error_code ec = {}, std::size_t = {}) { BOOST_ASIO_CORO_REENTER(coro) { while (true) { // Read from the socket BOOST_ASIO_CORO_YIELD asio::async_read_until(obj->sock, asio::dynamic_buffer(obj->buffer), "\n", std::move(self)); // Check for errors if (ec) self.complete(ec); // Write back BOOST_ASIO_CORO_YIELD asio::async_write(obj->sock, asio::buffer(obj->buffer), std::move(self)); // Done self.complete(ec); } } } }; template <class CompletionToken> auto async_echo(CompletionToken&& token) { return asio::async_compose<CompletionToken, void(error_code)>(echo_op{this}, token, sock); } void cancel() { sock.cancel(); } }; There is a race condition here. `cancel()` may actually not cancel a running `async_echo`. After a read or write completes, the respective handler may not be called immediately, but queued for execution. If `cancel()` is called within that time frame, the cancellation will be ignored. The proper way to handle this is using per-operation cancellation, rather than a `cancel()` method. `async_compose` knows about this problem and keeps state about received cancellations, so you can write: // Read from the socket BOOST_ASIO_CORO_YIELD asio::async_read_until(obj->sock, asio::dynamic_buffer(obj->buffer), "\n", std::move(self)); // Check for errors if (ec) self.complete(ec); // Check for cancellations if (!!(self.get_cancellation_state().cancelled() & asio::cancellation_type_t::terminal)) self.complete(asio::error::operation_aborted); In 1.90, the library uses this approach everywhere, so cancellation is reliable. Keeping the `cancel()` method is a challenge, as it involves re-wiring cancellation slots, so I won’t show it here - but we’ve managed to do it. ## Next steps I’ve got plans to keep working on Boost.Redis for a time. You can expect more features in 1.91, like Sentinel support and more reliable health checks.
cppalliance.org
October 17, 2025 at 2:56 AM
Decimal Goes Back to Review
We are excited to announce that the Decimal (https://github.com/cppalliance/decimal) library is going back to review for inclusion in Boost from 06 to 15 October. In preparation for this we have made quite a few changes since the indeterminate end of the first review about 9 months ago: Breaking Changes: * Based on bitwise comparisons with other similar libraries and database software, we have changed the internal encoding of our IEEE 754-compliant types * We spent about 3 months optimizing just back end integer types that are now used throughout the library, and as the internals of decimal128_t * We have changed the type names to better match conventions: * `decimalXX` is now `decimalXX_t` * `decimalXX_fast` is now `decimal_fastXX_t` * The headers have been similarly renamed (e.g. decimal32.hpp -> decimal32_t.hpp), and can now be used independently instead of requiring the monolith based on feedback in Review * Constructors have been simplified to reduce confusion (no more double negative logic) * The default rounding mode has changed to align with IEEE 754, with rounding bugs being squashed across the modes as well Other Changes: * The documenation content has been overhauled thanks to feedback from Peter Turcan and others during the first review * The docs are no longer a single long page of Asciidoc; we have moved to Antora. Thanks to Joaquín and Christian for making it trivial to copy from Unordered to make that happen. * https://develop.decimal.cpp.al/ * We now support formatting with {fmt} * Benchmarks have been expanded to include GCC `_DecimalXX` types, and Intel’s libbid. I think people should be pleased with the results now, since that was a huge point of contention at the end of the review * We have added support for CMake pkg config for ease of use * Every post-review issue John was kind enough to consolidate and open have been addressed: https://github.com/cppalliance/decimal/issues?q=is%3Aissue%20state%3Aclosed%20label%3A%22Boost%20Review%22 Continued Developments: * I think the only unaddressed comment from the first review is support for hardware decimal floating point types. There are a few rarer architectures that have native decimal floating point units like POWER10. Is it possible to fully integrate these native types for use in the library? Armed with a compiler farm account I have begun developing a wrapper around the native types that seems to work. Stay tuned. * One item that we have considered, but have not put any effort into yet would be getting the library running on CUDA platforms. If this is a feature that you are interested in, please let us know! * As is always the case with Boost reviews, regardless of the outcome I am sure that we will receive lots of feedback on how to improve the library. If you are interested, much of the contents of the first review can be found in the original thread on the boost mailing list archive: https://lists.boost.org/archives/list/[email protected]/thread/AGFOQZMJ4HKKQ5C5XDDKNJ3VJL72YTWL/
cppalliance.org
October 17, 2025 at 2:56 AM
Systems, CI Updates Q3 2025
### Doc Previews and Doc Builds The isomorphic-git improvements are an ongoing saga. (As a reminder, isomorphic-git is a dependency and component of Antora, which can’t parse submodules while boost relies heavily on submodules). This quarter I coded full submodule support into isomorphic-git and then submitted a PR with 150 files modified. An issue is that the library is suffering from a general lack of maintainers, it’s stuck on Nodejs 14 from 5 years ago, and uses many out-of-date packages. These are preventing proper CI tests (specifically, recursive copy isn’t supported) and now the maintainer refuses to merge the pull request without a major overhaul of BrowserFS -> ZenFS. It’s a complete detour since this has nothing directly to do with submodules. An amusing anecdote from the story: out of the blue a developer from Germany appeared and he was very energetic about fixing everything in isomorphic-git. For a week or so, it seemed he would solve all problems. He would overhaul the entire package (which has been neglected), contribute many improvements, solve all bugs, upgrade ZenFS, etc. I was genuinely optimistic about his participation. However my latest assessment of the situation is he’s experiencing some delusions of grandeur based on comments in git repositories such as… that upgrading isomorphic-git “will trigger a massive, multi-trillion-dollar restructuring of the digital economy”. After expressing impatience with the existing maintainers of isomorphic-git, the developer forked the repo, and has ceased to be involved directly. However all is not lost. The author of ZenFS is actively working with isomorphic-git to implement the BrowserFS -> ZenFS upgrade. He is contributing the pull request. ### Boost website boostorg/website-v2 * Adjust db caching to support master/develop branches. * Improve script to deploy websites via ‘git push’. * Remove files from wowbagger, disk space filling up. * Redirect live.boost.org to boost.org. Switch regression.boost.io -> regression.boost.org. Fix bugs in the regression CI jobs. ### Mailman3 * Modified scripts to allow developers to locally download, parse, graph MM3 traffic. That is, conveniently download the database backups of the mailing lists. * Plausible Analytics for lists.boost.org. * Gunicorn number-of-workers adjustments. * Discuss HTML-formatting features with MM3 upstream maintainers. Resistance to the idea, and also from Andrey and others at Boost. It is not a basic feature to implement - affects multiple areas of the app. May not proceed in the short-term. * Troubleshooting, and discovered the cause of a page load delay. Disabled ‘member visibility’ for now, which causes a massive SQL query every time anyone visits the profile page. ### boost-ci Completed from last quarter: Nearly all CI jobs that use boost-ci set B2_CI_VERSION=1. Eliminate the need for this variable by making it a default, which will avoid errors when the variable isn’t set properly. CI configuration becomes one step easier. B2_CI_VERSION=1 may now be omitted. ### Jenkins Asio docs builds for K-ballo. ### Boost release process boostorg/release-tools * Release-tools bug fixes in connection with boostorg/redis antora builds. * Commitbot accepts case-insensitive variables. (Needs to be merged.) * publish_release.py: extend preflight checks to staging. * New superproject container image - upgrade Doxygen. ### Drone * Built a container image “cppalliance/2404-p2996:1” to support P2996 (“Reflection for C++26”), based on the repository https://github.com/bloomberg/clang-p2996 that contains experimental support for P2996 reflection. * Upgraded Xcode versions on existing macOS machines. ### JSON Benchmarks After a server outage, recovered access through the console on dedicatednode2.cpp.al. Adjusted network configuration. ### GHA Significant time working on an upgrade to the latest version of the Terraform code. Encountered multiple bugs. The CI user on Windows is no longer configurable, but it should be set (to ‘Administrator’) during boost CI tests. Composed and sent a pull request. Ongoing.
cppalliance.org
October 17, 2025 at 2:56 AM
Boost.RunTimeServices: The Glue for Optional Runtime Features
## How Boost.RunTimeServices Emerged from Boost.HTTP.Proto Development During the development of the **Boost.HTTP.Proto** library, we recognized the need for a flexible mechanism to install and access optional services at runtime without requiring prior knowledge of their specific implementations. For example, building a library with optional support for zlib and Brotli compression, even if those libraries weren’t installed on the user’s machine. This challenge led to the creation of **Boost.RunTimeServices**, a solution that offers several key benefits to both library developers and users, which I will briefly outline below. #### Libraries With No Configuration Macros One approach to managing optional dependencies in libraries is to use configuration macros at build time, such as `BOOST_HTTP_PROTO_HAS_ZLIB` or `BOOST_COOKIES_HAS_PSL`. However, this approach has major drawbacks: 1. Combinatorial explosion of binary variants. 2. Users can’t easily determine which features are enabled in a binary. 3. Configuration macros leak into downstream libraries, compounding complexity. 4. Changing features requires full rebuilds of all dependent code. 5. Difficult to distribute a single binary via package managers. With **Boost.RunTimeServices** , configuration macros become unnecessary. Features can be queried and installed at runtime. For example, installing an optional zlib inflate service: rts::context rts_ctx; rts::zlib::install_inflate_service(rts_ctx); Then, a library can conditionally use the service: if(cfg.decompression) { auto& svc = ctx.get_service<rts::zlib::inflate_service>(); svc.inflate(stream, rts::zlib::flush::finish); } #### Smaller Binaries by Stripping Unused Features Since service interfaces are decoupled from implementations, unused services and their dependencies can be eliminated by the linker. For example the following is part of the implementation of `rts::zlib::inflate_service`: class inflate_service_impl : public inflate_service { public: using key_type = inflate_service; int init2(stream& st, int windowBits) const override { stream_cast sc(st); return inflateInit2(sc.get(), windowBits); } int inflate(stream& st, int flush) const override { stream_cast sc(st); return ::inflate(sc.get(), flush); } // ... } The implementation class is only instantiated within: inflate_service& install_inflate_service(context& ctx) { return ctx.make_service<inflate_service_impl>(); } Libraries interact only with the abstract interface: struct BOOST_SYMBOL_VISIBLE inflate_service : public service { virtual int init2(stream& st, int windowBits) const = 0; virtual int inflate(stream& st, int flush) const = 0; // ... }; If the user never calls `install_inflate_service`, the implementation and its dependencies are omitted from the binary. In this particular example, having separate services for inflation and deflation gives us more granularity on the matter. For instance, a client application that uses **Boost.HTTP.Proto** will more likely only need to install `rts::zlib::inflate_service`, because it typically only needs to parse compressed HTTP response messages and compression of HTTP requests almost never happens in client applications. The reverse is true for server applications and they might only need to install `rts::zlib::deflate_service`, since client requests usually arrive uncompressed and the server needs to compress responses (if requested). #### Libraries Built Independent of the Availability of Optional Services Because a library that uses an optional service needs only the interface of that service, there is no need for a build-time dependency. Therefore, we can always build a single version of a library that takes advantage of all optional services if they are available at runtime. For example, in the case of **Boost.HTTP.Proto** , one can use the library without any compression services, as users simply don’t install those services and there’s no need to link any extra libraries. Another user can use the exact same binary of **Boost.HTTP.Proto** with zlib and Brotli decompression algorithms: rts::context rts_ctx; rts::zlib::install_inflate_service(rts_ctx); // links against boost_rts_zlib rts::brotli::install_decoder_service(rts_ctx); // links against boost_rts_brotli #### Optional Services in Downstream Libraries Assume we want to create a library named **Boost.Request** that uses **Boost.HTTP.Proto** and **Boost.HTTP.IO** , and provides an easy-to-use interface for client-side usage. Such a library doesn’t need to care about optional services and can delegate that responsibility to the end user, allowing them to decide which services to install. For example, **Boost.Request** can internally query the availability of these services and make requests accordingly: if(rts_ctx.has_service<brotli::decoder_service>()) encodings.append("br"); if(rts_ctx.has_service<zlib::inflate_service>()) { encodings.append("deflate"); encodings.append("gzip"); } if(!accept_encoding.empty()) request.set(field::accept_encoding, encodings.str()); ## Why This Needs to Be a Separate Library This is a core library that many other libraries may want to use. For example, a user who installs zlib services expects them to be usable in both **Boost.HTTP.Proto** and **Boost.WS.Proto** : rts::context rts_ctx; rts::zlib::install_inflate_service(rts_ctx); rts::zlib::install_deflate_service(rts_ctx); // Usage site http_proto::parser parser(rts_ctx); ws_proto::stream stream(rts_ctx); User libraries need to link against `boost_rts` in order to access `rts::context`. Note that `boost_rts` is a lightweight target with no dependency on optional services like zlib or Brotli. ## Existing Challenges #### Minimum Library For Mandatory Symbols A library that uses an optional service might still need to link against a minimal version that provides necessary symbols such as `error_category` instances, because we usually need to instantiate them inside the source and can’t leave them in headers. For example, assume a library that needs to call an API to provide the error message: char const* error_cat_type:: message( int ev, char*, std::size_t) const noexcept { return c_api_get_error_message(ev); } This clearly can’t be left in the headers because it would require the existence of the `c_api_get_error_message` symbol at link time, which defeats the purpose of optional services. To allow optional linkage, a fallback could be provided: char const* error_cat_type:: message( int ev, char*, std::size_t) const noexcept { return "service not available"; } But the remaining question is: where should this implementation go if we want optional linkage against services? Currently, we place this code inside the core **Boost.RunTimeServices** library, which could become a scalability problem in the future as the number of services grows. #### An Even Finer Grain Control Over Used and Unused Symbols Even though separate services (e.g., `inflate_service`, `deflate_service`) help the linker remove unused code; the granularity is still limited. For example, if a library uses only `inflate_service::init`, the linker still includes `inflate_service::init2` and other unused methods. This is because interfaces are polymorphic and the linker can’t remove individual virtual methods: class inflate_service_impl : public inflate_service { public: using key_type = inflate_service; int init(stream& st) const override { stream_cast sc(st); return inflateInit(sc.get()); } int init2(stream& st, int windowBits) const override { stream_cast sc(st); return inflateInit2(sc.get(), windowBits); } // ... } #### Space Overhead and Indirection Cost of Virtual Services This is probably not an issue for most users, as these costs are negligible in real-world applications. However, a solution that provides the same functionality as virtual service interfaces but without these overheads would be highly desirable.
cppalliance.org
July 18, 2025 at 2:37 AM
Bigger, Faster, Stronger Types
We continue to make exciting progress developing new libraries for inclusion in Boost, and expanding those already available. # New Libraries ## int128 Int128 (https://github.com/cppalliance/int128) is a small library that has more or less fallen out of work on Decimal. It provides two type: an unsigned 128-bit integer and a signed 128-bit integer. Since my last post the library should now be ready for beta, and subsequently production use. Much effort was put into optimizing every operation on a multitude of architectures. The documentation includes bar charts showing the performance of our types vs Boost.Multiprecision, and built-in types (if available). While orgianlly envisioned as just an improvement to the Decimal backend arithmetic, I think this has much more additional usefulness. ## Decimal Decimal (https://github.com/cppalliance/decimal) is a ground-up implementation of IEEE 754 Decimal Floating Point types in C++14, co-authored with Chris Kormanyos. In January we had our formal review for inclusion in Boost. As int128 above became more production ready we have integrated it into Decimal as a new backend integer type. Not only do we now use the int128 as a backend, we were able to find a few bugs in integration due to the special functions test suite in Decimal. The relationship during co-development has worked out really well. We also recently merged a new 256-bit integer backend based on developments and lessons learned from `int128`. These combined have given us >100% speedups for the 128-bit types, and also benefit the 64-bit types to a lesser degree. Discussions are ongoing in the Cpplang Slack channel `#boost-decimal`. # Existing Libraries ## Math As posted a few versions ago Boost.Math began offering support to be run on GPU with NVCC, NVRTC, and SYCL. We have recieved a few bug reports now that this functionallity is being used more. For Boost 1.89 we have put good effort into fixes and internal restructuring to address the issues that people have been having. ## Multiprecision For the first time in a while Boost.Multiprecision has a new backend type: `cpp_double_fp_backend`. This project was started during GSoC 2021 and has finally come to full fruition. The `cpp_double_fp_backend` back-end is the sum of two IEEE floating-point numbers combined to create a type having roughly twice the composite width of one of its parts. The `cpp_double_fp_backend` back-end is used in conjunction with `number` and acts as an entirely C++ header only floating-point number type. If you need more precision than a `double` with less computational expense than arbitrary precision types look out for this in Boost 1.89.
cppalliance.org
July 15, 2025 at 2:40 AM
Bringing B2-Style Test Granularity to CMake
# Introduction Boost libraries typically maintain granular unit tests using Boost.Build (B2). B2 provides a `run` rule that makes it easy to define many independent test targets from a single source file or executable. Each test case can be listed, invoked, and reported separately, which improves developer workflow, test clarity, and CI diagnostics. However, Boost’s CMake integration has lacked this granularity. When Boost libraries are built with CMake, the typical approach is to define a single test executable and add all test suites as a single test in CTest with `add_test()`. As a result, when running tests with CTest, developers lose the ability to see individual test failures in isolation, run only subsets of tests, or leverage parallel execution at the test level. The goal of this work is to bridge that gap. We want to replicate the B2 “one executable, many independent tests” idiom in CMake. Specifically, we want to use modern CMake techniques to split a single unit test executable into multiple independent CTest targets, while preserving the flexibility and simplicity of the Boost testing style. # Problem Analysis To understand why splitting tests into independent CTest targets is non-trivial, it helps to look at **how CMake’s build and test model is structured**. When building and testing libraries with CMake, the developer usually has a workflow of four key phases: * **Configuration step** : This is where CMakeLists.txt files are processed and commands like `add_executable` and `add_test()` are called. Test targets must be defined here, so CTest knows about them. * **Build step** : This is when the underlying build system (the CMake “generator”: e.g., Ninja or Make) compiles sources and produces executables, including unit test binaries. * **Test step** : This is when `ctest` runs the defined tests, using the executables built in the previous step. * **Installation step** : This is where the built libraries and executables are installed to their final locations. This step is not directly relevant to the problem at hand, but it’s part of the overall CMake workflow. At the configuration step, we would like to have something like add_test(NAME test_a COMMAND my_unit_test_executable a) add_test(NAME test_b COMMAND my_unit_test_executable b) add_test(NAME test_c COMMAND my_unit_test_executable c) instead of add_test(NAME my_unit_test_executable COMMAND my_unit_test_executable) The fundamental obstacle is that, in the general case, **you cannot know what tests exist inside a unit test executable until you can _run_ it**. Many modern test frameworks (like Boost.Test, Catch2, GoogleTest) support listing their available tests by running the executable with a special argument (e.g., `--list-tests`). But this only works _after_ the executable is built. In other words: > You need the executable to discover the tests, but CMake requires you to declare the tests in the configuration phase before building the executable in the build step. This dependency cycle is the core problem that makes it difficult to reproduce B2’s `run` rule semantics in CMake. Without special handling, you’re forced to treat the entire unit test binary as a single test, losing the ability to register its internal test cases as independent CTest targets. The solution to this problem involves the `TEST_INCLUDE_FILES` directory property, which allows you to specify additional files that CTest should consider when running tests. By leveraging this property, we can dynamically generate a CMake script that defines individual `add_test()` calls for each test case found in the unit test executable. So we can use set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${TEST_SUITE_CTEST_INCLUDE_FILE}" ) to include a generated CMake script that contains the individual test definitions. This allows us to run the executable post-build, extract the test names, and then register them with CTest in a way that mimics the B2 experience. Other modern test frameworks have explored this feature to provide an automated test discovery mechanism for their libraries in CMake. For example, Catch2’s `catch_discover_tests()` and GoogleTest’s `gtest_discover_tests()` run the built test executable with a listing flag (like `--list-tests`) to extract individual test cases and generate separate `add_test()` entries for each. # Design Overview Since CMake requires test registration in the configuration step, but we can only discover test cases _after_ building the executable, we introduce an approach to bridge that gap. The high-level plan is: * **Build the executable** : Compile the unit test executable as usual. The target is defined in the configuration step and built in the build step. * **Post-build step** : After building, run the executable with `--list-tests` (or equivalent) to enumerate all available test cases. This is achieved with a custom command that runs after the build completes. * **Generate a CMake script** : This post-build step writes a `.cmake` file containing one `add_test()` call for each discovered test case. * **Conditional inclusion** : The main CMake configuration includes this generated script _only if it exists_ , so the tests appear in CTest after they’re generated. The new script is included using the `TEST_INCLUDE_FILES` property, which allows CTest to pick it up automatically. This approach effectively moves test discovery to the build phase while still registering the resulting tests with CTest in the configuration phase for subsequent runs. This process is transparent to the user. In Boost.URL, where we implemented the functionality, the test registration process went from: add_test(NAME boost_url_unit_tests COMMAND boost_url_unit_tests) to boost_url_test_suite_discover_tests(boost_url_unit_tests) # Implementation Details This section describes the approach bottom-up, showing the overall mechanism of discovering and registering independent test targets in CMake. ## The Test Listing Extractor Script The first piece is a small CMake script that **runs the compiled test executable** with `--list-tests` (or an equivalent flag your test framework supports). It captures the output, which is expected to be a plain list of test case names. For example, suppose your unit test executable outputs: UnitA.TestAlpha UnitA.TestBeta UnitB.TestGamma The script saves these names so they can be transformed into separate CTest targets. Example command in CMake: execute_process( COMMAND "${TEST_SUITE_TEST_EXECUTABLE}" ${TEST_SUITE_TEST_SPEC} --list-tests OUTPUT_VARIABLE TEST_SUITE_LIST_TESTS_OUTPUT ERROR_VARIABLE TEST_SUITE_LIST_TESTS_OUTPUT RESULT_VARIABLE TEST_SUITE_RESULT WORKING_DIRECTORY "${TEST_SUITE_TEST_WORKING_DIR}" ) ## Generator of CMake Test Definitions Once the list of tests is available, the script generates a new `.cmake` file containing one `add_test()` call per discovered test. This file effectively defines the independent CTest targets. Example generated `tests.cmake` content: add_test(NAME UnitA.TestAlpha COMMAND my_test_executable UnitA.TestAlpha) add_test(NAME UnitA.TestBeta COMMAND my_test_executable UnitA.TestBeta) add_test(NAME UnitB.TestGamma COMMAND my_test_executable UnitB.TestGamma) This approach ensures each test is addressable, selectable, and independently reported by CTest. ## Post-Build Step Integration CMake can’t know these test names at configuration time, so we hook the test listing step to the build phase using a `POST_BUILD` custom command. After the test executable is built, this command runs the extractor and generates the script file defining the tests. Example: add_custom_command( # The executable target with the unit tests TARGET ${TARGET} POST_BUILD BYPRODUCTS "${TEST_SUITE_CTEST_TESTS_FILE}" # Run the CMake script to discover tests after the build step COMMAND "${CMAKE_COMMAND}" # Arguments to the script -D "TEST_TARGET=${TARGET}" -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>" -D "TEST_WORKING_DIR=${TEST_SUITE_WORKING_DIRECTORY}" # ... # The output file where the test definitions will be written -D "CTEST_FILE=${TEST_SUITE_CTEST_TESTS_FILE}" # The script that generates the test definitions -P "${TEST_SUITE_DISCOVER_AND_WRITE_TESTS_SCRIPT}" VERBATIM ) This ensures the test listing happens automatically as part of the build. ## Including Generated Tests The main CMake configuration includes the generated `.cmake` file, but only if it exists. This avoids errors on a test pass before the executable is built. This could happen because the user is calling `ctest` before the build step completes, because the test executable was not built, or because the cache was invalidated. So the discovery function uses the example pattern: if(EXISTS "${CMAKE_BINARY_DIR}/generated/tests.cmake") include("${CMAKE_BINARY_DIR}/generated/tests.cmake") endif() And this is the file that the test step will ultimately include in the CTest run, allowing CTest to see all the individual test targets. ## CMake Function for Reuse To make this easy for other libraries, the pattern can be wrapped in a CMake function. This function: * Defines the `POST_BUILD` rule for the given target. * Encapsulates the details of running the extractor script. * Ensures consistent output locations for the generated test definitions. Example usage: boost_url_test_suite_discover_tests(boost_url_unit_tests) This approach lets library maintainers adopt the system with minimal changes to their existing CMake setup, while maintaining Boost’s fine-grained, many-target test philosophy. When we look at CI results for Boost.URL, this is the only thing we used to have: /__w/_tool/cmake/3.20.0/x64/bin/ctest --test-dir /__w/url/boost-root/build_cmake --parallel 4 --no-tests=error --progress --output-on-failure Internal ctest changing into directory: /__w/url/boost-root/build_cmake Test project /__w/url/boost-root/build_cmake Start 1: boost_url_unit_tests Start 2: boost_url_extra Start 3: boost_url_limits 1/3 Test #2: boost_url_extra .................. Passed 0.00 sec 2/3 Test #3: boost_url_limits ................. Passed 0.00 sec 3/3 Test #1: boost_url_unit_tests ............. Passed 0.02 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 0.02 sec And now we see one unit test per test case: /__w/_tool/cmake/3.20.0/x64/bin/ctest --test-dir /__w/url/boost-root/build_cmake --parallel 4 --no-tests=error --progress --output-on-failure Internal ctest changing into directory: /__w/url/boost-root/build_cmake Test project /__w/url/boost-root/build_cmake Start 1: boost.url.absolute_uri_rule Start 2: boost.url.authority_rule Start 3: boost.url.authority_view Start 4: boost.url.compat.ada 1/76 Test #1: boost.url.absolute_uri_rule .......... Passed 0.01 sec Start 5: boost.url.decode_view 2/76 Test #2: boost.url.authority_rule ............. Passed 0.01 sec Start 6: boost.url.doc.3_urls 3/76 Test #3: boost.url.authority_view ............. Passed 0.01 sec Start 7: boost.url.doc.grammar 4/76 Test #5: boost.url.decode_view ................ Passed 0.01 sec Start 8: boost.url.encode 5/76 Test #4: boost.url.compat.ada ................. Passed 0.01 sec Start 9: boost.url.error 6/76 Test #6: boost.url.doc.3_urls ................. Passed 0.01 sec Start 10: boost.url.format 7/76 Test #7: boost.url.doc.grammar ................ Passed 0.01 sec Start 11: boost.url.gen_delim_chars 8/76 Test #8: boost.url.encode ..................... Passed 0.01 sec Start 12: boost.url.grammar.alnum_chars ... meaning that each test is now executed and reported individually and in parallel, allowing developers to see which specific tests passed or failed, and enabling more granular control over test execution. # Conclusion This approach brings fine-grained tests into the modern CMake Boost workflow. By splitting a single test executable into multiple independent CTest targets, maintainers gain: * **More granular failure reporting** : CI logs show exactly which test case failed. * **Better developer experience** : Developers can run or re-run individual tests easily. * **Improved parallel execution** : Faster test runs in CI and locally. * **Better IDE integration** : IDEs can show individual test cases. For other Boost libraries considering adopting this pattern, the only requirement is that their test executables support a `--list-tests` (or equivalent) command that outputs the available test cases. Once that’s available, the necessary CMake changes to define an equivalent function are minimal: * Add a `POST_BUILD` step that runs the listing command and generates the `.cmake` file. * Conditionally include that generated file in the main CMakeLists. If the output of `--list-tests` is one test suite per line, the existing script can be used as-is. This small investment pays off with a much more maintainable and CI-friendly testing setup. I encourage other maintainers and contributors to try this technique, refine it, and share feedback. The complete script and CMake snippets are available in the Boost.URL repository at commit #a1a5d18.
cppalliance.org
July 18, 2025 at 2:38 AM
Include Hallucinations for Adventures in AI-Generated C++
AI generated code has some fun side effects. Whilst generating a lot of code and testing it using Microsoft Visual Studio I had the odd experience of giving the AI model a line of buggy code, and it forthrightly states to replace this faulty line with this new line. On close inspection both lines are identical! Ha - in the AI world this is known as a “hallucination” - amusing but not the answer I was looking for. Other odd side effects of AI generated code include having unused “#include <header>” statements - I commented out a bunch of #includes that I was not sure about, and presto the code compiled and ran unimpressed with my efforts to disable it. Unused #includes don’t so much add errors but the appearance of complexity. As I am in the education business, complexity is something I try to avoid. I did find that the more complex the syntax - say in metaprogamming with templates, or shorthand means of referencing elements within a multi-dimensional matrix perhaps - the more likely AI would not get it right. My simplistic conclusion is that a C++ AI model will get the code right as long as you don’t veer too far from the standard language and the fewer libraries the better. In my also simplistic opinion this is not ready for prime time - we all use, and want to use, libraries to take much of the heavy-lifting away from development effort and time. Investigating the use of the enormous precision available using libraries such as Boost.Multiprecision, I am impressed by the huge prime numbers used in cryptography and the accuracy of constants in science such as astrophysics (gravitational constants, pi, zeta constants in string theory and the like). At the same time - fun fact - NASA’s orbital calculations use only around 15 digits of pi to get very precise orbits, so precision can become an obsession devolved of any practical application! With recent library releases, such as Boost.Hash, it seemed prudent to add pertinent hash terminology to the User Guide Glossary - help the uninitiated understand what it’s all about. Same for Open Methods and Bloom Filters - all adding more terminology to our already overloaded capacity. Another fun fact - Bloom filters are not named after anything to do with blooming - but simply after the name of the inventor - Burton Howard Bloom. Testing Boost libraries did pique my imagination several times. Many years ago, working with a smart human friend - a computer analyst - we devised a language to be used for giving orders in a battle simulation. “Marshal Davout, order one infantry and one cavalry division to attack the hill to the NorthEast”, for example. This involved three distinct steps - syntax analysis to check for correct wording, semantic analysis to check for correct grammar, and interpretation. The first two steps are logical and complete. The third step - interpretation is a toughie - “is there a hill to the NorthEast?” - and if not, what do I do? Similarly, “what if I have two infantry divisions available, but no cavalry, should I still attack?”. Duh - interpretation can be ambiguous and difficult. Wish Boost.Spirit had been available way back then, it has a granular approach to syntax and semantics I could recognize as being intuitive and useful - would have taken a lot of the work out of syntax and semantics, and left time for battling with the ambiguities of interpretation. Talking of useful Boost libraries, I did some testing with Boost.Asio, and writing and running client-server and peer-to-peer samples on multiple computers. Yikes it worked well and is largely intuitive. Go Asio. Perhaps one of the less-well documented topics involving Boost libraries is the topic of which libraries play well together, and which do not. I started building up some knowledge on this and added a “Combining Libraries” section to the User Guide FAQ - it is what I would want to know if I was a newcomer to Boost - many hands make light work - or many libraries make light work. May your AI always hallucinate slightly less than your compiler does.
cppalliance.org
July 18, 2025 at 2:37 AM
Boost.Bloom ready for shipping in Boost 1.89
During Q2 2025, I’ve been working in the following areas: ### Boost.Bloom The acceptance review for Boost.Bloom took place between the 13th and 22nd of May, and the final verdict was acceptance into Boost. Arnauld Becheler did an awesome job at managing the review, which was one of the most lively and productive I remember in recent years. Incorporating the feedback from the review took me the last five weeks of this quarter, but everything’s ready for shipping with Boost 1.89 (Aug 2025): * Review feedback as compiled by Arnauld * Post-review changes * Docs on `develop` branch ### Boost.ContainerHash * Added `boost::hash_is_avalanching` trait (PR#40). This trait was originally in Boost.Unordered, but it makes sense that it be moved here because Boost.Bloom also uses it. ### Boost.Unordered * Added `pull(const_iterator)` to open-addressing containers (PR#309). * CI maintenance work (PR#310, PR#311). * Improved documentation of `erase_if` (PR#312). * Deprecated `boost::unordered::hash_is_avalanching` in favor of `boost::hash_is_avalanching` (PR#313). * Addressed some user-filed issues (#305, #308). ### Boost.MultiIndex * CI maintenance work (PR#82). * Addressed a user-filed issue (#81). ### Boost.Flyweight * CI maintenance work (PR#21). ### Boost.PolyCollection * CI maintenance work (PR#31). ### Boost.Interprocess * Filed some issues (#258, #259). ### Boost website * Filed some issues (#1760, #1792, #1832). ### Boost promotion * Posted several tweets on Boost’s X account: * Boost 1.88 announcement * Boost.MQTT5 promotion * Boost.OpenMethod review announcement * New website announcement * Boost.Bloom review announcement * Boost.OpenMethod review result * Boost.Bloom review result * Promotion of a talk about cancellations in Asio * Promotion of a compilation of videos on Boost from the Utah C++ Programmers Group * I also did a special series on X covering the presence of Boost on the latest WG21 meeting in Sofia, Bulgaria: * Boost.Hash2 * Fiscal Sponsorship Agreement * Boost.Parser * Boost.Math * Boost.Unordered * Boost.MQTT5 ### Support to the community * Reviewed Jean-Louis Leroy’s Boost.OpenMethod proposal. * Did a small theoretical analysis of Boost.OpenMethod’s perfect hashing algorithm. * Did some `c2.py` testing for Chris Mazakas. * Supporting the community as a member of the Fiscal Sponsorhip Committee (FSC). The asset transfer from the Boost Foundation to the C++ Alliance was finally completed this quarter, which has enabled the launch of the long-awaited new website.
cppalliance.org
July 11, 2025 at 2:37 AM
Some Thoughts on Documentation
Most of my work during the first quarter of this year involved experimenting with things which haven’t yet reached the state where it would be interesting to discuss them. But there was a project that at this point is reaching its finish, and thus let’s focus on it. That project is AsciiDoc-based documentation for Boost.JSON. Previously the project used a setup that was not too dissimilar to that of many Boost libraries. Doxygen collected API reference docs from Javadocs comments in the project sources and outputted a set of XML files, then those XML files were converted to a Quickbook file. The file was included by the main Quickbook documentation source, which Quickbook converted into BoostBook (a dialect of DocBook). Finally, BoostBook XSLT stylesheets were used to transform files from the previous step to HTML (or some other format, e.g. PDF). At some point I got frustrated with issues with Docca, the tool we used to convert Doxygen XML to Quickbook and wrote a Python equivalent. I wrote about it back in July 2025. As noted there, the new tool’s design allows for changing of output formats and the resulting structure of the API reference documentation. Specifically, AsciiDoc and producing one page for a set of function overloads was mentioned. Well, this is exactly what soon will be used for Boost.JSON’s docs. (Menwhile, here’s a preview). The first step of the change was of course conversion from Quickbook syntax to AsciiDoc syntax. The next step was making the output multi-page. The default Asciidoctor HTML backend only produces a single page. I could create multiple source files and generate a separate page from each one, but that creates two problems: brittle cross-references between sections and no table of contents for the entire thing. So, instead I took an existing Asciidoctor plugin, asciidoctor-multipage, and using its core idea I wrote my own, as I needed somewhat different behaviour. The core idea of the plugin is that certain sections are removed from their parent sections and spawn separate documents. The third step involved structuring Javadocs comments very differently. Analysing Cpprefernce pages for overloaded functions I came to the idea that rather than documenting every overload separately, I could put all documentation text into the Javadocs comment of the first one, and reference every overload by their number there. There is a catch, though. Overloads have different parameter lists and Doxygen warns if a Javadocs comment documents a parameter that the function doesn’t have or doesn’t document a parameter it does have. I worked around that by using most of the description for the first overload, but collecting parameter list documentation from all overloads. In the end it came out nicely. The coolest thing about it is that I didn’t have to change anything in my Python version of Docca, everything I needed was possible with just a custom Jinja template. The current target for Boost.JSON’s documentation pipeline is Doxygen + Python Docca + Asciidoctor. In the future I’m aiming towards replacing Doxygen with Mr.Docs, another exciting project from the C++ Alliance. I also hope that this change will motivate other projects to switch to AsciiDoc.
cppalliance.org
May 1, 2025 at 2:34 AM
Unordered’s New Look
## Boost.Unordered When I’m not busy eternally grinding on implementing hashing algorithms, I’ll sometimes take a moment and help other Boost libraries. I enjoy being a kind of soldier of Boost. Deploy me to any library in the project and I’ll dive in and get results. This is actually how my involvement with Regex started and I’ve done this a couple of other times to fix some bugs in both Serialization and Optional. This time around, it was a library I’ve worked on previously: Unordered. Because hash tables are such an important data structure and because Boost has the currently-reigning implementation, the library matters a lot. As important as the code is, the documentation for it matters just as much and for the longest of times Unordered has had terrible documentation. In its earliest forms, the Unordered docs were a PHP template file that was dynamically templated based on the container. I came in and chose a more brutish approach, just creating hard-coded files where everything was manually transliterated to AsciiDoc. From here, the HTML was generated as a single-page using the Asciidoctor tool. Many Boost libraries have started using this and if you ever open a library’s docs and you see it as a single page with a table-of-contents on the left, it’s a safe bet that the page is an asciidoctor product. The problem though is that this doesn’t really scale well as your documentation grows. In the case of Unordered, we were by far the worst offendors here. We had full reference docs for all the containers along with all the sets of images showing the benchmarking results. This meant that page load was impressively long and even worse, the docs were a nightmare to navigate. Trying to search the page for something like `emplace` would yield several unrelated results and would bounce you around the page randomly. Luckily, the Asciidoctor developers seem to recognize the limitations of their tooling but still recognize the tremendous value AsciiDoc has as a markup language. AsciiDoc is basically just a more powerful version of markdown, something most developers enjoy and know well. Antora fixes the previous problems with Asciidoctor and can output its HTML as a series of disparate files. This now means that Unordered’s docs are officially multi-page and that they’re actually useful and readable now. The change wasn’t entirely straight-forward and there were some difficulties in setting it all up. Nothing that couldn’t be overcome, however. Hopefully Antora becomes more of a standard default in the Boost developer’s toolchain. Asciidoctor has served us well for a little while but it’s clear that Antora is an eventuality all library docs will eventually gravitate towards. ### The Future of Boost’s Tools Far and away the most difficult aspect in dealing with Antora was actually getting it to work reliably with b2, Boost’s home-grown build tool. b2 is fantastic at silently just not doing things. What’s even worse, building Boost docs for a release is something that’s not readily done with a simple command or two after an `apt install`. Instead, it essentially _requires_ a Docker container which holds the plethora of tools required to build the docs. b2 will typically only run something if it’s been defined as a target it can understand but this is seldom as straight-forward as something like `add_custom_target()` or `add_custom_command()`. This ultimately proved not to be a blocker for the documentation updates because I received some fantastic help from a colleague but at the same time it made me concerned because genuinely, only one person in the entire Boost project really understands. Or at least, that’s my perception of the situation after I asked for code review from other Boosters. The purpose of the Jamfile was to simply ensure that b2 understood certain builds artifcacts _needed_ to be generated when `b2 doc` gets run. When Boost started, CMake didn’t technically exist. It came out a couple of years after and what’s more, it would’ve been incredibly immature and unusable for the project. But now in 2025, things are quite different. We live in a world where now no one outside Boost really seems to use b2 or write Jamfiles anymore. Most developers aren’t exactly CMake experts either but it’s possible to actually google CMake issues and find dedicated forums and outside experts. Not using this as a soap box to simply rant about a build tool but the whole experience did make me reflect on the future of the project. What if my colleague wasn’t there to help me out? I maybe could’ve worked with the release managers to update the requisite scripts for Unordered but it would’ve been a divergent path from the other libraries, which is almost always a net negative. What if the Alliance wasn’t there to fund paid staff engineers to sit there and sift through a now-niche build system with relatively bare docs? Packagers themselves have migrated to relying on Boost’s CMake support. vcpkg builds Boost libraries using the CMake workflow and CMake itself has obsoleted their FindBoost.cmake in favor of the BoostConfig.cmake that gets generated by both `b2 install` and the CMake workflow. Projects live and die by their communities. On a technical level, I really don’t think CMake is much better than b2. The syntax in CMake is somehow better and things are a lot less runic as well. Jamfiles are very good at looking like an arcane incantation. But when it comes down to it, b2 has way more flexibility and power for what Boost needs which is to be able to build tests with multiple compilers and multiple standards and flags with a single invocation. The real problem is the lack of robust documentation and things like StackOverflow answers. There’s no dedicated slack channel like #cmake on the C++ slack (which everyone should join) for b2. Even if there, it’d be the only two people in the project today answering any questions. I think Boost is slowly approaching a fork in the road where it needs to get realistic about its future. Or maybe it doesn’t matter. Maybe we’ll just slap everything in a docker container and if we need to gradually hack into the build scripts with custom exceptions and carveouts, so be it. It’ll be interesting to see how right or wrong I am in the future. Luckily for most, I have a tendency towards being wrong. – Christian
cppalliance.org
April 29, 2025 at 2:36 AM
Looking at the Numbers
We continue to make exciting progress developing new libraries for inclusion in Boost. # New Libraries ## Decimal Decimal (https://github.com/cppalliance/decimal) is a ground-up implementation of IEEE 754 Decimal Floating Point types in C++14, co-authored with Chris Kormanyos. In January we had our formal review for inclusion in Boost. Unfortunately, we were not accepted, but we were also not rejected. There were a lot of comments that we have addressed in the past few months, and many other optimization path we are pursuing. We will look at doing a mini-review later in the year once we talk with our review manager, and the Boost community on the updated state of the library. The library is certainly better now than it was in Janurary, and will continue to improve over the coming months. We welcome users to try the library and provide feedback before we pursue a second review. Discussions are ongoing in the Cpplang Slack channel `#boost-decimal`. ## int128 Int128 (https://github.com/cppalliance/int128) is a small library that has more or less fallen out of work on Decimal. It provides two type: an unsigned 128-bit integer and a signed 128-bit integer. There are plenty of applications of 128-bit integers, but trying to make them portable is a pain. Since Decial uses 128-bit integers behind the scenes we decided to pull those parts out into their own library. We have optimizations for a variety of 32-bit and 64-bit platforms as we benchmark all commits extensivly. Look for this one to be announced shortly. ## Crypt I know there are a few people interested in this library. Unfortunately, due to the above two libraries we did not have as much time for Crypt as we would have liked. We did make some improvements to memory safety, and contiue to make architectural improvements. This library will continue to develop over the coming months. We think it will be worth the wait. # Existing Libraries ## Random By popular demand I added a number of PRNGs from the XOSHIRO Family (128, 256, and 512 bits) which shipped with Boost version 1.88. If you are unsure of which generator you need in your code this family of generators is a good starting point. Details and performance measurements can be found in the Boost.Random docs starting with Boost version 1.88.
cppalliance.org
April 17, 2025 at 2:33 AM
Moving Boost forward: Asio, coroutines, and maybe even modules
It’s been an exciting quarter over here. Both old-but-refreshed and brand new projects are flourishing in the Boost ecosystem, making today a really exciting moment to contribute to it. ## using std::cpp 2025: a tale of coroutines, timeouts and cancellation in Asio I had the pleasure of speaking at using std::cpp 2025, the biggest C++ conference in Spain. I even had Bjarne Stroustrup himself among the audience! The talk was focused on how to write simple but effective asynchronous Asio code. Asio is almost two decades old, yet it keeps improving. Since Boost 1.86, code like this is legal: // Write a message over a socket, and set a timeout to the operation co_await asio::async_write(sock, asio::buffer(buff), asio::cancel_after(20s)); My talk addressed the mechanisms that power this kind of code, together with many tips gathered by making mistakes while working on Boost.MySQL and related projects. The slides and code snippets I used can be found here, and a recording of the talk will be available soon. I’ve also updated the servertech chat project, a chat application with a server powered by Boost libraries. It now uses most of the features that I talked about during the talk, including timeouts and C++20 coroutines. Since the project was using `asio::yield_context`, I wrote a small benchmark and found out that C++20 coroutines were ~10% faster. This might not be the case for all projects, though. ## C++20 modules and Boost I’ve led an initiative to natively support C++20 modules in Boost. The ultimate goal is to enable the user write code like: import boost.mp11; using L1 = boost::mp11::mp_list<int, float, int, float>; The point of the prototype is to be as complete as possible. I firmly believe that just writing the module units (i.e. the `boost_mp11.cppm` file) is far from enough. This is essentially shipping untested code to the user. Additionally, building modules is not trivial, specially when there are complex dependency chains. For this reason, the prototype also includes: * CMake files to build and install the modules, compatible with today’s Boost CMake infrastructure. * Running the library’s entire test suite with modules enabled. * CI scripts to automate running these tests. I’ve fully migrated Boost.Mp11 (as a representative of header-only libraries) and Boost.Charconv (representing compiled ones). I’ve found a myriad of problems, including compiler bugs, but the result is functional. Compile times wins are really significant - building mp11’s testsuite with modules took only a fourth of the time it takes with headers. We haven’t merged any of this yet, since it relies on CMake’s experimental features, and there are still many compiler bugs to be fixed. I’ve summarized my findings in this article. While working in Charconv, I found some problems unrelated to modules and helped the author fix them. ## Boost.MySQL benchmarks When you use Boost.MySQL, it’s logical to ask yourself “is this much slower than the official drivers?”. Spoiler alert: it’s as fast as libmysqlclient and libmariadb, and even faster under some of the benchmarks. I didn’t have the data to answer this question until recently, since there are no standard benchmarks to measure this. I’ve designed four benchmarks that focus on the protocol implementation. This includes reading both small and big rows, big batches of rows and using statements with big parameters. Full information available here. While I’m pretty satisfied with the results, I’ve also identified some points where performance could be improved. These will be addressed in the near future. ## Boost.MySQL new features and maintenance Per user request, I’ve added `any_connection::connection_id()`, equivalent to `mysql_thread_id()` in the official API. This allows using `KILL` statements to cancel queries. I’ve also improved safety by detecting the violation of some preconditions and issuing errors rather than incurring in undefined behavior. For example, attempting to start an asynchronous operation in an `any_connection` while another one is outstanding is no longer UB: // Since Boost 1.88, this is no longer UB. // The second operation will fail with client_errc::operation_in_progress conn.async_execute("SELECT 1", r1, {}); conn.async_execute("SELECT 1", r2, {}); I’ve fixed some other minor bugs and made some improvements to the docs. The most remarkable is the addition of a [fully-fledged HTTP server example] (https://github.com/boostorg/mysql/tree/07200f17c28293e910151abbc46d22eeff944384/example/3_advanced/http_server_cpp20) that uses C++20 coroutines. I’m in the process of writing the equivalent using callbacks, for users that prefer them. ## Boost.Decimal review Another exciting submission. As usual, I contributed to it by integrating it into Boost.MySQL, to parse `DECIMAL` types. ## Next steps I’m preparing a Postgres library that I aim to submit to Boost on the long run. It’s still in a very early state. I intend to make it similar to Boost.MySQL, but exposing the protocol interface as a public API. More on this next quarter!
cppalliance.org
April 17, 2025 at 2:33 AM
Cloud and Infrastructure Update Q1 2025
Here’s an overview of some projects I have been working on the last few months. ### Server Upgrades The Ubuntu LTS ‘Long Term Support’ release 24.04 was officially available in April 2024, however it’s not advisable to upgrade existing servers until the release has a chance to be field-tested and validated. Among other issues, the Ubuntu 24.04 zip package “fails when filenames contain unicode characters”, which was a serious bug that affected release-tools. By April 2025 though, it seems reasonable to upgrade. The C++ Alliance is hosting around 35 server instances, many small or nano in size, for a variety of purposes. This quarter, upgraded all the operating systems to 24.04. If a workload is critical, there is a risk an in-place upgrade will disrupt that system, and so it’s preferable to rebuild the machine from scratch and migrate. That is especially important for postgres database servers because an upgrade generates a completely new DB service with an empty database, causing multiple DBs to be running. Rebuilding an application server from scratch may involve many components. What if something gets skipped? To be sure everything is accounted for, I composed some new installation scripts with Ansible, that will allow future upgrades of 26.04 or 28.04 to run more easily also. For example, the github repository `cppalliance/ansible-paperbot` installs the Slack integration Paperbot onto a new server. To see what this does, in the Cpplang Slack workspace, invite @npaperbot to a channel and try “@npaperbot search constexpr”. Another new installer `cppalliance/ansible-drone` migrated the Alliance’s Drone server to a new instance. This script includes crontab entries to clear frozen jobs, monitoring probes reporting the number of autoscaled instances, and automatically copies the postgres database from backup archives. Upgrading many servers at once has a benefit of serving as an inventory/review. Small problems can be discovered and corrected. For example, a server instance boost-library-stats.cpp.al depends on the Github API to retrieve repo statistics. The API format changed after a few years so the code needed a refresh. Another (recurring) issue, the latest Ubuntu is the first one to require Python virtual environments, which were purely optional in 22.04. ### boost-ci Upgraded lcov from v1.15 to v2.3 in codecov.sh. While that sounds uneventful, in fact it turned into a project, since the new lcov generated numerous “ERRORS” that hadn’t existed before. Almost certainly if you leverage boost-ci your boost library now has failing coverage reports using lcov 2.3 - except that through careful testing we have enabled “ignore-errors”. The ignored errors are “inconsistent, mismatch, unused”. Examine the CI output of recent codecov and search for the string “warning”. The errors have been replaced by warnings. Of the 20 possible lcov error conditions, how can we be sure which need to be ignored? I wrote an automated report (in the feature/reports branch of boost-ci) that ran lcov on all boost libraries. The results confirmed the common errors. This is customizable with $LCOV_OPTIONS and $LCOV_IGNORE_ERRORS_LEVEL (see codecov.sh) so you may voluntarily switch back to a crashing codecov reports at any time. You might think “wait, you should leave errors enabled, and force developers to fix their mistakes.” However there are multiple reasons not to do this. The main one is that lcov is encountering errors in the standard library itself, and so everyone’s coverage reports would fail, without any way to fix that. Next, many boost developers are part-time and do not have unlimited resources. Should their coverage reports remain broken/unusable for a year or two before they are able to devote the proper time to fix them? And finally, the errors have not been hidden or removed. They have simply been converted to warnings that are still visible in the log output. ### Jenkins General adjustments to the new configuration and jenkinsfiles. New automated docs for boostorg/bloom library candidate. Iterations of redesigns for the boostorg/unordered docs. Migrate preview hosting to jenkins2. Among the Jenkins jobs are gcovr/lcov coverage reports. Refactored and improved those bash scripts, in the ci-automation repo. ### Boost website boostorg/website-v2 Experimentation (ongoing) to install a Horizontal Pod Autoscaler HPA in the k8s cluster, which will automatically scale the deployment, and therefore allow a smaller deployment in steady-state when the cluster is not busy. Wrote a git deploy script for Rob. Mailman db dumps. Discussions about tino,non-tino deployments in boostlook. ### Boost release process boostorg/release-tools Multiple small bug fixes. JFrog was finally shut off which affected boost.org and wowbagger. Upgraded CDN origin servers. Merged PRs to use webhooks on boost.io, as well as git tagging during releases. ### Drone Dealing with the IBM Cloud account, which perpetually has billing problems on their side. Re-enabled account. Migrate IBM servers from São Paulo to Dallas. Implement git fetch timeouts, which did not exist in the drone-git codebase, and was a necessary bug fix. Sent them a PR. Database query scripts to verify results. ### GHA New Windows 2025 images. ### JSON Benchmarks Format/lint script. boostorg/json bench output changed, and so the ingest of the data needed updating. New monitoring alerts, email alerts.
cppalliance.org
April 17, 2025 at 2:33 AM
Krystian’s Q4 2024 Update
# Clang This quarter, I continued working on the _fourth_ iteration of my refactoring of multi-level template argument list collection (#112381, #114258 and #114569 related), fixed support for the `@relates` command, made improvements to C++ conformance, amongst other things. ## Multi-level template argument list collection (again) My initial patch that refactored multi-level template argument list collection proved to have some nasty bugs relating to instantiation order. The following is a reduction of a reported regression when compiling QT: template<int N> struct A { template<typename T> static constexpr bool f(); }; template<> template<typename T> constexpr bool A<0>::f() { return A<1>::f<T>(); // note: undefined function 'f<int>' cannot be used in a constant expression } template<> template<typename T> constexpr bool A<1>::f() { return true; } static_assert(A<0>::f<int>()); // error: static assertion expression is not an integral constant expression I initially thought Clang was correct to complain here, since the member specialization `A<1>::f` does not precede its first use (lexically), ergo IFNDR per [temp.expl.spec] p7: > If a template, a member template or a member of a class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. However, the declaration of the member specialization is reachable from a point in the _instantiation context_ of the definition of `A<0>::f<int>` (that being, immediately prior to the _static_assert-declaration_), so this example is indeed valid. ## Explicit specialization of members of partial specializations On the C++ conformance side of things, I landed a patch implementing [temp.spec.partial.member] p2: > If the primary member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the partial specializations of the member template are ignored for this specialization of the enclosing class template. If a partial specialization of the member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the primary member template and its other partial specializations are still considered for this specialization of the enclosing class template. Its meaning can be illustrated via the following: template<typename T> struct A { template<typename U> struct B { static constexpr int x = 0; // #1 }; template<typename U> struct B<U*> { static constexpr int x = 1; // #2 }; }; template<> template<typename U> struct A<long>::B { static constexpr int x = 2; // #3 }; static_assert(A<short>::B<int>::y == 0); // uses #1 static_assert(A<short>::B<int*>::y == 1); // uses #2 static_assert(A<long>::B<int>::y == 2); // uses #3 static_assert(A<long>::B<int*>::y == 2); // uses #3 Since the primary member template `A<long>::B` is explicitly specialized for a given (implicit) specialization of its enclosing class template, the partial specialization `B<U*>` will be ignored when instantiating a specialization of `B`.
cppalliance.org
January 22, 2025 at 6:23 PM
Boost.Http.Io and Boost.Http.Proto Project Highlights
Here’s a look at some recent projects I’ve been focusing on: ## Boost.Http.Io ### `burl` project We’ve recently started adding a new example to the http-io library called burl. This is a relatively large example application designed to serve as a drop-in replacement for Curl’s HTTP functionality. The primary goal of the project is to ensure that http-proto and http-io provide all the necessary features for building a curl-like application. It also aims to demonstrate how these libraries can be leveraged to perform common HTTP tasks. The project has made excellent progress so far, with support for around 90 Curl command-line options, covering nearly all HTTP and TLS-related features provided by Curl. During development, we identified the need for three additional libraries (or http-proto services) that could benefit users: * **Multipart/form-data Library** : A container and parser/serializer for working with form data on both the client and server sides. * **CookieJar Library** : A utility for storing cookies and tracking modifications made to the jar. * **Public Suffix List Library** : A library that utilizes Mozilla’s Public Suffix List to accurately and efficiently identify a domain suffix. This is crucial for enhancing the CookieJar implementation by preventing supercookie vulnerabilities and proper validation of wildcard SSL/TLS certificates. ## Boost.Http.Proto ### Different Styles of Body Attachment in the Parser Interface The http-proto library is a sans-IO implementation of the HTTP protocol. It is designed to facilitate the reception of HTTP message bodies with minimal bookkeeping required from the user at the call site. Currently, the library supports three distinct styles of body attachment: #### In-Place Body This style allows users to leverage the parser’s internal buffer to read the body either in chunks or as a complete view if it fits entirely within the internal buffer. This approach is efficient for scenarios where the body size is known to be small or when incremental processing is required. read_header(stream, parser); // When the entire body fits in the internal buffer read(stream, parser); string_view body = parser.body(); // Reading the body in chunks while (!parser.is_complete()) { read_some(stream, parser); auto buf = parser.pull_body(); parser.consume_body(buffer::buffer_size(buf)); } #### Sink Body The sink body style allows users to process body content directly from the parser’s internal buffer, either in one step or through multiple iterations. This method is particularly useful for writing body data to external storage, such as a file. The parser takes ownership of the sink object, driving the processing logic by invoking its virtual interfaces. This style is ideal for streaming large bodies directly to a sink without needing to hold the entire body in memory. read_header(stream, parser); http_proto::file file; system::error_code ec; file.open("./index.html", file_mode::write_new, ec); if (ec.failed()) return ec; parser.set_body<file_body>(std::move(file)); read(stream, parser); #### Dynamic Buffer The dynamic buffer interface allows the parser to write body content directly into a user-provided buffer or container, reducing the need for additional copying and intermediate buffering. read_header(stream, parser); std::string body; parser.set_body(buffers::dynamic_for(body)); read(stream, parser);
cppalliance.org
January 22, 2025 at 6:23 PM
Hashing and Matching
## Boost.Hash2 I’m happy to report that the library I helped Peter Dimov develop, Hash2, was accepted after probably one of the most thorough Boost reviews to have happened in recent history. I can’t claim to have contributed all too much to the design. After all, Hash2 was an implementation of Types Don’t Know #. But I did come along and help implement myriad algorithms and help with the absolutely massive testing burden. Interestingly, I think people who don’t sit and write/maintain Boost libraries all day underestimate just how much testing even something like 10 extra lines of source can require. When you write software with a certain minimum bar of quality, almost all your effort and time go into testing it than anything else. This is because if a Boost library gets it wrong, there’s really no good way to unwind that. Bad versions of `libboost-dev` will have already gone out and then packagers need to re-package and the whole thing is a huge debacle for users and packagers. Working on Hash2 is fun and engaging and more importantly, it finally gives C++ developers something reputable and makes hashing as easy as it should be. The only problem with Hash2 that I can think of as a user of Boost would be that it took until 2024 (and now basically 2025) for Boost to have simple and effective hashing routines. For example, std::string get_digest(std::vector<char> const& buf) { boost::hash2::sha2_256 h; h.update(buf.data(), buf.size()); return to_string(h.result()); } Very simple, very nice and clean and does what it says on the box. The version of the library that was accepted is also far from the final version as well. The library will continue to evolve and quality of implementation will be iterated on and the interfaces will naturally be refined. It’s good for reviewers and authors of Boost libraries to keep in mind that libraries aren’t some static thing that are carved out of stone. The accepted version of a Boost library is very seldom similar to the version 4 releases down the line. Boost.Asio is probably the most emblematic of this, having undergone dramatic refactors over the years. One thing I’m particularly looking forward to is experimenting with sha-256 intrinsics available on certain Intel CPUs but that’ll come later once the base sha2 family has had a nice performance overhaul and maybe a few new algorithms are also added to the collection. ## Boost.Regex I’ve also started working with Boost.Regex author John Maddock to squash CVEs filed against Boost.Regex found by Google’s oss-fuzz project, which is a wonderful contribution to the world of open-source. While as developers we may use regexes in our day-to-day lives, it’s an entirely different world to actually implement a regex engine. Learning what goes into this has been fascinating and I do have to admit, I’m tremendously humbled by John’s prowess and ability to navigate the complexities of the state machine construction. In a similar vein, I’m equally impressed at just how effective fuzzing is at crafting input. I’ve known about fuzzing for a good bit of time now as most modern software developers do but I’ve never stopped to sit down and truly appreciate just how valuable these tools are. One of the first things I essentially had to do for the repo was get it to a place where clangd could handle the generated compiled_commands.json. clangd is one of the better developer tools to come out but has caveats in that it can’t handle non-self-contained header files and old school Boost libraries like to `#include` various implementation headers at the bottom. Fixing this for clangd normally requires recursive `#include`s or just not using implementation headers. In most cases, it’s easiest to deal with the recursion and solve it by simply just adding `#pragma once`. Because this is Boost, the Config module even has all the macros that help detect when `#pragma once` is available so we can support all the compilers and all the toolchains no matter what. I look forward to continuing to work with John on Regex but in the interim, I’m having fun taking a Hash2 break. – Christian
cppalliance.org
January 21, 2025 at 6:23 PM
Boost.MySQL 1.87 and the new Boost citizens
## Boost.MySQL 1.87 I already anticipated in my previous post that Boost 1.87 was going to be an exciting release for the users of Boost.MySQL. Many new features have been promoted to stable, making using the library much more enjoyable. After putting the final touches during this last month, Boost 1.87 was released on December the 12th with all these new features. Many of these changes make frequent tasks much easier. In this post, we will review some of the recommendations that changed in this release. We suggest sticking to these for new code. Old code will keep working as expected. ### Type-erased connections `any_connection` is the new recommended way to open connections to MySQL. It features simpler connection establishment semantics, more functionality and lower compile times with no loss of performance. We recommend using it over `tcp_ssl_connection` and friends in new code. Since an example is worth a thousand words, here’s one: // Boost 1.86 int main() { // The execution context, required for all I/O operations asio::io_context ctx; // The SSL context, required for connections that use TLS. asio::ssl::context ssl_ctx(asio::ssl::context::tlsv12_client); // Construct the connection mysql::tcp_ssl_connection conn(ctx, ssl_ctx); // Resolve the hostname to get a collection of endpoints auto endpoints = resolver.resolve("localhost", mysql::default_port_string); // Parameters specifying how to perform the MySQL handshake operation. mysql::handshake_params params( "some_username", "some_password", "some_database" ); // Connect to the server using the first endpoint returned by the resolver conn.connect(*endpoints.begin(), params); } // Boost 1.87 int main() { // The execution context, required to run I/O operations. asio::io_context ctx; // Represents a connection to the MySQL server. mysql::any_connection conn(ctx); // The hostname, username and password to use mysql::connect_params params { .server_address = mysql::host_and_port("some_host"), .username = "some_username", .password = "some_password", .database = "some_database", }; // Connect to the server conn.connect(params); } ### Client-side SQL formatting and with_params `with_params` can be used instead of prepared statements for one-off queries: // Boost 1.86 void lookup(mysql::tcp_ssl_connection& conn, int id) { // Prepare a statement mysql::statement stmt = conn.prepare_statement("SELECT * FROM user WHERE id = ?"); // Execute it mysql::static_results<user> res; conn.execute(stmt.bind(id), res); // Close it conn.close_statement(stmt); // Do something with the results } // Boost 1.87 void lookup(mysql::any_connection& conn, int id) { // Execute your query mysql::static_results<user> res; conn.execute(mysql::with_params("SELECT * FROM user WHERE id = {}", id), res); // Do something with the results } Since I already talked about this feature in my last post, I won’t delve into more detail here. ### Connection pools Might be the most requested feature in the library. Establishing sessions is costly, especially if TLS is enabled. Maintaining connections alive and reconnecting them on failure is also non-trivial. Connection pools do both for you, so you can focus on your queries, rather than on the infrastructure: // Boost 1.87. There's no equivalent in previous versions! int main() { asio::io_context ctx; // pool_params contains configuration for the pool. mysql::pool_params params { .server_address = mysql::host_and_port("my_server_hostname.com"); .username = "my_username", .password = "my_password", .database = "my_database", }; // Create the pool and run it. async_run maintains the connections healthy mysql::connection_pool pool(ctx, std::move(params)); pool.async_run(asio::detached); // ... } asio::awaitable<void> lookup(mysql::connection_pool& pool, int id) { // Get a connection from the pool. We don't need to connect or close the connection mysql::pooled_connection conn = co_await pool.async_get_connection(); // Execute your query mysql::static_results<user> res; co_await conn->async_execute(mysql::with_params("SELECT * FROM user WHERE id = {}", id), res); // Do something with the results } ### Built-in diagnostics in exceptions when using async functions MySQL may produce diagnostic text when queries fail. You can get this passing a `diagnostics` object that will be populated on error. This, combined with Asio’s built-in error checking, made using throwing async functions cumbersome. As I already explained in my previous post, `with_diagnostics` and default completion tokens solve this problem: // Boost 1.86 asio::awaitable<void> handle_request(mysql::tcp_ssl_connection& conn) { mysql::results r; mysql::diagnostics diag; auto [ec] = co_await conn.async_execute("SELECT 1", r, diag, asio::as_tuple(asio::deferred)); mysql::throw_on_error(ec, diag); } // Boost 1.87 asio::awaitable<void> handle_request(mysql::any_connection& conn) { mysql::results r; co_await conn.async_execute("SELECT 1", r); } During these last months, I’ve polished these features. I’ve fix a myriad of small issues in `connection_pool`, made `mysql::sequence` owning (and thus easier to use as argument to `with_params`), and made `mysql::with_diagnostics` more interoperable with other tokens, like `asio::as_tuple`. ## A new exposition for Boost.MySQL With great power comes great responsibility. I strongly believe that these new, exciting features are almost worthless if they are not properly explained. I wrote Boost.MySQL docs some time ago, and user experience has changed my mind on how an exposition should be. I’ve re-written many of the pages for this release, making them more practical, with more examples and use cases. I’ve introduced not one, but seven tutorials, slowly walking the user through the most common MySQL (and Asio) concepts to get them up to speed. And I’ve added new examples out of questions I received on GitHub. The old discussion used sync functions to expose library concepts, because they were by far the easiest to use. This lack of async examples led many users down to using sync functions when they shouldn’t. Now that C++20 coroutines yield clean code, I’ve re-written part of the exposition to use them, providing more guidance on async patterns. As not everyone can (or want) to use them, I’ve also added some pages on how to port C++20 coroutine code to other completion styles. Writing all this has proven to be really time-consuming. Some might think of it as unexciting, but I hope my users will appreciate it. I’d like to thank the C++ Alliance sponsorship here, because these new docs wouldn’t be possible without it. My next step here will be migrating the docs to Asciidoc, so they get a “younger” look and feel. This implies moving from the old Docca toolchain to an Asciidoc generator like MrDocs. I’ve had the pleasure to be an early user of the tool, and been able to provide some (hopefully useful) feedback to its authors. My thank you to Allan, Krystian and Fernando here. ## New citizens in Boost: MQTT5 and Hash2 It’s been some intense months in Boost. I’ve had the pleasure to participate in three Boost reviews: a MQTT5 Asio-based library, an SQLite wrapper and a hashing library. I’ve been specially involved in the first one, since Asio is one of my main areas of expertise. With this library, our Asio ecosystem grows, and with it, the overall usefulness of Boost. ## Other contributions One of my features that I implemented in Boost.MySQL required me to write a `std::to_array` backport. I’ve contributed it to Boost.Compat, with the help of its maintainer, Peter Dimov. As always, a great learning opportunity. I’ve also helped with some maintenance tasks in Boost.Redis.
cppalliance.org
January 22, 2025 at 6:23 PM
New container in Boost.PolyCollection, additions to Boost.Mp11 and more
During Q4 2024, I’ve been working in the following areas: ### Boost.Unordered * Updated CI support (PR#293, PR#296, PR#297, PR#298). * Prepared a private document for Peter Dimov and Braden Ganetsky discussing massively parallel scenarios where ParlayHash has better performance than `boost::concurrent_flat_map`. We haven’t been able to progress much further than that in Q4 2024, mainly because of my lack of availablity for this specific task. * I’ve set up and run benchmarks comparing `indivi::flat_umap` with `boost::unordered_flat_map`. Although Indivi is generally slower, a conversation with the author led to some interesting design discussions that may be worth exploring further. * After the last major update in Boost 1.87.0, the backlog for Boost.Unordered is basically cleared. This means that the library will likely enter into maintenance mode, except if new requests show up —do you have any? ### Boost.PolyCollection * Updated CI support (PR#22, PR#23). * Added the new `boost::variant_collection` container (to be released in Boost 1.88.0). `boost::variant_collection_of<Ts...>` is similar to `std::vector<std::variant<Ts...>>`, with the crucial difference that elements storing the same alternative type are grouped together. For instance, the following: boost::variant_collection_of<int, double, std::string> c; // ... for(const auto& v: c) { visit( [](const auto& x) { std::cout << x << "\n"; }, v); } will print first the `int`s in the collection, then the `double`s, and finally the `std::string`s. In exchange for this restriction, important processing speedups can be obtained. ### Boost.Mp11 * Implemented `mp_lambda` (released in Boost 1.87.0), a metaprogramming utility inspired by the venerable _placeholder expressions_ from Boost.MPL. `mp_lambda` allows us to generate complex types specified with a rather natural syntax in terms of elementary input types indicated by Boost.Mp11 _placeholder types_. For instance, `std::pair<_1*, _2*>` can be regarded as a type template with two placeholder positions, and `mp_lambda<std::pair<_1*, _2*>>::fn<int, char>` is, unsurprisingly enough, the type `std::pair<int*, char*>`. My original motivation for writing this utility is to provide a Boost.Mp11 equivalent to Boost.MPL lambda expressions that can pave the way for an eventual modernization of Boost.Flyweight, which relies heavily on this functionality from Boost.MPL. * Rewritten the implementation of `mp_is_set` (PR#100, released in Boost 1.87.0) to achieve some rather noticeable compile-time improvements. ### Boost.MultiIndex, Boost.Flyweight * Updated CI support (PR#75, PR#78, PR#20). * Investigated issue with Boost.Interprocess (#236) that was causing Boost.Flyweight tests to fail in GCC/MinGW. ### Boost promotion and new website * Authored the Boost 1.87 announcement tweet, and the Boost.Hash2 acceptance tweet. * Held some meetings with Rob’s team on Asciidoc display problems and a new `latest` URL scheme for doc publication. I’m very excited about the latter, as this addition will likely help us improve SEO and fight outdated archived links to the libraries docs: for instance, the link https://www.boost.io/doc/libs/latest/doc/html/boost_asio.html will _always_ point to the latest published version of Boost.Asio documentation, unlike version-specific links such as https://www.boost.io/doc/libs/1_87_0/doc/html/boost_asio.html. * Filed some issues (#1549, #1576, #1577). ### Support to the community * I’ve proofread Braden Ganetsky’s article on Natvis testing. * As a member of the Fiscal Sponsorhip Committee (FSC), we’re having conversations with For Purpose Law Group to finalize the writing of the Boost/C++ Alliance Fiscal Sponsor Agreement (FSA), and with the Boost Foundation to help them complete the transfer of the assets to be controlled by such FSA, most importantly the ownership of the boost.org domain. Both tracks are making progress, albeit at a lower pace than desired.
cppalliance.org
January 22, 2025 at 6:23 PM