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
26 changes: 26 additions & 0 deletions docs/bitset.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

== `bitset.hpp`

=== `bitset`

https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/bitset.hpp[`bitset.hpp`]
provides an implementation that mirrors
https://en.cppreference.com/w/cpp/utility/bitset[`std::bitset`], but is
Expand All @@ -17,6 +19,9 @@ platform.
* `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
* `size` does the same as `count()`: `capacity()` is the static size
* `empty` (size == 0) is provided
* `clear` does the same as `reset`

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 @@ -112,3 +117,24 @@ auto result = transform_reduce([](auto i) { return i * 2 },
std::plus{}, std::size_t{}, bs);
// result is 1*2 + 3*2 + 5*2 + 7*2
----

=== `type_bitset`

A `type_bitset` is much the same as a `bitset`, except that it is indexed by types.
It may also be constructed using a `type_list`.

[source,cpp]
----
auto tbs = stdx::type_bitset<int, float, bool>{stdx::type_list<int, bool>{}};
assert(tbs[stdx::type_identity_v<int>]);
assert(not tbs[stdx::type_identity_v<float>]);
assert(tbs[stdx::type_identity_v<bool>]);
----

The `set`, `reset` and `flip` functions are templates:

[source,cpp]
----
tbs.flip<float>();
assert(tbs[stdx::type_identity_v<float>]);
----
45 changes: 35 additions & 10 deletions include/stdx/bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class bitset {
return to<T>();
}

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

template <typename T>
[[nodiscard]] constexpr auto operator[](T idx) const -> bool {
Expand Down Expand Up @@ -313,6 +313,11 @@ class bitset {
return set(lsb, len, false);
}

template <typename... Ts>
constexpr auto clear(Ts... ts) LIFETIMEBOUND -> bitset & {
return reset(ts...);
}

template <typename T> constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & {
static_assert(admissible_enum<T>() or
stdx::always_false_v<T, decltype(Size)>,
Expand Down Expand Up @@ -341,15 +346,20 @@ class bitset {
return n;
}

[[nodiscard]] constexpr auto size() const -> std::size_t { return count(); }
[[nodiscard]] constexpr auto empty() const -> bool {
return size() == std::size_t{};
}

[[nodiscard]] constexpr auto all() const -> bool {
return count() == size();
return count() == capacity();
}

[[nodiscard]] constexpr auto any() const -> bool {
return count() != std::size_t{};
}

[[nodiscard]] constexpr auto none() const -> bool { return not any(); }
[[nodiscard]] constexpr auto none() const -> bool { return empty(); }

[[nodiscard]] constexpr auto lowest_unset() const {
std::size_t i = 0;
Expand Down Expand Up @@ -478,6 +488,9 @@ template <typename...> constexpr std::size_t index_of = 0;
template <typename T, typename... Us>
constexpr std::size_t index_of<T, type_list<Us...>> =
boost::mp11::mp_find<type_list<Us...>, T>::value;

template <typename T>
concept type_index_like = requires { typename T::type; };
} // namespace detail

template <typename... Ts> class type_bitset {
Expand Down Expand Up @@ -524,6 +537,8 @@ template <typename... Ts> class type_bitset {
}...};
}

constexpr explicit(true) type_bitset(bitset<N> const &b) : bs{b} {}

public:
constexpr type_bitset() = default;
constexpr explicit type_bitset(all_bits_t) : bs{all_bits} {}
Expand All @@ -541,11 +556,11 @@ template <typename... Ts> class type_bitset {
}
[[nodiscard]] constexpr auto to_natural() const { return bs.to_natural(); }

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

template <typename T>
[[nodiscard]] constexpr auto operator[](type_identity<T>) const
-> decltype(auto) {
template <detail::type_index_like TI>
[[nodiscard]] constexpr auto operator[](TI) const -> decltype(auto) {
using T = typename TI::type;
constexpr auto idx = detail::index_of<T, list_t>;
static_assert(idx < sizeof...(Ts), "Type not found in bitset");
return bs[idx];
Expand Down Expand Up @@ -575,6 +590,11 @@ template <typename... Ts> class type_bitset {
return *this;
}

template <typename... Us>
constexpr auto clear() LIFETIMEBOUND -> type_bitset & {
return reset<Us...>();
}

template <typename... Us>
constexpr auto flip() LIFETIMEBOUND -> type_bitset & {
static_assert((... and (detail::index_of<Us, type_list<Ts...>> < N)),
Expand All @@ -591,18 +611,23 @@ template <typename... Ts> class type_bitset {
return bs.count();
}

[[nodiscard]] constexpr auto size() const -> std::size_t { return count(); }
[[nodiscard]] constexpr auto empty() const -> bool {
return size() == std::size_t{};
}

[[nodiscard]] constexpr auto all() const -> bool {
return count() == size();
return count() == capacity();
}

[[nodiscard]] constexpr auto any() const -> bool {
return count() != std::size_t{};
}

[[nodiscard]] constexpr auto none() const -> bool { return not any(); }
[[nodiscard]] constexpr auto none() const -> bool { return empty(); }

[[nodiscard]] constexpr auto operator~() const -> type_bitset {
return type_bitset{~bs.template to<std::uint64_t>()};
return type_bitset{~bs};
}

constexpr auto
Expand Down
42 changes: 38 additions & 4 deletions test/bitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ TEST_CASE("bitset with implicit storage element type", "[bitset]") {
STATIC_REQUIRE(sizeof(stdx::bitset<64>) == 8);
}

TEMPLATE_TEST_CASE("bitset size", "[bitset]", std::uint8_t, std::uint16_t,
TEMPLATE_TEST_CASE("bitset capacity", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
STATIC_REQUIRE(stdx::bitset<1, TestType>{}.size() == 1);
STATIC_REQUIRE(stdx::bitset<8, TestType>{}.size() == 8);
STATIC_REQUIRE(stdx::bitset<1, TestType>{}.capacity() == 1);
STATIC_REQUIRE(stdx::bitset<8, TestType>{}.capacity() == 8);
}

TEMPLATE_TEST_CASE("index operation", "[bitset]", std::uint8_t, std::uint16_t,
Expand Down Expand Up @@ -73,6 +73,22 @@ TEMPLATE_TEST_CASE("reset all bits", "[bitset]", std::uint8_t, std::uint16_t,
CHECK(not bs[0]);
}

TEMPLATE_TEST_CASE("clear single bit", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
auto bs = stdx::bitset<1, TestType>{1ul};
CHECK(bs[0]);
bs.clear(0);
CHECK(not bs[0]);
}

TEMPLATE_TEST_CASE("clear all bits", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
auto bs = stdx::bitset<1, TestType>{1ul};
CHECK(bs[0]);
bs.clear();
CHECK(not bs[0]);
}

TEMPLATE_TEST_CASE("flip single bit", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
auto bs = stdx::bitset<3, TestType>{0b101ul};
Expand Down Expand Up @@ -200,6 +216,24 @@ TEMPLATE_TEST_CASE("count", "[bitset]", std::uint8_t, std::uint16_t,
STATIC_REQUIRE(bs2.count() == 3u);
}

TEMPLATE_TEST_CASE("size", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
constexpr auto bs1 = stdx::bitset<8, TestType>{};
STATIC_REQUIRE(bs1.size() == 0u);

constexpr auto bs2 = stdx::bitset<8, TestType>{0b10101ul};
STATIC_REQUIRE(bs2.size() == 3u);
}

TEMPLATE_TEST_CASE("empty", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
constexpr auto bs1 = stdx::bitset<8, TestType>{};
STATIC_REQUIRE(bs1.empty());

constexpr auto bs2 = stdx::bitset<8, TestType>{0b10101ul};
STATIC_REQUIRE(not bs2.empty());
}

TEMPLATE_TEST_CASE("or", "[bitset]", std::uint8_t, std::uint16_t, std::uint32_t,
std::uint64_t) {
constexpr auto bs1 = stdx::bitset<3, TestType>{0b101ul};
Expand Down Expand Up @@ -443,7 +477,7 @@ enum struct Bits : std::uint8_t { ZERO, ONE, TWO, THREE, MAX };

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

TEST_CASE("use bitset with enum struct (to)", "[bitset]") {
Expand Down
39 changes: 35 additions & 4 deletions test/type_bitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
#include <string>
#include <type_traits>

TEST_CASE("bitset size", "[type_bitset]") {
STATIC_CHECK(stdx::type_bitset<>{}.size() == 0u);
STATIC_CHECK(stdx::type_bitset<int>{}.size() == 1u);
STATIC_CHECK(stdx::type_bitset<int, float>{}.size() == 2u);
TEST_CASE("bitset capacity", "[type_bitset]") {
STATIC_CHECK(stdx::type_bitset<>{}.capacity() == 0u);
STATIC_CHECK(stdx::type_bitset<int>{}.capacity() == 1u);
STATIC_CHECK(stdx::type_bitset<int, float>{}.capacity() == 2u);
}

TEST_CASE("index operation", "[type_bitset]") {
Expand All @@ -35,6 +35,13 @@ TEST_CASE("reset single bit", "[type_bitset]") {
CHECK(not bs[stdx::type_identity_v<int>]);
}

TEST_CASE("clear single bit", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
CHECK(bs[stdx::type_identity_v<int>]);
bs.clear<int>();
CHECK(not bs[stdx::type_identity_v<int>]);
}

TEST_CASE("flip single bit", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
CHECK(bs[stdx::type_identity_v<int>]);
Expand Down Expand Up @@ -106,6 +113,22 @@ TEST_CASE("count", "[type_bitset]") {
STATIC_CHECK(bs2.count() == 3u);
}

TEST_CASE("size", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(bs1.size() == 0u);

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(bs2.size() == 3u);
}

TEST_CASE("empty", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(bs1.empty());

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(not bs2.empty());
}

TEST_CASE("set all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{};
bs.set();
Expand All @@ -122,6 +145,14 @@ TEST_CASE("reset all bits", "[type_bitset]") {
CHECK(bs == stdx::type_bitset<int, float, bool>{});
}

TEST_CASE("clear all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
bs.clear();
CHECK(bs == stdx::type_bitset<int, float, bool>{});
bs.clear();
CHECK(bs == stdx::type_bitset<int, float, bool>{});
}

TEST_CASE("flip all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
bs.flip();
Expand Down