evaluate layout-compatible at compile time

Signed-off-by: wangbaiping <wbphub@live.com>
pull/1292/head
wangbaiping 2 years ago
parent 76466b85fb
commit de6dd6c899
  1. 140
      absl/container/internal/container_memory.h

@ -338,22 +338,19 @@ union map_slot_type {
};
template <class K, class V>
struct map_slot_policy {
using MutableKeys = memory_internal::IsLayoutCompatible<K, V>;
template <class K, class V>
struct map_slot_policy_base {
using slot_type = map_slot_type<K, V>;
using value_type = std::pair<const K, V>;
using mutable_value_type =
std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>;
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<const K, V> and pair<K, V> 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<K, V>;
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 <class Allocator>
static void construct(Allocator* alloc, slot_type* slot,
const slot_type* other) {
emplace(slot);
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
other->value);
}
template <class Allocator>
static void destroy(Allocator* alloc, slot_type* slot) {
absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value);
}
};
template <class K, class V, bool B = MutableKeys<K, V>::value>
struct map_slot_policy;
template <class K, class V>
struct map_slot_policy<K, V, false> : map_slot_policy_base<K, V> {
using slot_type = map_slot_type<K, V>;
using value_type = std::pair<const K, V>;
using mutable_value_type =
std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>;
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<K*>(
std::addressof(slot->value.first)));
return *std::launder(const_cast<K*>(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 <class Allocator, class... Args>
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
emplace(slot);
if (kMutableKeys::value) {
absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value,
std::forward<Args>(args)...);
} else {
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
std::forward<Args>(args)...);
}
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
std::forward<Args>(args)...);
}
// Construct this slot by moving from another slot.
template <class Allocator>
static void construct(Allocator* alloc, slot_type* slot, slot_type* other) {
emplace(slot);
if (kMutableKeys::value) {
absl::allocator_traits<Allocator>::construct(
*alloc, &slot->mutable_value, std::move(other->mutable_value));
} else {
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
std::move(other->value));
}
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
std::move(other->value));
}
// Construct this slot by copying from another slot.
template <class Allocator>
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<value_type>()) {
// TODO(b/247130232,b/251814870): remove casts after fixing warnings.
std::memcpy(static_cast<void*>(std::launder(&new_slot->value)),
static_cast<const void*>(&old_slot->value),
sizeof(value_type));
return;
}
#endif
absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value,
std::move(old_slot->value));
destroy(alloc, old_slot);
}
};
// If pair<const K, V> and pair<K, V> 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 <class K, class V>
struct map_slot_policy<K, V, true> : map_slot_policy_base<K, V> {
using slot_type = map_slot_type<K, V>;
using value_type = std::pair<const K, V>;
using mutable_value_type =
std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>;
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 <class Allocator, class... Args>
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
emplace(slot);
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
other->value);
absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value,
std::forward<Args>(args)...);
}
// Construct this slot by moving from another slot.
template <class Allocator>
static void destroy(Allocator* alloc, slot_type* slot) {
if (kMutableKeys::value) {
absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value);
} else {
absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value);
}
static void construct(Allocator* alloc, slot_type* slot, slot_type* other) {
emplace(slot);
absl::allocator_traits<Allocator>::construct(
*alloc, &slot->mutable_value, std::move(other->mutable_value));
}
template <class Allocator>
@ -436,13 +485,8 @@ struct map_slot_policy {
}
#endif
if (kMutableKeys::value) {
absl::allocator_traits<Allocator>::construct(
*alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value));
} else {
absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value,
std::move(old_slot->value));
}
absl::allocator_traits<Allocator>::construct(
*alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value));
destroy(alloc, old_slot);
}
};

Loading…
Cancel
Save