|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +nav-class: dark |
| 4 | +categories: gennaro |
| 5 | +title: "DynamicBitset Reimagined: A Quarter of Flexibility, Cleanup, and Modern C++" |
| 6 | +author-id: gennaro |
| 7 | +author-name: Gennaro Prota |
| 8 | +--- |
| 9 | + |
| 10 | +Over the past three months, I've been immersed in a deep and wide-ranging |
| 11 | +overhaul of the Boost.DynamicBitset library. What started as a few targeted |
| 12 | +improvements quickly evolved into a full-scale modernization effort—touching |
| 13 | +everything from the underlying container to iterator concepts, from test |
| 14 | +coverage to documentation style. More than 170 commits later, the library is |
| 15 | +leaner, more flexible, and better aligned with modern C++ practices. |
| 16 | + |
| 17 | +## Making the core more flexible |
| 18 | + |
| 19 | +The most transformative change this quarter was allowing users to choose the |
| 20 | +underlying container type for `dynamic_bitset`. Until now, the implementation |
| 21 | +assumed `std::vector`, which limited optimization opportunities and imposed |
| 22 | +certain behaviors. By lifting that restriction, developers can now use |
| 23 | +alternatives like `boost::container::small_vector`, enabling small buffer |
| 24 | +optimization and more control over memory layout. |
| 25 | + |
| 26 | +This change had ripple effects throughout the codebase. I had to revisit |
| 27 | +assumptions about contiguous storage, update operators like `<<=`, `>>=`, and |
| 28 | +ensure that reference stability and iterator behavior were correctly handled. |
| 29 | + |
| 30 | +## Introducing C++20 iterators |
| 31 | + |
| 32 | +One of the more exciting additions this quarter was support for C++20-style |
| 33 | +iterators. These new iterators conform to the standard iterator concepts, making |
| 34 | +`dynamic_bitset` more interoperable with modern algorithms and range-based |
| 35 | +utilities. |
| 36 | + |
| 37 | +I added assertions to ensure that both the underlying container and |
| 38 | +`dynamic_bitset` itself meet the requirements for bidirectional iteration. These |
| 39 | +checks are enabled only when compiling with C++20 or later, and they help catch |
| 40 | +subtle mismatches early—especially when users plug in custom containers. |
| 41 | + |
| 42 | +## Saying goodbye to legacy workarounds |
| 43 | + |
| 44 | +With modern compilers and standard libraries, many old workarounds are no longer |
| 45 | +needed. I removed the `max_size_workaround()` after confirming that major |
| 46 | +implementations now correctly account for allocators in `max_size()`. I also |
| 47 | +dropped support for obsolete compilers like MSVC 6 and CodeWarrior 8.3, and for |
| 48 | +pre-standard iostreams, cleaned up outdated macros, and removed compatibility |
| 49 | +layers for pre-C++11 environments. |
| 50 | + |
| 51 | +These removals weren't just cosmetic—they simplified the code and made it easier |
| 52 | +to reason about. In many places, I replaced legacy constructs with standard |
| 53 | +features like `noexcept` and `std::move()`. |
| 54 | + |
| 55 | +## constexpr support |
| 56 | + |
| 57 | +When it is compiled as C++20 or later, almost all functions in DynamicBitset are |
| 58 | +now `constexpr`. |
| 59 | + |
| 60 | +## Dropping obsolete dependencies |
| 61 | + |
| 62 | +As part of the cleanup effort, I also removed several outdated dependencies that |
| 63 | +were no longer justified. These included Boost.Integer (previously used by |
| 64 | +`lowest_bit()`), `core/allocator_access.hpp`, and various compatibility headers |
| 65 | +tied to pre-C++11 environments. This not only reduces compile-time overhead and |
| 66 | +cognitive load, but also makes the library easier to audit and maintain. |
| 67 | + |
| 68 | + |
| 69 | +## Strengthening the test suite |
| 70 | + |
| 71 | +A part of this quarter's work was expanding and refining the test coverage. I |
| 72 | +added new tests for `flip()`, `resize()`, `swap()`, and `operator!=()`. I also |
| 73 | +ensured that input iterators are properly supported in `append()`, and verified |
| 74 | +that `std::hash` behaves correctly even when two bitsets share the same |
| 75 | +underlying container but differ in size. |
| 76 | + |
| 77 | +Along the way, I cleaned up misleading comments, shortened overly complex |
| 78 | +conditions, and removed legacy test code that no longer reflected the current |
| 79 | +behavior of the library. The result is a test suite that's more robust, more |
| 80 | +meaningful, and easier to maintain. |
| 81 | + |
| 82 | +## Documentation that speaks clearly |
| 83 | + |
| 84 | +I've always believed that documentation should be treated as part of the design, |
| 85 | +not an afterthought. This quarter, I ported the existing documentation to MrDocs |
| 86 | +and Antora, while fixing and improving a few bits in the process. This uncovered |
| 87 | +a few MrDocs bugs, some of which remain—but I'm hopeful. |
| 88 | + |
| 89 | +I also spent time harmonizing the style and structure of the library's comments |
| 90 | +and docstrings. |
| 91 | + |
| 92 | +I chose to document iterator categories rather than exposing concrete types, |
| 93 | +which keeps the interface clean and focused on behavior rather than |
| 94 | +implementation details. |
| 95 | + |
| 96 | +## New member functions and smarter implementations |
| 97 | + |
| 98 | +This quarter also introduced several new member functions that expand the |
| 99 | +expressiveness and utility of `dynamic_bitset`: |
| 100 | + |
| 101 | +- `push_front()` and `pop_front()` allow bit-level manipulation at the front of |
| 102 | + the bitset, complementing the existing back-oriented operations. |
| 103 | +- `find_first_off()` and `find_next_off()` provide symmetric functionality to |
| 104 | + their `find_first()` counterparts, making it easier to locate unset bits. |
| 105 | +- A constructor from `basic_string_view` was added for C++17 and later, |
| 106 | + improving interoperability with modern string APIs. |
| 107 | + |
| 108 | +Alongside these additions, I revisited the implementation of several existing |
| 109 | +members to improve performance and clarity: |
| 110 | + |
| 111 | +- `push_back()` and `pop_back()` were streamlined for better efficiency. |
| 112 | +- `all()` and `lowest_bit()` were simplified and optimized, with the latter also |
| 113 | + shedding its dependency on Boost.Integer. |
| 114 | +- `append()` was fixed to properly support input iterators and avoid redundant |
| 115 | + checks. |
| 116 | + |
| 117 | +## Minor but impactful cleanups |
| 118 | + |
| 119 | +A large number of small edits improved correctness, readability, and |
| 120 | +maintainability: |
| 121 | + |
| 122 | +- Fixed the stream inserter to set `badbit` if an exception is thrown during |
| 123 | + output. |
| 124 | +- Changed the stream extractor to rethrow any exceptions coming from the |
| 125 | + underlying container. |
| 126 | +- Reordered and cleaned up all #include sections to use the "" form for Boost |
| 127 | + includes where appropriate and to keep include groups sorted. |
| 128 | +- Removed an example timing benchmark that was misleading and a number of |
| 129 | + unneeded comments and minor typos across code and docs. |
| 130 | + |
| 131 | +These edits reduce noise and make code reviews and maintenance more pleasant. |
| 132 | + |
| 133 | +## Reflections |
| 134 | + |
| 135 | +Looking back, this quarter reminded me of the value of revisiting assumptions. |
| 136 | +Many of the workarounds and constraints that once made sense are now obsolete. |
| 137 | +By embracing modern C++ features and simplifying where possible, we can make |
| 138 | +libraries like `dynamic_bitset` more powerful and more approachable. |
| 139 | + |
| 140 | +It also reinforced the importance of clarity—both in code and in documentation. |
| 141 | +Whether it's a test case, a comment, or a public API, precision and consistency |
| 142 | +go a long way. |
| 143 | + |
| 144 | +The work continues, but the foundation is stronger than ever. If you're using |
| 145 | +`dynamic_bitset` or thinking about integrating it into your project, I'd love to |
| 146 | +hear your feedback. |
0 commit comments