diff --git a/docs/bitset.adoc b/docs/bitset.adoc index 57c72a2..de28fe0 100644 --- a/docs/bitset.adoc +++ b/docs/bitset.adoc @@ -101,6 +101,13 @@ auto bs = stdx::bitset<8>{0b1010'1010ul}; for_each([&](auto i) { /* i == 1, 3, 5, 7 */ }, bs); ---- +To iterate all the unset bits, use: +[source,cpp] +---- +auto bs = stdx::bitset<8>{0b1010'1010ul}; +for_each([&](auto i) { /* i == 0, 2, 4, 6 */ }, bs); +---- + To support "external" iteration, or use cases like using a bitset to track used objects, `lowest_unset` is also provided: [source,cpp] diff --git a/include/stdx/bitset.hpp b/include/stdx/bitset.hpp index 36e1187..a48b7d9 100644 --- a/include/stdx/bitset.hpp +++ b/include/stdx/bitset.hpp @@ -21,6 +21,45 @@ namespace stdx { inline namespace v1 { +struct set_bit { + template + constexpr static auto fn(auto e, auto idx, auto &f) { + using elem_t = decltype(Bit); + while (e != 0) { + auto const offset = static_cast(countr_zero(e)); + e &= static_cast(~(Bit << offset)); + f(static_cast(idx + offset)); + } + } +}; +struct unset_bit { + template + constexpr static auto fn(auto e, auto idx, auto &f) { + using elem_t = decltype(Bit); + while (e != Mask) { + auto const offset = static_cast(countr_one(e)); + e |= static_cast(Bit << offset); + f(static_cast(idx + offset)); + } + } +}; +struct bit { + template + constexpr static auto fn(auto e, auto idx, auto &f) { + using elem_t = decltype(Bit); + for (auto i = std::size_t{}; i < popcount(Mask); ++i) { + bool b = e & static_cast(Bit << i); + f(static_cast(idx + i), b); + } + } +}; + +namespace detail { +template +concept bit_spec = std::same_as or std::same_as or + std::same_as; +} + template ())> class bitset { @@ -106,20 +145,20 @@ class bitset { return not std::is_enum_v or std::is_same_v; } - template constexpr auto for_each(F &&f) const -> F { - std::size_t i = 0; - for (auto e : storage) { - while (e != 0) { - auto const offset = static_cast(countr_zero(e)); - e &= static_cast(~(bit << offset)); - f(static_cast(i + offset)); - } - i += std::numeric_limits::digits; + template + constexpr auto for_each(F &&f) const -> F { + std::size_t idx = 0; + for (auto i = std::size_t{}; i < storage_size - 1; ++i) { + Spec::template fn::max()>(storage[i], + idx, f); + idx += std::numeric_limits::digits; } + Spec::template fn(highbits(), idx, f); return std::forward(f); } - template + template friend constexpr auto for_each(F &&f, bitset const &...bs) -> F; template @@ -457,10 +496,10 @@ class bitset { } }; -template +template constexpr auto for_each(F &&f, bitset const &...bs) -> F { if constexpr (sizeof...(bs) == 1) { - return (bs.for_each(std::forward(f)), ...); + return (bs.template for_each(std::forward(f)), ...); } else { static_assert(stdx::always_false_v, "unimplemented"); return f; @@ -648,9 +687,10 @@ template class type_bitset { return *this; } - template constexpr auto for_each(F &&f) const -> F { + template + constexpr auto for_each(F &&f) const -> F { constexpr auto callers = make_callers(std::make_index_sequence{}); - stdx::for_each([&](auto i) { callers[i](f); }, bs); + stdx::for_each([&](auto i) { callers[i](f); }, bs); return f; } }; diff --git a/test/bitset.cpp b/test/bitset.cpp index 97557ce..ebe13c2 100644 --- a/test/bitset.cpp +++ b/test/bitset.cpp @@ -350,6 +350,23 @@ TEMPLATE_TEST_CASE("for_each", "[bitset]", std::uint8_t, std::uint16_t, CHECK(result == bs); } +TEMPLATE_TEST_CASE("for_each (unset bits)", "[bitset]", std::uint8_t, + std::uint16_t, std::uint32_t, std::uint64_t) { + constexpr auto bs = stdx::bitset<64, TestType>{0x01020304'05060708ul}; + auto result = decltype(bs){}; + for_each([&](auto i) { result.set(i); }, bs); + CHECK(result.template to() == + ~bs.template to()); +} + +TEMPLATE_TEST_CASE("for_each (all bits)", "[bitset]", std::uint8_t, + std::uint16_t, std::uint32_t, std::uint64_t) { + constexpr auto bs = stdx::bitset<64, TestType>{0x01020304'05060708ul}; + auto result = decltype(bs){}; + for_each([&](auto i, bool b) { result.set(i, b); }, bs); + CHECK(result == bs); +} + TEMPLATE_TEST_CASE("for_each iterates in order lsb to msb", "[bitset]", std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) { constexpr auto bs = stdx::bitset<5, TestType>{0b10101ul}; diff --git a/test/type_bitset.cpp b/test/type_bitset.cpp index a899474..d20c76d 100644 --- a/test/type_bitset.cpp +++ b/test/type_bitset.cpp @@ -200,3 +200,12 @@ TEST_CASE("for_each", "[type_bitset]") { }); CHECK(result == "int0float1bool2"); } + +TEST_CASE("for_each (unset bits)", "[type_bitset]") { + constexpr auto bs = stdx::type_bitset{}; + auto result = std::string{}; + bs.for_each([&]() -> void { + result += std::string{stdx::type_as_string()} + std::to_string(I); + }); + CHECK(result == "int0float1bool2"); +}