diff --git a/docs/bitset.adoc b/docs/bitset.adoc index 0ed75e6..57c72a2 100644 --- a/docs/bitset.adoc +++ b/docs/bitset.adoc @@ -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 @@ -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. @@ -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{stdx::type_list{}}; +assert(tbs[stdx::type_identity_v]); +assert(not tbs[stdx::type_identity_v]); +assert(tbs[stdx::type_identity_v]); +---- + +The `set`, `reset` and `flip` functions are templates: + +[source,cpp] +---- +tbs.flip(); +assert(tbs[stdx::type_identity_v]); +---- diff --git a/include/stdx/bitset.hpp b/include/stdx/bitset.hpp index b897a8e..36e1187 100644 --- a/include/stdx/bitset.hpp +++ b/include/stdx/bitset.hpp @@ -217,7 +217,7 @@ class bitset { return to(); } - constexpr static std::integral_constant size{}; + constexpr static std::integral_constant capacity{}; template [[nodiscard]] constexpr auto operator[](T idx) const -> bool { @@ -313,6 +313,11 @@ class bitset { return set(lsb, len, false); } + template + constexpr auto clear(Ts... ts) LIFETIMEBOUND -> bitset & { + return reset(ts...); + } + template constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & { static_assert(admissible_enum() or stdx::always_false_v, @@ -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; @@ -478,6 +488,9 @@ template constexpr std::size_t index_of = 0; template constexpr std::size_t index_of> = boost::mp11::mp_find, T>::value; + +template +concept type_index_like = requires { typename T::type; }; } // namespace detail template class type_bitset { @@ -524,6 +537,8 @@ template class type_bitset { }...}; } + constexpr explicit(true) type_bitset(bitset const &b) : bs{b} {} + public: constexpr type_bitset() = default; constexpr explicit type_bitset(all_bits_t) : bs{all_bits} {} @@ -541,11 +556,11 @@ template class type_bitset { } [[nodiscard]] constexpr auto to_natural() const { return bs.to_natural(); } - constexpr static std::integral_constant size{}; + constexpr static std::integral_constant capacity{}; - template - [[nodiscard]] constexpr auto operator[](type_identity) const - -> decltype(auto) { + template + [[nodiscard]] constexpr auto operator[](TI) const -> decltype(auto) { + using T = typename TI::type; constexpr auto idx = detail::index_of; static_assert(idx < sizeof...(Ts), "Type not found in bitset"); return bs[idx]; @@ -575,6 +590,11 @@ template class type_bitset { return *this; } + template + constexpr auto clear() LIFETIMEBOUND -> type_bitset & { + return reset(); + } + template constexpr auto flip() LIFETIMEBOUND -> type_bitset & { static_assert((... and (detail::index_of> < N)), @@ -591,18 +611,23 @@ template 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()}; + return type_bitset{~bs}; } constexpr auto diff --git a/test/bitset.cpp b/test/bitset.cpp index 79d076c..97557ce 100644 --- a/test/bitset.cpp +++ b/test/bitset.cpp @@ -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, @@ -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}; @@ -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}; @@ -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{}; - 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]") { diff --git a/test/type_bitset.cpp b/test/type_bitset.cpp index 897c1a1..a899474 100644 --- a/test/type_bitset.cpp +++ b/test/type_bitset.cpp @@ -9,10 +9,10 @@ #include #include -TEST_CASE("bitset size", "[type_bitset]") { - STATIC_CHECK(stdx::type_bitset<>{}.size() == 0u); - STATIC_CHECK(stdx::type_bitset{}.size() == 1u); - STATIC_CHECK(stdx::type_bitset{}.size() == 2u); +TEST_CASE("bitset capacity", "[type_bitset]") { + STATIC_CHECK(stdx::type_bitset<>{}.capacity() == 0u); + STATIC_CHECK(stdx::type_bitset{}.capacity() == 1u); + STATIC_CHECK(stdx::type_bitset{}.capacity() == 2u); } TEST_CASE("index operation", "[type_bitset]") { @@ -35,6 +35,13 @@ TEST_CASE("reset single bit", "[type_bitset]") { CHECK(not bs[stdx::type_identity_v]); } +TEST_CASE("clear single bit", "[type_bitset]") { + auto bs = stdx::type_bitset{stdx::all_bits}; + CHECK(bs[stdx::type_identity_v]); + bs.clear(); + CHECK(not bs[stdx::type_identity_v]); +} + TEST_CASE("flip single bit", "[type_bitset]") { auto bs = stdx::type_bitset{stdx::all_bits}; CHECK(bs[stdx::type_identity_v]); @@ -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{}; + STATIC_CHECK(bs1.size() == 0u); + + constexpr auto bs2 = stdx::type_bitset{stdx::all_bits}; + STATIC_CHECK(bs2.size() == 3u); +} + +TEST_CASE("empty", "[type_bitset]") { + constexpr auto bs1 = stdx::type_bitset{}; + STATIC_CHECK(bs1.empty()); + + constexpr auto bs2 = stdx::type_bitset{stdx::all_bits}; + STATIC_CHECK(not bs2.empty()); +} + TEST_CASE("set all bits", "[type_bitset]") { auto bs = stdx::type_bitset{}; bs.set(); @@ -122,6 +145,14 @@ TEST_CASE("reset all bits", "[type_bitset]") { CHECK(bs == stdx::type_bitset{}); } +TEST_CASE("clear all bits", "[type_bitset]") { + auto bs = stdx::type_bitset{stdx::all_bits}; + bs.clear(); + CHECK(bs == stdx::type_bitset{}); + bs.clear(); + CHECK(bs == stdx::type_bitset{}); +} + TEST_CASE("flip all bits", "[type_bitset]") { auto bs = stdx::type_bitset{stdx::all_bits}; bs.flip();