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
15 changes: 15 additions & 0 deletions docs/bitset.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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
* `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
element type to use. The storage element type must be unsigned.
Expand Down Expand Up @@ -71,3 +72,17 @@ 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`.

A bitset can also be used with an enumeration that represents bits:
[source,cpp]
----
enum struct Bits { ZERO, ONE, TWO, THREE, MAX };
auto bs = stdx::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];
----

NOTE: The enumeration values are the bit positions, not the bits themselves (the
enumeration values are not fixed to powers-of-2).
39 changes: 24 additions & 15 deletions include/stdx/bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,21 @@ template <std::size_t N, typename StorageElem> class bitset {
}

template <typename T> [[nodiscard]] constexpr auto to() const -> T {
static_assert(unsigned_integral<T>,
"Conversion must be to an unsigned integral type!");
static_assert(N <= std::numeric_limits<T>::digits,
using U = underlying_type_t<T>;
static_assert(
unsigned_integral<U>,
"Conversion must be to an unsigned integral type or enum!");
static_assert(N <= std::numeric_limits<U>::digits,
"Bitset too big for conversion to T");
if constexpr (std::is_same_v<StorageElem, T>) {
return storage[0] & lastmask;
if constexpr (std::is_same_v<StorageElem, U>) {
return static_cast<T>(storage[0] & lastmask);
} else {

T result{highbits()};
U result{highbits()};
for (auto i = storage_size - 2u; i < storage_size; --i) {
result = static_cast<T>(result << storage_elem_size);
result |= storage[i];
}
return result;
return static_cast<T>(result);
}
}

Expand All @@ -182,13 +183,16 @@ template <std::size_t N, typename StorageElem> class bitset {

constexpr static std::integral_constant<std::size_t, N> size{};

[[nodiscard]] constexpr auto operator[](std::size_t pos) const -> bool {
template <typename T>
[[nodiscard]] constexpr auto operator[](T idx) const -> bool {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
return (storage[index] & (bit << offset)) != 0;
}

constexpr auto set(std::size_t pos,
bool value = true) LIFETIMEBOUND -> bitset & {
template <typename T>
constexpr auto set(T idx, bool value = true) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
if (value) {
storage[index] |= static_cast<StorageElem>(bit << offset);
Expand Down Expand Up @@ -241,7 +245,9 @@ template <std::size_t N, typename StorageElem> class bitset {
return *this;
}

constexpr auto reset(std::size_t pos) LIFETIMEBOUND -> bitset & {
template <typename T>
constexpr auto reset(T idx) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
storage[index] &= static_cast<StorageElem>(~(bit << offset));
return *this;
Expand All @@ -262,7 +268,8 @@ template <std::size_t N, typename StorageElem> class bitset {
return set(lsb, len, false);
}

constexpr auto flip(std::size_t pos) LIFETIMEBOUND -> bitset & {
template <typename T> constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
storage[index] ^= static_cast<StorageElem>(bit << offset);
return *this;
Expand Down Expand Up @@ -406,8 +413,10 @@ constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
}
} // namespace detail

template <std::size_t N, typename StorageElem = void>
using bitset = detail::bitset<N, decltype(smallest_uint<N, StorageElem>())>;
template <auto N, typename StorageElem = void>
using bitset =
detail::bitset<to_underlying(N),
decltype(smallest_uint<to_underlying(N), StorageElem>())>;

} // namespace v1
} // namespace stdx
2 changes: 2 additions & 0 deletions include/stdx/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ template <typename E> constexpr auto to_underlying(E e) noexcept {
return e;
}
}
template <typename E>
using underlying_type_t = decltype(to_underlying(std::declval<E>()));

template <typename T> struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
Expand Down
32 changes: 32 additions & 0 deletions test/bitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,35 @@ TEMPLATE_TEST_CASE("find lowest unset bit (full)", "[bitset]", std::uint8_t,
constexpr auto bs = stdx::bitset<sz, TestType>{stdx::all_bits};
static_assert(bs.lowest_unset() == sz);
}

namespace {
enum struct Bits : std::uint8_t { ZERO, ONE, TWO, THREE, MAX };
}

TEST_CASE("use bitset with enum struct (construct)", "[bitset]") {
constexpr auto bs = stdx::bitset<Bits::MAX>{};
static_assert(bs.size() == stdx::to_underlying(Bits::MAX));
}

TEST_CASE("use bitset with enum struct (to)", "[bitset]") {
constexpr auto bs = stdx::bitset<Bits::MAX>{stdx::all_bits};
static_assert(bs.to<Bits>() == static_cast<Bits>(0b1111));
}

TEST_CASE("use bitset with enum struct (set/flip)", "[bitset]") {
auto bs = stdx::bitset<Bits::MAX>{};
bs.set(Bits::ZERO);
CHECK(bs.to_natural() == 1);
bs.reset(Bits::ZERO);
CHECK(bs.to_natural() == 0);
bs.flip(Bits::ZERO);
CHECK(bs.to_natural() == 1);
}

TEST_CASE("use bitset with enum struct (read index)", "[bitset]") {
constexpr auto bs = stdx::bitset<Bits::MAX>{stdx::all_bits};
static_assert(bs[Bits::ZERO]);
static_assert(bs[Bits::ONE]);
static_assert(bs[Bits::TWO]);
static_assert(bs[Bits::THREE]);
}