Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:
DEFAULT_LLVM_VERSION: 18
DEFAULT_GCC_VERSION: 13
MULL_LLVM_VERSION: 17
HYPOTHESIS_PROFILE: ci
HYPOTHESIS_PROFILE: default

concurrency:
group: ${{ github.head_ref || github.run_id }}
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target_sources(
include
FILES
include/stdx/algorithm.hpp
include/stdx/atomic_bitset.hpp
include/stdx/bit.hpp
include/stdx/bitset.hpp
include/stdx/byterator.hpp
Expand All @@ -51,6 +52,7 @@ target_sources(
include/stdx/cx_queue.hpp
include/stdx/cx_set.hpp
include/stdx/cx_vector.hpp
include/stdx/detail/bitset_common.hpp
include/stdx/detail/list_common.hpp
include/stdx/for_each_n_args.hpp
include/stdx/functional.hpp
Expand Down
107 changes: 107 additions & 0 deletions docs/atomic_bitset.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

== `atomic_bitset.hpp`

https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/atomic_bitset.hpp[`atomic_bitset.hpp`]
provides an implementation of a xref:bitset.adoc#_bitset_hpp[`bitset`] with atomic semantics.

An `atomic_bitset` is limited in size to the maximum integral type a platform
can support while still using lock-free atomic instructions. Like `bitset`, it
can be defined by selecting the underlying storage type automatically:
[source,cpp]
----
using A = stdx::bitset<8>; // uses uint8_t
using B = stdx::bitset<16>; // uses uint16_t
using C = stdx::bitset<32>; // uses uint32_t
using D = stdx::bitset<64>; // uses uint64_t
----

`atomic_bitset` is constructed in the same way as `bitset`: with `all_bits`,
`place_bits`, a value, or a `string_view`:
[source,cpp]
----
using namespace std::string_view_literals;
auto bs0 = stdx::atomic_bitset<8>{};
auto bs1 = stdx::atomic_bitset<8>{stdx::all_bits}; // 0b1111'1111
auto bs2 = stdx::atomic_bitset<8>{stdx::place_bits, 0, 1, 3}; // 0b1011
auto bs3 = stdx::atomic_bitset<8>{0b1011};
auto bs4 = stdx::atomic_bitset<8>{"1011"sv};
----

NOTE: `atomic_bitset`​'s constructors are `constexpr`, but none of the other
functions are.

Also like `bitset`, `atomic_bitset` supports conversion to integral types:
[source,cpp]
----
auto bs = stdx::atomic_bitset<11>{0b101}; // 11 bits, value 5
auto i = bs.to<std::uint64_t>(); // 5 (a std::uint64_t)
auto j = bs.to_natural(); // 5 (a std::uint16_t)
----

And operation with enumeration types:
[source,cpp]
----
enum struct Bits { ZERO, ONE, TWO, THREE, MAX };
auto bs = stdx::atomic_bitset<Bits::MAX>{stdx::all_bits}; // 4 bits, value 0b1111
bs.set(Bits::ZERO);
bs.reset(Bits::ZERO);
bs.flip(Bits::ZERO);
auto bit_zero = bs[Bits::ZERO];
----

Unlike `bitset`, `atomic_bitset`​'s operations are atomic. For example, `load`
and `store` are basic operations that return and take a corresponding `bitset`:

[source,cpp]
----
constexpr auto bs = stdx::atomic_bitset<8>{0b1010ul};
auto copy = bs.load(); // a stdx::bitset<8>{0b1010ul};
bs.store(copy);
----

Like https://en.cppreference.com/w/cpp/atomic/atomic/load[`load`] and
https://en.cppreference.com/w/cpp/atomic/atomic/store[`store`] on
https://en.cppreference.com/w/cpp/atomic/atomic[`std::atomic`], the `load` and
`store` operations on `stdx::atomic_bitset` take an optional
https://en.cppreference.com/w/cpp/atomic/memory_order[`std::memory_order`].
`stdx::atomic_bitset` is also implicitly convertible to a corresponding
`stdx::bitset`; that operation is equivalent to `load()`.

The `set`, `reset` and `flip` operations also take an optional
`std::memory_order`: these operations are equivalent to `store` in their
semantics, except that they return the `stdx::bitset` that was the previous
value.

[source,cpp]
----
constexpr auto bs = stdx::atomic_bitset<8>{0b1010ul};
auto prev = bs.set(0);
// bs == 1011
// prev == 1010 (stdx::bitset<8>)
----

NOTE: When `set` or `reset` are called without specifying bits, they return a
reference to the `atomic_bitset`. This is because these operations result in a
plain `store` which does not return the previous value.

`all`, `any`, `none` and `count` are also available on `atomic_bitset` and they
are each equivalent to `load` followed by the respective operation. Like `load`,
they also take an optional `std::memory_order`.

So what is _not_ available on `atomic_bitset`?

* any binary operation: equality, binary versions of `and`, `or`, etc.
* bit shift operations
* `for_each` and `lowest_unset`
* unary `not`

These operations are not provided for varying reasons:

* atomic semantics are impossible or problematic to guarantee (binary operations)
* atomic instructions are not available (bit shifts, `lowest_unset`)
* atomic semantics are unclear (`for_each`)
* the caller can easily achieve what they want (unary `not`)

In all of these cases though, the caller can make the right choice for them, and
use the corresponding operations on `bitset` after correctly reasoning about the
required semantics.
23 changes: 20 additions & 3 deletions docs/bitset.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ platform.

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

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

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

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

NOTE: The enumeration values are the bit positions, not the bits themselves (the
enumeration values are not fixed to powers-of-2).

A bitset also supports efficient iteration with `for_each`, which calls a
function with each set bit in turn, working from LSB to MSB:
[source,cpp]
----
auto bs = stdx::bitset<8>{0b1010'1010ul};
for_each([&](auto i) { /* i == 1, 3, 5, 7 */ }, bs);
----

To support "external" iteration, or use cases like using a bitset to track used
objects, `lowest_unset` is also provided:
[source,cpp]
----
auto bs = stdx::bitset<8>{0b11'0111ul};
auto i = bs.lowest_unset(); // i == 3
----
2 changes: 2 additions & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:toc: left

include::intro.adoc[]
include::atomic_bitset.adoc[]
include::algorithm.adoc[]
include::bit.adoc[]
include::bitset.adoc[]
Expand All @@ -33,6 +34,7 @@ include::numeric.adoc[]
include::optional.adoc[]
include::panic.adoc[]
include::priority.adoc[]
include::ranges.adoc[]
include::span.adoc[]
include::tuple.adoc[]
include::tuple_algorithms.adoc[]
Expand Down
2 changes: 2 additions & 0 deletions docs/intro.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ into headers whose names match the standard.
The following headers are available:

* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/algorithm.hpp[`algorithm.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/atomic_bitset.hpp[`atomic_bitset.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/bit.hpp[`bit.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/bitset.hpp[`bitset.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/byterator.hpp[`byterator.hpp`]
Expand All @@ -60,6 +61,7 @@ The following headers are available:
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/optional.hpp[`optional.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/panic.hpp[`panic.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/priority.hpp[`priority.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/span.hpp[`span.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple.hpp[`tuple.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_algorithms.hpp[`tuple_algorithms.hpp`]
Expand Down
3 changes: 1 addition & 2 deletions docs/intrusive_forward_list.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
== `intrusive_forward_list.hpp`

`intrusive_forward_list` is a singly-linked list designed for use at compile-time or
with static objects. It supports pushing and popping at the front or back, and
removal from the middle.
with static objects. It supports pushing and popping at the front or back.

[source,cpp]
----
Expand Down
6 changes: 6 additions & 0 deletions docs/ranges.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

== `ranges.hpp`

https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
contains a single concept: `range`. A type models the `stdx::range` concept if
`std::begin` and `std::end` are defined for that type.
Loading