From de6dd6c89911b32cd755f931fb6889753610500b Mon Sep 17 00:00:00 2001 From: wangbaiping Date: Mon, 10 Oct 2022 22:02:15 +0800 Subject: [PATCH] evaluate layout-compatible at compile time Signed-off-by: wangbaiping --- absl/container/internal/container_memory.h | 140 ++++++++++++++------- 1 file changed, 92 insertions(+), 48 deletions(-) diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index bfa4ff93..d8f779fa 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -338,22 +338,19 @@ union map_slot_type { }; template -struct map_slot_policy { +using MutableKeys = memory_internal::IsLayoutCompatible; + +template +struct map_slot_policy_base { using slot_type = map_slot_type; using value_type = std::pair; - using mutable_value_type = - std::pair, absl::remove_const_t>; - private: + protected: static void emplace(slot_type* slot) { // The construction of union doesn't do anything at runtime but it allows us // to access its members without violating aliasing rules. new (slot) slot_type; } - // If pair and pair are layout-compatible, we can accept one - // or the other via slot_type. We are also free to access the key via - // slot_type::key in this case. - using kMutableKeys = memory_internal::IsLayoutCompatible; public: static value_type& element(slot_type* slot) { return slot->value; } @@ -361,65 +358,117 @@ struct map_slot_policy { return slot->value; } + // Construct this slot by copying from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, + const slot_type* other) { + emplace(slot); + absl::allocator_traits::construct(*alloc, &slot->value, + other->value); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } +}; + +template ::value> +struct map_slot_policy; + +template +struct map_slot_policy : map_slot_policy_base { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = + std::pair, absl::remove_const_t>; + + using base = typename map_slot_policy::map_slot_policy_base; + using base::construct; + using base::destroy; + using base::element; + using base::emplace; + // When C++17 is available, we can use std::launder to provide mutable // access to the key for use in node handle. #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 static K& mutable_key(slot_type* slot) { - // Still check for kMutableKeys so that we can avoid calling std::launder - // unless necessary because it can interfere with optimizations. - return kMutableKeys::value ? slot->key - : *std::launder(const_cast( - std::addressof(slot->value.first))); + return *std::launder(const_cast(std::addressof(slot->value.first))); } #else // !(defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606) static const K& mutable_key(slot_type* slot) { return key(slot); } #endif - static const K& key(const slot_type* slot) { - return kMutableKeys::value ? slot->key : slot->value.first; - } + static const K& key(const slot_type* slot) { return slot->value.first; } template static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { emplace(slot); - if (kMutableKeys::value) { - absl::allocator_traits::construct(*alloc, &slot->mutable_value, - std::forward(args)...); - } else { - absl::allocator_traits::construct(*alloc, &slot->value, - std::forward(args)...); - } + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); } // Construct this slot by moving from another slot. template static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { emplace(slot); - if (kMutableKeys::value) { - absl::allocator_traits::construct( - *alloc, &slot->mutable_value, std::move(other->mutable_value)); - } else { - absl::allocator_traits::construct(*alloc, &slot->value, - std::move(other->value)); - } + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); } - // Construct this slot by copying from another slot. template - static void construct(Allocator* alloc, slot_type* slot, - const slot_type* other) { + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + emplace(new_slot); +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + if (absl::is_trivially_relocatable()) { + // TODO(b/247130232,b/251814870): remove casts after fixing warnings. + std::memcpy(static_cast(std::launder(&new_slot->value)), + static_cast(&old_slot->value), + sizeof(value_type)); + return; + } +#endif + + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + destroy(alloc, old_slot); + } +}; + +// If pair and pair are layout-compatible, we can accept one +// or the other via slot_type. We are also free to access the key via +// slot_type::key in this case. +template +struct map_slot_policy : map_slot_policy_base { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = + std::pair, absl::remove_const_t>; + + using base = typename map_slot_policy::map_slot_policy_base; + using base::construct; + using base::destroy; + using base::element; + using base::emplace; + + static K& mutable_key(slot_type* slot) { return slot->key; } + + static const K& key(const slot_type* slot) { return slot->key; } + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { emplace(slot); - absl::allocator_traits::construct(*alloc, &slot->value, - other->value); + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); } + // Construct this slot by moving from another slot. template - static void destroy(Allocator* alloc, slot_type* slot) { - if (kMutableKeys::value) { - absl::allocator_traits::destroy(*alloc, &slot->mutable_value); - } else { - absl::allocator_traits::destroy(*alloc, &slot->value); - } + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); } template @@ -436,13 +485,8 @@ struct map_slot_policy { } #endif - if (kMutableKeys::value) { - absl::allocator_traits::construct( - *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); - } else { - absl::allocator_traits::construct(*alloc, &new_slot->value, - std::move(old_slot->value)); - } + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); destroy(alloc, old_slot); } };