Browse Source

core: public popcount (waiting for C++20)

Michele Caini 1 year ago
parent
commit
fb47314d6f
3 changed files with 53 additions and 33 deletions
  1. 12 0
      src/entt/core/memory.hpp
  2. 2 7
      src/entt/entity/entity.hpp
  3. 39 26
      test/entt/core/memory.cpp

+ 12 - 0
src/entt/core/memory.hpp

@@ -11,6 +11,18 @@
 
 namespace entt {
 
+/**
+ * @brief Returns the number of set bits in a value (waiting for C++20 and
+ * `std::popcount`).
+ * @tparam Type Unsigned integer type.
+ * @param value A value of unsigned integer type.
+ * @return The number of set bits in the value.
+ */
+template<typename Type>
+constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(Type value) noexcept {
+    return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
+}
+
 /**
  * @brief Checks whether a value is a power of two or not (waiting for C++20 and
  * `std::has_single_bit`).

+ 2 - 7
src/entt/entity/entity.hpp

@@ -5,6 +5,7 @@
 #include <cstdint>
 #include <type_traits>
 #include "../config/config.h"
+#include "../core/memory.hpp"
 #include "fwd.hpp"
 
 namespace entt {
@@ -12,12 +13,6 @@ namespace entt {
 /*! @cond TURN_OFF_DOXYGEN */
 namespace internal {
 
-// waiting for C++20 and std::popcount
-template<typename Type>
-constexpr int popcount(Type value) noexcept {
-    return value ? (int(value & 1) + popcount(value >> 1)) : 0;
-}
-
 template<typename, typename = void>
 struct entt_traits;
 
@@ -64,7 +59,7 @@ struct entt_traits<std::uint64_t> {
  */
 template<typename Traits>
 class basic_entt_traits {
-    static constexpr auto length = internal::popcount(Traits::entity_mask);
+    static constexpr auto length = popcount(Traits::entity_mask);
 
     static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
     static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");

+ 39 - 26
test/entt/core/memory.cpp

@@ -15,32 +15,17 @@
 #include "../../common/throwing_type.hpp"
 #include "../../common/tracked_memory_resource.hpp"
 
-TEST(ToAddress, Functionalities) {
-    const std::shared_ptr<int> shared = std::make_shared<int>();
-    auto *plain = std::addressof(*shared);
-
-    ASSERT_EQ(entt::to_address(shared), plain);
-    ASSERT_EQ(entt::to_address(plain), plain);
-}
-
-TEST(PoccaPocmaAndPocs, Functionalities) {
-    test::basic_test_allocator<int> lhs, rhs;
-    test::basic_test_allocator<int, std::false_type> no_pocs;
-
-    // code coverage purposes
-    ASSERT_FALSE(lhs == rhs);
-    ASSERT_NO_THROW(entt::propagate_on_container_swap(no_pocs, no_pocs));
-
-    // honestly, I don't even know how one is supposed to test such a thing :)
-    entt::propagate_on_container_copy_assignment(lhs, rhs);
-    entt::propagate_on_container_move_assignment(lhs, rhs);
-    entt::propagate_on_container_swap(lhs, rhs);
-}
-
-ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) {
-    test::basic_test_allocator<int, std::false_type> lhs, rhs;
-
-    ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
+TEST(PopCount, Functionalities) {
+    // constexpr-ness guaranteed
+    constexpr auto zero_popcount = entt::popcount(0u);
+
+    ASSERT_EQ(zero_popcount, 0u);
+    ASSERT_EQ(entt::popcount(1u), 1u);
+    ASSERT_EQ(entt::popcount(2u), 1u);
+    ASSERT_EQ(entt::popcount(3u), 2u);
+    ASSERT_EQ(entt::popcount(7u), 3u);
+    ASSERT_EQ(entt::popcount(128u), 1u);
+    ASSERT_EQ(entt::popcount(201u), 4u);
 }
 
 TEST(IsPowerOfTwo, Functionalities) {
@@ -84,6 +69,34 @@ TEST(FastMod, Functionalities) {
     ASSERT_EQ(entt::fast_mod(8u, 8u), 0u);
 }
 
+TEST(ToAddress, Functionalities) {
+    const std::shared_ptr<int> shared = std::make_shared<int>();
+    auto *plain = std::addressof(*shared);
+
+    ASSERT_EQ(entt::to_address(shared), plain);
+    ASSERT_EQ(entt::to_address(plain), plain);
+}
+
+TEST(PoccaPocmaAndPocs, Functionalities) {
+    test::basic_test_allocator<int> lhs, rhs;
+    test::basic_test_allocator<int, std::false_type> no_pocs;
+
+    // code coverage purposes
+    ASSERT_FALSE(lhs == rhs);
+    ASSERT_NO_THROW(entt::propagate_on_container_swap(no_pocs, no_pocs));
+
+    // honestly, I don't even know how one is supposed to test such a thing :)
+    entt::propagate_on_container_copy_assignment(lhs, rhs);
+    entt::propagate_on_container_move_assignment(lhs, rhs);
+    entt::propagate_on_container_swap(lhs, rhs);
+}
+
+ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) {
+    test::basic_test_allocator<int, std::false_type> lhs, rhs;
+
+    ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
+}
+
 TEST(AllocateUnique, Functionalities) {
     test::throwing_allocator<test::throwing_type> allocator{};