From 9a38ac08d55f859dc46a293d16c5aef2be7beccd Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Aug 2026 11:29:37 +0200 Subject: [PATCH 1/6] Refactor `sparse_hash` to improve alignment with standard layout requirements. --- include/dice/sparse-map/sparse_hash.hpp | 157 ++++++++++++++---------- tests/sparse_map_tests.cpp | 8 ++ tests/sparse_set_tests.cpp | 6 + 3 files changed, 104 insertions(+), 67 deletions(-) diff --git a/include/dice/sparse-map/sparse_hash.hpp b/include/dice/sparse-map/sparse_hash.hpp index 72b51466..94251197 100644 --- a/include/dice/sparse-map/sparse_hash.hpp +++ b/include/dice/sparse-map/sparse_hash.hpp @@ -55,6 +55,18 @@ #define tsl_sh_assert(expr) (static_cast(0)) #endif +/** + * Marks a member as potentially overlapping, so an empty member costs no space. + * MSVC only honours its own spelling of the attribute. + */ +#if defined(_MSC_VER) && !defined(__clang__) +#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address) +#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS +#endif + namespace dice::sparse_map { namespace sh { @@ -1110,15 +1122,21 @@ namespace dice::sparse_map { * `sparse_array`, use respectively the methods * `sparse_array::sparse_ibucket(ibucket)` and * `sparse_array::index_in_sparse_bucket(ibucket)`. + * + * The hasher, the key equality, the allocator and the growth policy are held + * as members, not as base classes. A standard layout class may declare its + * non-static data members in one class of the hierarchy only, and a growth + * policy usually has state. Keeping everything in `sparse_hash` therefore + * makes `sparse_hash`, and with it `sparse_map` and `sparse_set`, a standard + * layout type whenever `Hash`, `KeyEqual`, `Allocator`, `GrowthPolicy` and the + * bucket container are standard layout themselves. The members are marked + * potentially overlapping, so empty ones still cost no space. */ template - class sparse_hash : private Allocator, - private Hash, - private KeyEqual, - private GrowthPolicy { + class sparse_hash { private: template using has_mapped_type = @@ -1288,10 +1306,10 @@ namespace dice::sparse_map { public: sparse_hash(size_type bucket_count, Hash const &hash, KeyEqual const &equal, Allocator const &alloc, float max_load_factor) - : Allocator(alloc), - Hash(hash), - KeyEqual(equal), - GrowthPolicy(bucket_count), + : m_alloc(alloc), + m_hash(hash), + m_key_equal(equal), + m_growth_policy(bucket_count), m_sparse_buckets_data(alloc), // m_sparse_buckets_data(std::allocator_traits::rebind_alloc(alloc)), m_sparse_buckets(static_empty_sparse_bucket_ptr()), @@ -1335,14 +1353,14 @@ namespace dice::sparse_map { } sparse_hash(sparse_hash const &other) - : Allocator(std::allocator_traits< - Allocator>::select_on_container_copy_construction(other)), - Hash(other), - KeyEqual(other), - GrowthPolicy(other), + : m_alloc(std::allocator_traits< + Allocator>::select_on_container_copy_construction(other.m_alloc)), + m_hash(other.m_hash), + m_key_equal(other.m_key_equal), + m_growth_policy(other.m_growth_policy), m_sparse_buckets_data( std::allocator_traits< - Allocator>::select_on_container_copy_construction(other)), + Allocator>::select_on_container_copy_construction(other.m_alloc)), m_bucket_count(other.m_bucket_count), m_nb_elements(other.m_nb_elements), m_nb_deleted_buckets(other.m_nb_deleted_buckets), @@ -1357,10 +1375,10 @@ namespace dice::sparse_map { sparse_hash(sparse_hash &&other) noexcept( std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value) - : Allocator(std::move(other)), - Hash(std::move(other)), - KeyEqual(std::move(other)), - GrowthPolicy(std::move(other)), + : m_alloc(std::move(other.m_alloc)), + m_hash(std::move(other.m_hash)), + m_key_equal(std::move(other.m_key_equal)), + m_growth_policy(std::move(other.m_growth_policy)), m_sparse_buckets_data(std::move(other.m_sparse_buckets_data)), m_sparse_buckets(m_sparse_buckets_data.empty() ? static_empty_sparse_bucket_ptr() @@ -1371,7 +1389,7 @@ namespace dice::sparse_map { m_load_threshold_rehash(other.m_load_threshold_rehash), m_load_threshold_clear_deleted(other.m_load_threshold_clear_deleted), m_max_load_factor(other.m_max_load_factor) { - other.GrowthPolicy::clear(); + other.m_growth_policy.clear(); other.m_sparse_buckets_data.clear(); other.m_sparse_buckets = static_empty_sparse_bucket_ptr(); other.m_bucket_count = 0; @@ -1387,22 +1405,22 @@ namespace dice::sparse_map { if constexpr (std::allocator_traits< Allocator>::propagate_on_container_copy_assignment::value) { - Allocator::operator=(other); + m_alloc = other.m_alloc; } - Hash::operator=(other); - KeyEqual::operator=(other); - GrowthPolicy::operator=(other); + m_hash = other.m_hash; + m_key_equal = other.m_key_equal; + m_growth_policy = other.m_growth_policy; if constexpr (std::allocator_traits< Allocator>::propagate_on_container_copy_assignment::value) { m_sparse_buckets_data = - sparse_buckets_container(static_cast(other)); + sparse_buckets_container(other.m_alloc); } else { if (m_sparse_buckets_data.size() != other.m_sparse_buckets_data.size()) { m_sparse_buckets_data = - sparse_buckets_container(static_cast(*this)); + sparse_buckets_container(m_alloc); } else { m_sparse_buckets_data.clear(); } @@ -1429,10 +1447,10 @@ namespace dice::sparse_map { if (not std::allocator_traits< Allocator>::propagate_on_container_move_assignment::value and - (static_cast(*this) != static_cast(other))) { + (m_alloc != other.m_alloc)) { move_buckets_from(std::move(other)); } else { - static_cast(*this) = std::move(static_cast(other)); + m_alloc = std::move(other.m_alloc); m_sparse_buckets_data = std::move(other.m_sparse_buckets_data); } @@ -1440,10 +1458,9 @@ namespace dice::sparse_map { ? static_empty_sparse_bucket_ptr() : m_sparse_buckets_data.data(); - static_cast(*this) = std::move(static_cast(other)); - static_cast(*this) = std::move(static_cast(other)); - static_cast(*this) = - std::move(static_cast(other)); + m_hash = std::move(other.m_hash); + m_key_equal = std::move(other.m_key_equal); + m_growth_policy = std::move(other.m_growth_policy); m_bucket_count = other.m_bucket_count; m_nb_elements = other.m_nb_elements; m_nb_deleted_buckets = other.m_nb_deleted_buckets; @@ -1451,7 +1468,7 @@ namespace dice::sparse_map { m_load_threshold_clear_deleted = other.m_load_threshold_clear_deleted; m_max_load_factor = other.m_max_load_factor; - other.GrowthPolicy::clear(); + other.m_growth_policy.clear(); other.m_sparse_buckets_data.clear(); other.m_sparse_buckets = static_empty_sparse_bucket_ptr(); other.m_bucket_count = 0; @@ -1464,7 +1481,7 @@ namespace dice::sparse_map { } allocator_type get_allocator() const { - return static_cast(*this); + return m_alloc; } /* @@ -1532,7 +1549,7 @@ namespace dice::sparse_map { */ void clear() noexcept { for (auto &bucket : m_sparse_buckets_data) { - bucket.clear(*this); + bucket.clear(m_alloc); } m_nb_elements = 0; @@ -1630,7 +1647,7 @@ namespace dice::sparse_map { tsl_sh_assert(pos != end() && m_nb_elements > 0); // vector iterator with fancy pointers have a problem with -> auto it_sparse_array_next = - (*pos.m_sparse_buckets_it).erase(*this, pos.m_sparse_array_it); + (*pos.m_sparse_buckets_it).erase(m_alloc, pos.m_sparse_array_it); m_nb_elements--; m_nb_deleted_buckets++; @@ -1686,16 +1703,14 @@ namespace dice::sparse_map { using std::swap; if constexpr (std::allocator_traits::propagate_on_container_swap::value) { - swap(static_cast(*this), static_cast(other)); + swap(m_alloc, other.m_alloc); } else { - tsl_sh_assert(static_cast(*this) == - static_cast(other)); + tsl_sh_assert(m_alloc == other.m_alloc); } - swap(static_cast(*this), static_cast(other)); - swap(static_cast(*this), static_cast(other)); - swap(static_cast(*this), - static_cast(other)); + swap(m_hash, other.m_hash); + swap(m_key_equal, other.m_key_equal); + swap(m_growth_policy, other.m_growth_policy); swap(m_sparse_buckets_data, other.m_sparse_buckets_data); swap(m_sparse_buckets, other.m_sparse_buckets); swap(m_bucket_count, other.m_bucket_count); @@ -1870,11 +1885,11 @@ namespace dice::sparse_map { * Observers */ hasher hash_function() const { - return static_cast(*this); + return m_hash; } key_equal key_eq() const { - return static_cast(*this); + return m_key_equal; } /* @@ -1902,16 +1917,16 @@ namespace dice::sparse_map { private: template std::size_t hash_key(K const &key) const { - return Hash::operator()(key); + return m_hash(key); } template bool compare_keys(const K1 &key1, const K2 &key2) const { - return KeyEqual::operator()(key1, key2); + return m_key_equal(key1, key2); } size_type bucket_for_hash(std::size_t hash) const { - std::size_t const bucket = GrowthPolicy::bucket_for_hash(hash); + std::size_t const bucket = m_growth_policy.bucket_for_hash(hash); tsl_sh_assert(sparse_array::sparse_ibucket(bucket) < m_sparse_buckets_data.size() || (bucket == 0 && m_sparse_buckets_data.empty())); @@ -1924,11 +1939,12 @@ namespace dice::sparse_map { nullptr> size_type next_bucket(size_type ibucket, size_type iprobe) const { (void) iprobe; + // bucket_for_hash is a mask operation for a power of two policy if constexpr (Probing == dice::sparse_map::sh::probing::linear) { - return (ibucket + 1) & this->m_mask; + return m_growth_policy.bucket_for_hash(ibucket + 1); } else { tsl_sh_assert(Probing == dice::sparse_map::sh::probing::quadratic); - return (ibucket + iprobe) & this->m_mask; + return m_growth_policy.bucket_for_hash(ibucket + iprobe); } } @@ -1953,8 +1969,7 @@ namespace dice::sparse_map { try { for (auto const &bucket : other.m_sparse_buckets_data) { - m_sparse_buckets_data.emplace_back(bucket, - static_cast(*this)); + m_sparse_buckets_data.emplace_back(bucket, m_alloc); } } catch (...) { clear(); @@ -1970,8 +1985,7 @@ namespace dice::sparse_map { try { for (auto &&bucket : other.m_sparse_buckets_data) { - m_sparse_buckets_data.emplace_back(std::move(bucket), - static_cast(*this)); + m_sparse_buckets_data.emplace_back(std::move(bucket), m_alloc); } } catch (...) { clear(); @@ -2002,7 +2016,7 @@ namespace dice::sparse_map { auto confirmed_insert = [&](std::size_t sparse_ibucket, typename sparse_array::size_type index_in_sparse_bucket) { if (size() >= m_load_threshold_rehash) { - rehash_impl(GrowthPolicy::next_bucket_count()); + rehash_impl(m_growth_policy.next_bucket_count()); return insert_impl(key, std::forward(value_type_args)...); } else if (size() + m_nb_deleted_buckets >= m_load_threshold_clear_deleted) { @@ -2080,7 +2094,7 @@ namespace dice::sparse_map { Args &&...value_type_args) { // is not called when empty auto value_it = m_sparse_buckets[sparse_ibucket].set( - *this, index_in_sparse_bucket, std::forward(value_type_args)...); + m_alloc, index_in_sparse_bucket, std::forward(value_type_args)...); m_nb_elements++; return std::make_pair( @@ -2105,7 +2119,7 @@ namespace dice::sparse_map { auto value_it = m_sparse_buckets[sparse_ibucket].value(index_in_sparse_bucket); if (compare_keys(key, KeySelect()(*value_it))) { - m_sparse_buckets[sparse_ibucket].erase(*this, value_it, + m_sparse_buckets[sparse_ibucket].erase(m_alloc, value_it, index_in_sparse_bucket); m_nb_elements--; m_nb_deleted_buckets++; @@ -2171,9 +2185,8 @@ namespace dice::sparse_map { typename std::enable_if::type * = nullptr> void rehash_impl(size_type count) { - sparse_hash new_table(count, static_cast(*this), - static_cast(*this), - static_cast(*this), m_max_load_factor); + sparse_hash new_table(count, m_hash, m_key_equal, m_alloc, + m_max_load_factor); for (auto &bucket : m_sparse_buckets_data) { for (auto &val : bucket) { @@ -2181,7 +2194,7 @@ namespace dice::sparse_map { } // TODO try to reuse some of the memory - bucket.clear(*this); + bucket.clear(m_alloc); } new_table.swap(*this); @@ -2196,9 +2209,8 @@ namespace dice::sparse_map { typename std::enable_if< U == dice::sparse_map::sh::exception_safety::strong>::type * = nullptr> void rehash_impl(size_type count) { - sparse_hash new_table(count, static_cast(*this), - static_cast(*this), - static_cast(*this), m_max_load_factor); + sparse_hash new_table(count, m_hash, m_key_equal, m_alloc, + m_max_load_factor); for (auto const &bucket : m_sparse_buckets_data) { for (auto const &val : bucket) { @@ -2221,7 +2233,7 @@ namespace dice::sparse_map { sparse_array::index_in_sparse_bucket(ibucket); if (!m_sparse_buckets[sparse_ibucket].has_value(index_in_sparse_bucket)) { - m_sparse_buckets[sparse_ibucket].set(*this, index_in_sparse_bucket, + m_sparse_buckets[sparse_ibucket].set(m_alloc, index_in_sparse_bucket, std::forward(key_value)); m_nb_elements++; @@ -2299,7 +2311,7 @@ namespace dice::sparse_map { m_bucket_count = numeric_cast( bucket_count_ds, "Deserialized bucket_count is too big."); - GrowthPolicy::operator=(GrowthPolicy(m_bucket_count)); + m_growth_policy = GrowthPolicy(m_bucket_count); // GrowthPolicy should not modify the bucket count we got from // deserialization if (m_bucket_count != bucket_count_ds) { @@ -2322,8 +2334,8 @@ namespace dice::sparse_map { nb_sparse_buckets, "Deserialized nb_sparse_buckets is too big.")); for (slz_size_type ibucket = 0; ibucket < nb_sparse_buckets; ibucket++) { m_sparse_buckets_data.emplace_back( - sparse_array::deserialize_hash_compatible( - deserializer, static_cast(*this))); + sparse_array::deserialize_hash_compatible(deserializer, + m_alloc)); } if (!m_sparse_buckets_data.empty()) { @@ -2359,6 +2371,17 @@ namespace dice::sparse_map { } private: + DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS Allocator m_alloc; + DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS Hash m_hash; + DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS KeyEqual m_key_equal; + + /** + * Declared before m_bucket_count. Its constructor takes the bucket count by + * reference and rounds it up to the count the policy can serve, and + * m_bucket_count is initialized from that rounded value. + */ + DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS GrowthPolicy m_growth_policy; + sparse_buckets_container m_sparse_buckets_data; diff --git a/tests/sparse_map_tests.cpp b/tests/sparse_map_tests.cpp index 55b87833..a87d0e82 100644 --- a/tests/sparse_map_tests.cpp +++ b/tests/sparse_map_tests.cpp @@ -92,6 +92,14 @@ using test_types = boost::mpl::list< dice::sparse_map::sh::power_of_two_growth_policy<2>, dice::sparse_map::sh::exception_safety::basic, dice::sparse_map::sh::sparsity::low>>; +/** + * layout + */ +BOOST_AUTO_TEST_CASE_TEMPLATE(test_standard_layout, HMap, test_types) { + static_assert(std::is_standard_layout_v); + BOOST_CHECK(std::is_standard_layout_v); +} + /** * insert */ diff --git a/tests/sparse_set_tests.cpp b/tests/sparse_set_tests.cpp index f509c92a..103aa659 100644 --- a/tests/sparse_set_tests.cpp +++ b/tests/sparse_set_tests.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "utils.h" @@ -57,6 +58,11 @@ using test_types = std::allocator, dice::sparse_map::sh::mod_growth_policy<>>>; +BOOST_AUTO_TEST_CASE_TEMPLATE(test_standard_layout, HSet, test_types) { + static_assert(std::is_standard_layout_v); + BOOST_CHECK(std::is_standard_layout_v); +} + BOOST_AUTO_TEST_CASE_TEMPLATE(test_insert, HSet, test_types) { // insert x values, insert them again, check values using key_t = typename HSet::key_type; From 55a104a450b68365cb92de3315dd2da6fdc71585 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+liss-h@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:44:55 +0200 Subject: [PATCH 2/6] make use of c++20 features, remove msvc workarounds --- cmake/boilerplate_init.cmake | 1 + doxygen.conf | 1 - include/dice/sparse-map/sparse_hash.hpp | 215 ++---------------------- include/dice/sparse-map/sparse_map.hpp | 93 ++++------ include/dice/sparse-map/sparse_set.hpp | 73 +++----- tests/CMakeLists.txt | 1 - tests/popcount_tests.cpp | 106 ------------ 7 files changed, 75 insertions(+), 415 deletions(-) delete mode 100644 tests/popcount_tests.cpp diff --git a/cmake/boilerplate_init.cmake b/cmake/boilerplate_init.cmake index 9807ef98..1530fc89 100644 --- a/cmake/boilerplate_init.cmake +++ b/cmake/boilerplate_init.cmake @@ -1,5 +1,6 @@ macro(boilerplate_init) ## enforce standard compliance + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/doxygen.conf b/doxygen.conf index 096b2f58..99649004 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -893,7 +893,6 @@ EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = \ tsl::detail_popcount::* \ tsl::detail_sparse_hash::has_is_transparent* \ -tsl::detail_sparse_hash::make_void* \ tsl::detail_sparse_hash::is_power_of_two_policy* \ tsl::detail_sparse_hash::sparse_array* diff --git a/include/dice/sparse-map/sparse_hash.hpp b/include/dice/sparse-map/sparse_hash.hpp index 94251197..239ac8bd 100644 --- a/include/dice/sparse-map/sparse_hash.hpp +++ b/include/dice/sparse-map/sparse_hash.hpp @@ -41,32 +41,12 @@ #include "dice/sparse-map/sparse_growth_policy.hpp" -#ifdef __INTEL_COMPILER -#include // For _popcnt32 and _popcnt64 -#endif - -#ifdef _MSC_VER -#include // For __cpuid, __popcnt and __popcnt64 -#endif - #ifdef TSL_DEBUG #define tsl_sh_assert(expr) assert(expr) #else #define tsl_sh_assert(expr) (static_cast(0)) #endif -/** - * Marks a member as potentially overlapping, so an empty member costs no space. - * MSVC only honours its own spelling of the attribute. - */ -#if defined(_MSC_VER) && !defined(__clang__) -#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -#elif defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address) -#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else -#define DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS -#endif - namespace dice::sparse_map { namespace sh { @@ -87,122 +67,6 @@ namespace dice::sparse_map { }; } // namespace sh - namespace detail_popcount { - /** - * Define the popcount(ll) methods and pick-up the best depending on the - * compiler. - */ - - // From Wikipedia: https://en.wikipedia.org/wiki/Hamming_weight - inline int fallback_popcountll(unsigned long long int x) { - static_assert( - sizeof(unsigned long long int) == sizeof(std::uint64_t), - "sizeof(unsigned long long int) must be equal to sizeof(std::uint64_t). " - "Open a feature request if you need support for a platform where it " - "isn't the case."); - - std::uint64_t const m1 = 0x5555555555555555ull; - std::uint64_t const m2 = 0x3333333333333333ull; - std::uint64_t const m4 = 0x0f0f0f0f0f0f0f0full; - std::uint64_t const h01 = 0x0101010101010101ull; - - x -= (x >> 1ull) & m1; - x = (x & m2) + ((x >> 2ull) & m2); - x = (x + (x >> 4ull)) & m4; - return static_cast((x * h01) >> (64ull - 8ull)); - } - - inline int fallback_popcount(unsigned int x) { - static_assert(sizeof(unsigned int) == sizeof(std::uint32_t) || - sizeof(unsigned int) == sizeof(std::uint64_t), - "sizeof(unsigned int) must be equal to sizeof(std::uint32_t) " - "or sizeof(std::uint64_t). " - "Open a feature request if you need support for a platform " - "where it isn't the case."); - - if (sizeof(unsigned int) == sizeof(std::uint32_t)) { - std::uint32_t const m1 = 0x55555555; - std::uint32_t const m2 = 0x33333333; - std::uint32_t const m4 = 0x0f0f0f0f; - std::uint32_t const h01 = 0x01010101; - - x -= (x >> 1) & m1; - x = (x & m2) + ((x >> 2) & m2); - x = (x + (x >> 4)) & m4; - return static_cast((x * h01) >> (32 - 8)); - } else { - return fallback_popcountll(x); - } - } - -#if defined(__clang__) || defined(__GNUC__) - inline int popcountll(unsigned long long int value) { - return __builtin_popcountll(value); - } - - inline int popcount(unsigned int value) { - return __builtin_popcount(value); - } - -#elif defined(_MSC_VER) - /** - * We need to check for popcount support at runtime on Windows with __cpuid - * See https://msdn.microsoft.com/en-us/library/bb385231.aspx - */ - inline bool has_popcount_support() { - int cpu_infos[4]; - __cpuid(cpu_infos, 1); - return (cpu_infos[2] & (1 << 23)) != 0; - } - - inline int popcountll(unsigned long long int value) { -#ifdef _WIN64 - static_assert( - sizeof(unsigned long long int) == sizeof(std::int64_t), - "sizeof(unsigned long long int) must be equal to sizeof(std::int64_t). "); - - static bool const has_popcount = has_popcount_support(); - return has_popcount - ? static_cast(__popcnt64(static_cast(value))) - : fallback_popcountll(value); -#else - return fallback_popcountll(value); -#endif - } - - inline int popcount(unsigned int value) { - static_assert(sizeof(unsigned int) == sizeof(std::int32_t), - "sizeof(unsigned int) must be equal to sizeof(std::int32_t). "); - - static bool const has_popcount = has_popcount_support(); - return has_popcount - ? static_cast(__popcnt(static_cast(value))) - : fallback_popcount(value); - } - -#elif defined(__INTEL_COMPILER) - inline int popcountll(unsigned long long int value) { - static_assert(sizeof(unsigned long long int) == sizeof(__int64), ""); - return _popcnt64(static_cast<__int64>(value)); - } - - inline int popcount(unsigned int value) { - return _popcnt32(static_cast(value)); - } - -#else - inline int popcountll(unsigned long long int x) { - return fallback_popcountll(x); - } - - inline int popcount(unsigned int x) { - return fallback_popcount(x); - } - -#endif - } // namespace detail_popcount - - /* Replacement for const_cast in sparse_array. * Can be overloaded for specific fancy pointers * (see: include/dice/boost_offset_pointer.h). @@ -218,63 +82,11 @@ namespace dice::sparse_map { }; namespace detail_sparse_hash { - /* to_address can convert any raw or fancy pointer into a raw pointer. - * It is needed for the allocator construct and destroy calls. - * This specific implementation is based on boost 1.71.0. - */ -#if __cplusplus >= 201400L // with 14-features - template - T *to_address(T *v) noexcept { - return v; - } - - namespace fancy_ptr_detail { - template - inline T *ptr_address(T *v, int) noexcept { - return v; - } - - template - inline auto ptr_address(T const &v, int) noexcept - -> decltype(std::pointer_traits::to_address(v)) { - return std::pointer_traits::to_address(v); - } - template - inline auto ptr_address(T const &v, long) noexcept { - return fancy_ptr_detail::ptr_address(v.operator->(), 0); - } - } // namespace fancy_ptr_detail - - template - inline auto to_address(T const &v) noexcept { - return fancy_ptr_detail::ptr_address(v, 0); - } -#else // without 14-features - template - inline T *to_address(T *v) noexcept { - return v; - } - - template - inline typename std::pointer_traits::element_type *to_address(T const &v) noexcept { - return detail_sparse_hash::to_address(v.operator->()); - } -#endif - - template - struct make_void { - using type = void; + concept has_is_transparent = requires { + typename T::is_transparent; }; - template - struct has_is_transparent : std::false_type {}; - - template - struct has_is_transparent::type> - : std::true_type {}; - template struct is_power_of_two_policy : std::false_type {}; @@ -335,13 +147,7 @@ namespace dice::sparse_map { template static T deserialize_value(Deserializer &deserializer) { - // MSVC < 2017 is not conformant, circumvent the problem by removing the - // template keyword -#if defined(_MSC_VER) && _MSC_VER < 1910 - return deserializer.Deserializer::operator()(); -#else return deserializer.Deserializer::template operator()(); -#endif } /** @@ -809,11 +615,11 @@ namespace dice::sparse_map { static void construct_value(allocator_type &alloc, pointer value, Args &&...value_args) { std::allocator_traits::construct( - alloc, detail_sparse_hash::to_address(value), std::forward(value_args)...); + alloc, std::to_address(value), std::forward(value_args)...); } static void destroy_value(allocator_type &alloc, pointer value) noexcept { - std::allocator_traits::destroy(alloc, detail_sparse_hash::to_address(value)); + std::allocator_traits::destroy(alloc, std::to_address(value)); } static void destroy_and_deallocate_values( @@ -834,10 +640,9 @@ namespace dice::sparse_map { static size_type popcount(bitmap_type val) noexcept { if constexpr (sizeof(bitmap_type) <= sizeof(unsigned int)) { - return static_cast( - dice::sparse_map::detail_popcount::popcount(static_cast(val))); + return static_cast(std::popcount(static_cast(val))); } else { - return static_cast(dice::sparse_map::detail_popcount::popcountll(val)); + return static_cast(std::popcount(val)); } } @@ -2371,16 +2176,16 @@ namespace dice::sparse_map { } private: - DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS Allocator m_alloc; - DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS Hash m_hash; - DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS KeyEqual m_key_equal; + [[no_unique_address]] Allocator m_alloc; + [[no_unique_address]] Hash m_hash; + [[no_unique_address]] KeyEqual m_key_equal; /** * Declared before m_bucket_count. Its constructor takes the bucket count by * reference and rounds it up to the count the policy can serve, and * m_bucket_count is initialized from that rounded value. */ - DICE_SPARSE_MAP_NO_UNIQUE_ADDRESS GrowthPolicy m_growth_policy; + [[no_unique_address]] GrowthPolicy m_growth_policy; sparse_buckets_container m_sparse_buckets_data; diff --git a/include/dice/sparse-map/sparse_map.hpp b/include/dice/sparse-map/sparse_map.hpp index cd43ed9e..1a2c1b93 100644 --- a/include/dice/sparse-map/sparse_map.hpp +++ b/include/dice/sparse-map/sparse_map.hpp @@ -89,9 +89,6 @@ namespace dice::sparse_map { dice::sparse_map::sh::sparsity Sparsity = dice::sparse_map::sh::sparsity::medium> class sparse_map { private: - template - using has_is_transparent = dice::sparse_map::detail_sparse_hash::has_is_transparent; - class KeySelect { public: using key_type = Key; @@ -391,9 +388,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (dice::sparse_map::detail_sparse_hash::has_is_transparent) size_type erase(K const &key) { return m_ht.erase(key); } @@ -406,9 +402,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (dice::sparse_map::detail_sparse_hash::has_is_transparent) size_type erase(K const &key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); } @@ -450,9 +445,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) T &at(K const &key) { return m_ht.at(key); } @@ -465,9 +459,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) T &at(K const &key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } @@ -475,9 +468,8 @@ namespace dice::sparse_map { /** * @copydoc at(const K& key) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) T const &at(K const &key) const { return m_ht.at(key); } @@ -485,9 +477,8 @@ namespace dice::sparse_map { /** * @copydoc at(const K& key, std::size_t precalculated_hash) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) T const &at(K const &key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } @@ -518,9 +509,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type count(K const &key) const { return m_ht.count(key); } @@ -533,9 +523,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type count(K const &key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } @@ -570,9 +559,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) iterator find(K const &key) { return m_ht.find(key); } @@ -585,9 +573,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) iterator find(K const &key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } @@ -595,9 +582,8 @@ namespace dice::sparse_map { /** * @copydoc find(const K& key) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) const_iterator find(K const &key) const { return m_ht.find(key); } @@ -610,9 +596,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) const_iterator find(K const &key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); } @@ -635,9 +620,8 @@ namespace dice::sparse_map { * KeyEqual::is_transparent exists. If so, K must be hashable and comparable * to Key. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) bool contains(K const &key) const { return m_ht.contains(key); } @@ -649,9 +633,8 @@ namespace dice::sparse_map { * hash value should be the same as hash_function()(key). Useful to speed-up * the lookup if you already have the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) bool contains(K const &key, std::size_t precalculated_hash) const { return m_ht.contains(key, precalculated_hash); } @@ -688,9 +671,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key) { return m_ht.equal_range(key); } @@ -703,9 +685,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key, std::size_t precalculated_hash) { return m_ht.equal_range(key, precalculated_hash); @@ -714,9 +695,8 @@ namespace dice::sparse_map { /** * @copydoc equal_range(const K& key) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key) const { return m_ht.equal_range(key); } @@ -724,9 +704,8 @@ namespace dice::sparse_map { /** * @copydoc equal_range(const K& key, std::size_t precalculated_hash) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range( K const &key, std::size_t precalculated_hash) const { return m_ht.equal_range(key, precalculated_hash); diff --git a/include/dice/sparse-map/sparse_set.hpp b/include/dice/sparse-map/sparse_set.hpp index 32339d31..0ff3c256 100644 --- a/include/dice/sparse-map/sparse_set.hpp +++ b/include/dice/sparse-map/sparse_set.hpp @@ -89,9 +89,6 @@ namespace dice::sparse_map { dice::sparse_map::sh::sparsity Sparsity = dice::sparse_map::sh::sparsity::medium> class sparse_set { private: - template - using has_is_transparent = dice::sparse_map::detail_sparse_hash::has_is_transparent; - class KeySelect { public: using key_type = Key; @@ -322,9 +319,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type erase(K const &key) { return m_ht.erase(key); } @@ -337,9 +333,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type erase(K const &key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); } @@ -370,9 +365,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type count(K const &key) const { return m_ht.count(key); } @@ -385,9 +379,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) size_type count(K const &key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } @@ -422,9 +415,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) iterator find(K const &key) { return m_ht.find(key); } @@ -437,9 +429,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) iterator find(K const &key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } @@ -447,9 +438,8 @@ namespace dice::sparse_map { /** * @copydoc find(const K& key) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) const_iterator find(K const &key) const { return m_ht.find(key); } @@ -462,9 +452,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) const_iterator find(K const &key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); } @@ -487,9 +476,8 @@ namespace dice::sparse_map { * KeyEqual::is_transparent exists. If so, K must be hashable and comparable * to Key. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) bool contains(K const &key) const { return m_ht.contains(key); } @@ -501,9 +489,8 @@ namespace dice::sparse_map { * hash value should be the same as hash_function()(key). Useful to speed-up * the lookup if you already have the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) bool contains(K const &key, std::size_t precalculated_hash) const { return m_ht.contains(key, precalculated_hash); } @@ -540,9 +527,8 @@ namespace dice::sparse_map { * `KeyEqual::is_transparent` exists. If so, `K` must be hashable and * comparable to `Key`. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key) { return m_ht.equal_range(key); } @@ -555,9 +541,8 @@ namespace dice::sparse_map { * behaviour is undefined. Useful to speed-up the lookup if you already have * the hash. */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key, std::size_t precalculated_hash) { return m_ht.equal_range(key, precalculated_hash); @@ -566,9 +551,8 @@ namespace dice::sparse_map { /** * @copydoc equal_range(const K& key) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range(K const &key) const { return m_ht.equal_range(key); } @@ -576,9 +560,8 @@ namespace dice::sparse_map { /** * @copydoc equal_range(const K& key, std::size_t precalculated_hash) */ - template< - class K, class KE = KeyEqual, - typename std::enable_if::value>::type * = nullptr> + template + requires (detail_sparse_hash::has_is_transparent) std::pair equal_range( K const &key, std::size_t precalculated_hash) const { return m_ht.equal_range(key, precalculated_hash); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4fe7fb85..3b9d7fe0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,6 @@ find_package(Boost REQUIRED) add_executable(tsl_sparse_map_tests "main.cpp" "custom_allocator_tests.cpp" "policy_tests.cpp" - "popcount_tests.cpp" "sparse_map_tests.cpp" "sparse_set_tests.cpp" "fancy_pointer/sparse_array_tests.cpp" diff --git a/tests/popcount_tests.cpp b/tests/popcount_tests.cpp deleted file mode 100644 index 334f451f..00000000 --- a/tests/popcount_tests.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -BOOST_AUTO_TEST_SUITE(test_popcount) - -BOOST_AUTO_TEST_CASE(test_popcount_1) { - std::uint32_t value = 0; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcount(value), 0); - - value = 1; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcount(value), 1); - - value = 2; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcount(value), 1); - - value = 294967496; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcount(value), 12); - - value = std::numeric_limits::max(); - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcount(value), 32); -} - -BOOST_AUTO_TEST_CASE(test_popcountll_1) { - std::uint64_t value = 0; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 0); - - value = 1; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 1); - - value = 2; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 1); - - value = 294967496; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 12); - - value = 8446744073709551416ull; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 40); - - value = std::numeric_limits::max(); - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::popcountll(value), 64); -} - -BOOST_AUTO_TEST_CASE(test_fallback_popcount_1) { - std::uint32_t value = 0; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcount(value), 0); - - value = 1; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcount(value), 1); - - value = 2; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcount(value), 1); - - value = 294967496; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcount(value), 12); - - value = std::numeric_limits::max(); - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcount(value), 32); -} - -BOOST_AUTO_TEST_CASE(test_fallback_popcountll_1) { - std::uint64_t value = 0; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 0); - - value = 1; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 1); - - value = 2; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 1); - - value = 294967496; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 12); - - value = 8446744073709551416ull; - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 40); - - value = std::numeric_limits::max(); - BOOST_CHECK_EQUAL(dice::sparse_map::detail_popcount::fallback_popcountll(value), 64); -} - -BOOST_AUTO_TEST_SUITE_END() From 31adcd25e26d080620a7b1708375f6224c50de69 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+liss-h@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:46:12 +0200 Subject: [PATCH 3/6] bump pobr version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8e21702..a1f17256 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.24) project(dice-sparse-map VERSION 0.2.11 DESCRIPTION "C++ implementation of a memory efficient hash map and hash set based on [tsl::sparse_map](https://github.com/Tessil/sparse-map). We added support for fancy pointers.") -set(POBR_VERSION 1) # Persisted Object Binary Representation Version +set(POBR_VERSION 2) # Persisted Object Binary Representation Version include(cmake/boilerplate_init.cmake) boilerplate_init() From 0d6ad2db98af51c9a2c54a97ab5ade37a6017a06 Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+liss-h@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:00:09 +0200 Subject: [PATCH 4/6] add missing bit header --- include/dice/sparse-map/sparse_hash.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dice/sparse-map/sparse_hash.hpp b/include/dice/sparse-map/sparse_hash.hpp index 239ac8bd..0ea5675b 100644 --- a/include/dice/sparse-map/sparse_hash.hpp +++ b/include/dice/sparse-map/sparse_hash.hpp @@ -25,6 +25,7 @@ #define DICE_SPARSE_MAP_SPARSE_HASH_HPP #include +#include #include #include #include From d871cc0386d8c63f2745f54d35b24e81180999bd Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+liss-h@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:03:34 +0200 Subject: [PATCH 5/6] remove code for 32bit arch --- include/dice/sparse-map/sparse_hash.hpp | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/include/dice/sparse-map/sparse_hash.hpp b/include/dice/sparse-map/sparse_hash.hpp index 0ea5675b..5d11047f 100644 --- a/include/dice/sparse-map/sparse_hash.hpp +++ b/include/dice/sparse-map/sparse_hash.hpp @@ -199,21 +199,9 @@ namespace dice::sparse_map { ? 4 : 8; // (Sparsity == dice::sh::sparsity::low) - /** - * Bitmap size configuration. - * Use 32 bits for the bitmap on 32-bits or less environnement as popcount on - * 64 bits numbers is slow on these environnement. Use 64 bits bitmap - * otherwise. - */ -#if SIZE_MAX <= UINT32_MAX - using bitmap_type = std::uint_least32_t; - static constexpr std::size_t BITMAP_NB_BITS = 32; - static constexpr std::size_t BUCKET_SHIFT = 5; -#else using bitmap_type = std::uint_least64_t; static constexpr std::size_t BITMAP_NB_BITS = 64; static constexpr std::size_t BUCKET_SHIFT = 6; -#endif static constexpr std::size_t BUCKET_MASK = BITMAP_NB_BITS - 1; @@ -640,11 +628,7 @@ namespace dice::sparse_map { } static size_type popcount(bitmap_type val) noexcept { - if constexpr (sizeof(bitmap_type) <= sizeof(unsigned int)) { - return static_cast(std::popcount(static_cast(val))); - } else { - return static_cast(std::popcount(val)); - } + return static_cast(std::popcount(val)); } size_type index_to_offset(size_type index) const noexcept { From 8d78e5756e4b04ce71569263c8c9182208199cab Mon Sep 17 00:00:00 2001 From: Liss Heidrich <31625940+liss-h@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:04:48 +0200 Subject: [PATCH 6/6] fix weird ns --- include/dice/sparse-map/sparse_map.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dice/sparse-map/sparse_map.hpp b/include/dice/sparse-map/sparse_map.hpp index 1a2c1b93..03f45c06 100644 --- a/include/dice/sparse-map/sparse_map.hpp +++ b/include/dice/sparse-map/sparse_map.hpp @@ -389,7 +389,7 @@ namespace dice::sparse_map { * comparable to `Key`. */ template - requires (dice::sparse_map::detail_sparse_hash::has_is_transparent) + requires (detail_sparse_hash::has_is_transparent) size_type erase(K const &key) { return m_ht.erase(key); } @@ -403,7 +403,7 @@ namespace dice::sparse_map { * the hash. */ template - requires (dice::sparse_map::detail_sparse_hash::has_is_transparent) + requires (detail_sparse_hash::has_is_transparent) size_type erase(K const &key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); }