A C++23 modular bit array library with three container types: bitarray, bitvector, and bitspan. Built on a shared CRTP base that provides the full bitwise operation set.
- Three container types — fixed-size (
bitarray), dynamic (bitvector), and non-owning view (bitspan) - Full bitwise API — set/reset/flip, bitwise operators, shifts, rotations, population count, leading/trailing zero/one counting, bit width, floor/ceil, word/byte/bit swapping
- Formatting — binary, octal, hexadecimal, and decimal string output
- CRTP-based — no virtual dispatch; all operations are statically polymorphic
- C++20 modules — fast compilation, clean interface isolation
- Customizable word type —
uint8_t,uint16_t,uint32_t,uint64_t, or__uint128_t - Range-checked —
set(),flip(),get_word_at_pos()throwstd::out_of_range
- C++23 compiler (tested with Clang 23)
- CMake 3.28+
- Ninja build system (recommended)
import BitField;
int main() {
using Utils::BitField::bitarray;
bitarray<100> bits; // 100 bits, stack-allocated
bits.set(42); // set bit 42
bits.set(7, false); // clear bit 7
bits.flip(0); // toggle bit 0
bits.set(); // set all bits
bits.reset(); // clear all bits
auto count = bits.count(); // population count
auto any = bits.any(); // any bit set?
auto none = bits.none(); // no bits set?
auto all = bits.all(); // all bits set?
auto s = bits.formatted(); // binary: "0b..."
auto h = bits.formatted(Utils::BitField::Impl::BaseFormat::Hex); // hex: "0x..."
}Fixed-size bit array. Storage is a std::array<Word, N> — entirely stack-allocated. Size is known at compile time.
bitarray<64> a;
bitarray<100, uint64_t> b; // 100 bits using 64-bit words
a.set(0);
a.set(63);
auto c = a | (a << 1); // bitwise operations return new bitarrayDynamic-size bit vector. Storage is a std::vector<Word>. The NBits template parameter is advisory (used for trait selection); actual size is set at runtime.
bitvector<> v; // empty
bitvector<> v2{std::vector<uint32_t>{0xFFFFFFFF, 0x00000000}}; // from data
bitvector<> v3{100}; // allocate 100 bits
v.resize(200); // grow or shrink
v.set(50);
v.resize(50); // shrinkNon-owning view over an existing buffer of words. Storage is a std::span<Word, Extent>. Does not allocate or deallocate.
std::array<uint32_t, 4> buffer{};
bitspan<> view{buffer}; // view over all 128 bits
bitspan<> view2{buffer, 80}; // view over first 80 bits
view.set(7); // modifies buffer[0]
view.flip(63); // modifies buffer[1]
auto cpy = view; // shallow copy (same buffer)| Method | Description |
|---|---|
set(pos, val = true) |
Set or clear bit at pos |
set() |
Set all bits |
reset() |
Clear all bits |
flip(pos) |
Toggle bit at pos |
flip() |
Toggle all bits |
| Operator | Description |
|---|---|
a | b |
Bitwise OR (returns new container) |
a & b |
Bitwise AND |
a ^ b |
Bitwise XOR |
~a |
Bitwise NOT (flip all) |
a |= b |
In-place OR |
a &= b |
In-place AND |
a ^= b |
In-place XOR |
| Method / Operator | Description |
|---|---|
a << n |
Left shift (returns new container) |
a <<= n |
In-place left shift |
a >> n |
Right shift |
a >>= n |
In-place right shift |
rotl(n) |
In-place circular left shift |
rotr(n) |
In-place circular right shift |
rotated_left(n) |
Circular left shift (returns new) |
rotated_right(n) |
Circular right shift (returns new) |
| Method | Description |
|---|---|
size() |
Number of bits |
count() |
Population count (set bits) |
any() |
Any bit set? |
none() |
No bits set? |
all() |
All bits set? |
has_single_bit() |
Exactly one bit set? |
bit_width() |
Minimum bits needed to represent value |
countl_zero() |
Leading zero bits |
countl_one() |
Leading one bits |
countr_zero() |
Trailing zero bits |
countr_one() |
Trailing one bits |
| Method | Description |
|---|---|
get_word_at_pos(pos) |
Extract a word starting at bit pos |
set_word_at_pos(word, pos) |
Write a word starting at bit pos |
data() |
Direct access to underlying container |
| Method | Description |
|---|---|
wordswap() |
Reverse word order |
byteswap() |
Byte-swap each word |
bitswap() |
Bit-reverse each word |
a.formatted() // binary: "0b00010101..."
a.formatted(BaseFormat::Bin) // explicit binary
a.formatted(BaseFormat::Oct) // octal
a.formatted(BaseFormat::Hex) // hexadecimal
a.formatted(BaseFormat::Dec) // decimal (arbitrary precision)Format is from MSB to LSB with a prefix (0b, 0o, 0x).
a == b // equality
a != b // inequality
a <=> b // three-way (lexicographic)cmake -B build -G Ninja -DCMAKE_CXX_COMPILER=clang++
cmake --build buildbuild/test_bitarray # 83 tests
build/test_bitvector # 52 tests
build/test_bitspan # 30 testssrc/
bit_field_impl.cppm — partition module BitField:Internals
CRTP base class bitfield_impl (all operations)
bit_field.cppm — primary module BitField
bitarray, bitvector, bitspan, traits
tests/
test_bit_array.cpp — bitarray tests
test_bit_vector.cpp — bitvector tests
test_bitspan.cpp — bitspan tests
The primary module exports bitarray and bitvector. The partition contains the CRTP base with all shared implementation.
- All bitwise operations are range-checked — invalid positions throw
std::out_of_range - Padding bits (bits beyond the logical size in the last word) are kept zero by
sanitize() - The CRTP base uses private constructors with
frienddeclarations to prevent direct inheritance - The
formatted()hex output correctly handles digits A-F (unlike naivestd::to_string) - Rotations (
rotl/rotr) handle non-word-aligned sizes correctly