From 7c1e947282d5081a369f0c091427e2866964cbe1 Mon Sep 17 00:00:00 2001 From: Henrique Bucher Date: Tue, 14 Apr 2026 15:56:22 -0300 Subject: [PATCH] Modernize to C++17: replace aligned_storage, add nodiscard/explicit Bump minimum standard from C++11 to C++17 and apply modernizations: - Replace deprecated std::aligned_storage (removed in C++26) with alignas + std::byte arrays in bucket_container. Use std::launder for pointer access to constructed objects, and a separate storage_ptr() for construct/destroy on uninitialized storage. - Add [[nodiscard]] to public query functions: hash_function(), key_eq(), get_allocator(), hashpower(), bucket_count(), empty(), size(), capacity(), load_factor(), contains(). - Mark primary constructor explicit to prevent implicit conversion from size_type. - Update CMake target_compile_features from cxx_constexpr to cxx_std_17. --- libcuckoo/CMakeLists.txt | 6 ++---- libcuckoo/bucket_container.hh | 31 ++++++++++++++++++------------- libcuckoo/cuckoohash_map.hh | 27 ++++++++++++++------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/libcuckoo/CMakeLists.txt b/libcuckoo/CMakeLists.txt index 5ec8902..fbdeb0c 100644 --- a/libcuckoo/CMakeLists.txt +++ b/libcuckoo/CMakeLists.txt @@ -13,10 +13,8 @@ set (libcuckoo_VERSION "${libcuckoo_VERSION}.${libcuckoo_VERSION_PATCH}") add_library(libcuckoo INTERFACE) add_library(libcuckoo::libcuckoo ALIAS libcuckoo) -# tag libcuckoo target with a c++11 feature so that libcuckoo users -# will have c++11 turned on in their compile when they use this target. -# XXX: newer cmakes have a "cxx_std_11" feature that could be used -target_compile_features (libcuckoo INTERFACE cxx_constexpr) +# Require C++17 for std::byte, [[nodiscard]], and other modern features +target_compile_features (libcuckoo INTERFACE cxx_std_17) # Include relative to the base directory target_include_directories(libcuckoo INTERFACE diff --git a/libcuckoo/bucket_container.hh b/libcuckoo/bucket_container.hh index cbf1481..3d6fc6f 100644 --- a/libcuckoo/bucket_container.hh +++ b/libcuckoo/bucket_container.hh @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -60,11 +61,12 @@ public: bucket() noexcept : occupied_() {} const value_type &kvpair(size_type ind) const { - return *static_cast( - static_cast(&values_[ind])); + return *std::launder( + reinterpret_cast(values_[ind])); } value_type &kvpair(size_type ind) { - return *static_cast(static_cast(&values_[ind])); + return *std::launder( + reinterpret_cast(values_[ind])); } const key_type &key(size_type ind) const { @@ -90,19 +92,22 @@ public: using storage_value_type = std::pair; + // Access a constructed key-value pair. Only valid when occupied(ind). const storage_value_type &storage_kvpair(size_type ind) const { - return *static_cast( - static_cast(&values_[ind])); + return *std::launder( + reinterpret_cast(values_[ind])); } storage_value_type &storage_kvpair(size_type ind) { - return *static_cast( - static_cast(&values_[ind])); + return *std::launder( + reinterpret_cast(values_[ind])); + } + // Raw pointer to uninitialized storage for construct/destroy. + storage_value_type *storage_ptr(size_type ind) { + return reinterpret_cast(values_[ind]); } - std::array::type, - SLOT_PER_BUCKET> - values_; + alignas(storage_value_type) + std::byte values_[SLOT_PER_BUCKET][sizeof(storage_value_type)]; std::array partials_; std::array occupied_; }; @@ -201,7 +206,7 @@ public: bucket &b = buckets_[ind]; assert(!b.occupied(slot)); b.partial(slot) = p; - traits_::construct(allocator_, std::addressof(b.storage_kvpair(slot)), + traits_::construct(allocator_, b.storage_ptr(slot), std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)); @@ -214,7 +219,7 @@ public: bucket &b = buckets_[ind]; assert(b.occupied(slot)); b.occupied(slot) = false; - traits_::destroy(allocator_, std::addressof(b.storage_kvpair(slot))); + traits_::destroy(allocator_, b.storage_ptr(slot)); } // Destroys all the live data in the buckets. Does not deallocate the bucket diff --git a/libcuckoo/cuckoohash_map.hh b/libcuckoo/cuckoohash_map.hh index 04f63f1..b598741 100644 --- a/libcuckoo/cuckoohash_map.hh +++ b/libcuckoo/cuckoohash_map.hh @@ -101,9 +101,10 @@ public: * @param equal equality function instance to use * @param alloc allocator instance to use */ - cuckoohash_map(size_type n = DEFAULT_SIZE, const Hash &hf = Hash(), - const KeyEqual &equal = KeyEqual(), - const Allocator &alloc = Allocator()) + explicit cuckoohash_map(size_type n = DEFAULT_SIZE, + const Hash &hf = Hash(), + const KeyEqual &equal = KeyEqual(), + const Allocator &alloc = Allocator()) : hash_fn_(hf), eq_fn_(equal), buckets_(reserve_calc(n), alloc), old_buckets_(0, alloc), all_locks_(get_allocator()), num_remaining_lazy_rehash_locks_(0), @@ -287,21 +288,21 @@ public: * * @return the hash function */ - hasher hash_function() const { return hash_fn_; } + [[nodiscard]] hasher hash_function() const { return hash_fn_; } /** * Returns the function that compares keys for equality * * @return the key comparison function */ - key_equal key_eq() const { return eq_fn_; } + [[nodiscard]] key_equal key_eq() const { return eq_fn_; } /** * Returns the allocator associated with the map * * @return the associated allocator */ - allocator_type get_allocator() const { return buckets_.get_allocator(); } + [[nodiscard]] allocator_type get_allocator() const { return buckets_.get_allocator(); } /** * Returns the hashpower of the table, which is log2(@ref @@ -309,28 +310,28 @@ public: * * @return the hashpower */ - size_type hashpower() const { return buckets_.hashpower(); } + [[nodiscard]] size_type hashpower() const { return buckets_.hashpower(); } /** * Returns the number of buckets in the table. * * @return the bucket count */ - size_type bucket_count() const { return buckets_.size(); } + [[nodiscard]] size_type bucket_count() const { return buckets_.size(); } /** * Returns whether the table is empty or not. * * @return true if the table is empty, false otherwise */ - bool empty() const { return size() == 0; } + [[nodiscard]] bool empty() const { return size() == 0; } /** * Returns the number of elements in the table. * * @return number of elements in the table */ - size_type size() const { + [[nodiscard]] size_type size() const { if (all_locks_.size() == 0) { return 0; } @@ -347,7 +348,7 @@ public: * * @return capacity of table */ - size_type capacity() const { return bucket_count() * slot_per_bucket(); } + [[nodiscard]] size_type capacity() const { return bucket_count() * slot_per_bucket(); } /** * Returns the percentage the table is filled, that is, @ref size() ÷ @@ -355,7 +356,7 @@ public: * * @return load factor of the table */ - double load_factor() const { + [[nodiscard]] double load_factor() const { return static_cast(size()) / static_cast(capacity()); } @@ -622,7 +623,7 @@ public: * Returns whether or not @p key is in the table. Equivalent to @ref * find_fn with a functor that does nothing. */ - template bool contains(const K &key) const { + template [[nodiscard]] bool contains(const K &key) const { return find_fn(key, [](const mapped_type &) {}); }