Skip to content

Commit 31df61e

Browse files
committed
📚 Add docs for atomic_bitset
1 parent ef39adb commit 31df61e

File tree

5 files changed

+135
-3
lines changed

5 files changed

+135
-3
lines changed

docs/atomic_bitset.adoc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
2+
== `atomic_bitset.hpp`
3+
4+
https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/atomic_bitset.hpp[`atomic_bitset.hpp`]
5+
provides an implementation of a xref:bitset.adoc#_bitset_hpp[`bitset`] with atomic semantics.
6+
7+
An `atomic_bitset` is limited in size to the maximum integral type a platform
8+
can support while still using lock-free atomic instructions. Like `bitset`, it
9+
can be defined by selecting the underlying storage type automatically:
10+
[source,cpp]
11+
----
12+
using A = stdx::bitset<8>; // uses uint8_t
13+
using B = stdx::bitset<16>; // uses uint16_t
14+
using C = stdx::bitset<32>; // uses uint32_t
15+
using D = stdx::bitset<64>; // uses uint64_t
16+
----
17+
18+
`atomic_bitset` is constructed in the same way as `bitset`: with `allbits`,
19+
`place_bits`, a value, or a `string_view`:
20+
[source,cpp]
21+
----
22+
using namespace std::string_view_literals;
23+
auto bs1 = stdx::atomic_bitset<8>{stdx::all_bits}; // 0b1111'1111
24+
auto bs2 = stdx::atomic_bitset<8>{stdx::place_bits, 0, 1, 3}; // 0b1011
25+
auto bs3 = stdx::atomic_bitset<8>{"1100"sv}; // 0b1100
26+
----
27+
28+
NOTE: `atomic_bitset`'s constructors are `constexpr`, but none of the other
29+
functions are.
30+
31+
Also like `bitset`, `atomic_bitset` supports conversion to integral types:
32+
[source,cpp]
33+
----
34+
auto bs = stdx::atomic_bitset<11>{0b101}; // 11 bits, value 5
35+
auto i = bs.to<std::uint64_t>(); // 5 (a std::uint64_t)
36+
auto j = bs.to_natural(); // 5 (a std::uint16_t)
37+
----
38+
39+
And operation with enumeration types:
40+
[source,cpp]
41+
----
42+
enum struct Bits { ZERO, ONE, TWO, THREE, MAX };
43+
auto bs = stdx::atomic_bitset<Bits::MAX>{stdx::all_bits}; // 4 bits, value 0b1111
44+
bs.set(Bits::ZERO);
45+
bs.reset(Bits::ZERO);
46+
bs.flip(Bits::ZERO);
47+
auto bit_zero = bs[Bits::ZERO];
48+
----
49+
50+
Unlike `bitset`, `atomic_bitset`'s operations are atomic. For example, `load`
51+
and `store` are basic operations that return and take a corresponding `bitset`:
52+
53+
[source,cpp]
54+
----
55+
constexpr auto bs = stdx::atomic_bitset<8>{0b1010ul};
56+
auto copy = bs.load(); // a stdx::bitset<8>{0b1010ul};
57+
bs.store(copy);
58+
----
59+
60+
Like https://en.cppreference.com/w/cpp/atomic/atomic/load[`load`] and
61+
https://en.cppreference.com/w/cpp/atomic/atomic/store[`store`] on
62+
https://en.cppreference.com/w/cpp/atomic/atomic[`std::atomic`], the `load` and
63+
`store` operations on `stdx::atomic_bitset` take an optional
64+
https://en.cppreference.com/w/cpp/atomic/memory_order[`std::memory_order`].
65+
`stdx::atomic_bitset` is also implicitly convertible to a corresponding
66+
`stdx::bitset`; that operation is equivalent to `load()`.
67+
68+
The `set`, `reset` and `flip` operations also take an optional
69+
`std::memory_order`: these operations are equivalent to `store` in their
70+
semantics, except that they return the `stdx::bitset` that was the previous
71+
value.
72+
73+
[source,cpp]
74+
----
75+
constexpr auto bs = stdx::atomic_bitset<8>{0b1010ul};
76+
auto prev = bs.set(0);
77+
// bs == 1011
78+
// prev == 1010 (stdx::bitset<8>)
79+
----
80+
81+
NOTE: When `set` or `reset` are called without specifying bits, they return a
82+
reference to the `atomic_bitset`. This is because these operations result in a
83+
plain `store` which does not return the previous value.
84+
85+
`all`, `any`, `none` and `count` are also available on `atomic_bitset` and they
86+
are each equivalent to `load` followed by the respective operation. Like `load`,
87+
they also take an optional `std::memory_order`.
88+
89+
So what is _not_ available on `atomic_bitset`?
90+
91+
* any binary operation: equality, binary versions of `and`, `or`, etc.
92+
* bit shift operations
93+
* `for_each` and `lowest_unset`
94+
* unary `not`
95+
96+
These operations are not provided for varying reasons:
97+
98+
* atomic semantics are impossible or problematic to guarantee (binary operations)
99+
* atomic instructions are not available (bit shifts, `lowest_unset`)
100+
* atomic semantics are unclear (`for_each`)
101+
* the caller can easily achieve what they want (unary `not`)
102+
103+
In all of these cases though, the caller can make the right choice for them, and
104+
use the corresponding operations on `bitset` after correctly reasoning about the
105+
required semantics.

docs/bitset.adoc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ platform.
1414

1515
* Stream input and output operators are not implemented.
1616
* A `std::hash` specialization is not implemented.
17-
* `to_string`, `to_ulong` and `to_ullong` are not implemented
17+
* `to_string`, `to_ulong` and `to_ullong` are not implemented -- but `to` and
18+
`to_natural` provide ways to convert to integral types.
1819
* `operator[]` is read-only: it does not return a proxy reference type
1920

2021
A bitset has two template parameters: the size of the bitset and the storage
@@ -70,8 +71,8 @@ auto i = bs.to<std::uint64_t>(); // 5 (a std::uint64_t)
7071
auto j = bs.to_natural(); // 5 (a std::uint16_t)
7172
----
7273

73-
Bitsets support all the usual bitwise operators (`and`, `or`, `xor` and `not`)
74-
and also support `operator-` meaning set difference, or `a & ~b`.
74+
Bitsets support all the usual bitwise operators (`and`, `or`, `xor` and `not`,
75+
shifts) and also support `operator-` meaning set difference, or `a & ~b`.
7576

7677
A bitset can also be used with an enumeration that represents bits:
7778
[source,cpp]
@@ -86,3 +87,19 @@ auto bit_zero = bs[Bits::ZERO];
8687

8788
NOTE: The enumeration values are the bit positions, not the bits themselves (the
8889
enumeration values are not fixed to powers-of-2).
90+
91+
A bitset also supports efficient iteration with `for_each`, which calls a
92+
function with each set bit in turn, working from LSB to MSB:
93+
[source,cpp]
94+
----
95+
auto bs = stdx::bitset<8>{0b1010'1010ul};
96+
for_each([&](auto i) { /* i == 1, 3, 5, 7 */ }, bs);
97+
----
98+
99+
To support "external" iteration, or use cases like using a bitset to track used
100+
objects, `lowest_unset` is also provided:
101+
[source,cpp]
102+
----
103+
auto bs = stdx::bitset<8>{0b11'0111ul};
104+
auto i = bs.lowest_unset(); // i == 3
105+
----

docs/index.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
:toc: left
88

99
include::intro.adoc[]
10+
include::atomic_bitset.adoc[]
1011
include::algorithm.adoc[]
1112
include::bit.adoc[]
1213
include::bitset.adoc[]
@@ -33,6 +34,7 @@ include::numeric.adoc[]
3334
include::optional.adoc[]
3435
include::panic.adoc[]
3536
include::priority.adoc[]
37+
include::ranges.adoc[]
3638
include::span.adoc[]
3739
include::tuple.adoc[]
3840
include::tuple_algorithms.adoc[]

docs/intro.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ into headers whose names match the standard.
3535
The following headers are available:
3636

3737
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/algorithm.hpp[`algorithm.hpp`]
38+
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/atomic_bitset.hpp[`atomic_bitset.hpp`]
3839
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/bit.hpp[`bit.hpp`]
3940
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/bitset.hpp[`bitset.hpp`]
4041
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/byterator.hpp[`byterator.hpp`]
@@ -60,6 +61,7 @@ The following headers are available:
6061
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/optional.hpp[`optional.hpp`]
6162
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/panic.hpp[`panic.hpp`]
6263
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/priority.hpp[`priority.hpp`]
64+
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
6365
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/span.hpp[`span.hpp`]
6466
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple.hpp[`tuple.hpp`]
6567
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_algorithms.hpp[`tuple_algorithms.hpp`]

docs/ranges.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
== `ranges.hpp`
3+
4+
https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
5+
contains a single concept: `range`. A type models the `stdx::range` concept if
6+
`std::begin` and `std::end` are defined for that type.

0 commit comments

Comments
 (0)