Commit db5c7993 by Abseil Team Committed by Copybara-Service

Make `FlatHashMapPolicy` return `std::true_type` for relocatable objects.

This reduces produced binary size and can trigger even more optimizations in the future.

PiperOrigin-RevId: 584136517
Change-Id: I3854833799f88f28b755ec53132925f0c3d468ab
parent f393335c
......@@ -268,6 +268,7 @@ cc_test(
":unordered_map_members_test",
":unordered_map_modifiers_test",
"//absl/log:check",
"//absl/meta:type_traits",
"//absl/types:any",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
......@@ -401,6 +402,7 @@ cc_test(
deps = [
":container_memory",
":test_instance_tracker",
"//absl/meta:type_traits",
"//absl/strings",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
......
......@@ -305,6 +305,7 @@ absl_cc_test(
absl::check
absl::flat_hash_map
absl::hash_generator_testing
absl::type_traits
absl::unordered_map_constructor_test
absl::unordered_map_lookup_test
absl::unordered_map_members_test
......@@ -450,6 +451,7 @@ absl_cc_test(
absl::container_memory
absl::strings
absl::test_instance_tracker
absl::type_traits
GTest::gmock_main
)
......
......@@ -579,9 +579,9 @@ struct FlatHashMapPolicy {
}
template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot,
static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
slot_policy::transfer(alloc, new_slot, old_slot);
return slot_policy::transfer(alloc, new_slot, old_slot);
}
template <class F, class... Args>
......
......@@ -14,14 +14,20 @@
#include "absl/container/flat_hash_map.h"
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/unordered_map_constructor_test.h"
#include "absl/container/internal/unordered_map_lookup_test.h"
#include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h"
#include "absl/log/check.h"
#include "absl/meta/type_traits.h"
#include "absl/types/any.h"
namespace absl {
......@@ -102,6 +108,34 @@ TEST(FlatHashMap, StandardLayout) {
}
}
TEST(FlatHashMap, Relocatability) {
static_assert(absl::is_trivially_relocatable<int>::value, "");
static_assert(
absl::is_trivially_relocatable<std::pair<const int, int>>::value, "");
static_assert(
std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
int, int>::transfer<std::allocator<char>>(nullptr,
nullptr,
nullptr)),
std::true_type>::value,
"");
struct NonRelocatable {
NonRelocatable() = default;
NonRelocatable(NonRelocatable&&) {}
NonRelocatable& operator=(NonRelocatable&&) { return *this; }
void* self = nullptr;
};
EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
EXPECT_TRUE(
(std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
int, NonRelocatable>::
transfer<std::allocator<char>>(nullptr, nullptr,
nullptr)),
std::false_type>::value));
}
// gcc becomes unhappy if this is inside the method, so pull it out here.
struct balast {};
......@@ -150,9 +184,7 @@ struct Hash {
struct Eq {
using is_transparent = void;
bool operator()(size_t lhs, size_t rhs) const {
return lhs == rhs;
}
bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
bool operator()(size_t lhs, const LazyInt& rhs) const {
return lhs == rhs.value;
}
......
......@@ -122,10 +122,10 @@ auto TupleRefImpl(T&& t, absl::index_sequence<Is...>)
// Returns a tuple of references to the elements of the input tuple. T must be a
// tuple.
template <class T>
auto TupleRef(T&& t) -> decltype(
TupleRefImpl(std::forward<T>(t),
absl::make_index_sequence<
std::tuple_size<typename std::decay<T>::type>::value>())) {
auto TupleRef(T&& t) -> decltype(TupleRefImpl(
std::forward<T>(t),
absl::make_index_sequence<
std::tuple_size<typename std::decay<T>::type>::value>())) {
return TupleRefImpl(
std::forward<T>(t),
absl::make_index_sequence<
......@@ -156,8 +156,8 @@ void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
// Constructs T using the args specified in the tuple and calls F with the
// constructed value.
template <class T, class Tuple, class F>
decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
Tuple&& t, F&& f) {
decltype(std::declval<F>()(std::declval<T>())) WithConstructed(Tuple&& t,
F&& f) {
return memory_internal::WithConstructedImpl<T>(
std::forward<Tuple>(t),
absl::make_index_sequence<
......@@ -423,16 +423,19 @@ struct map_slot_policy {
}
template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot,
static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
auto is_relocatable =
typename absl::is_trivially_relocatable<value_type>::type();
emplace(new_slot);
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
if (absl::is_trivially_relocatable<value_type>()) {
if (is_relocatable) {
// 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;
return is_relocatable;
}
#endif
......@@ -444,6 +447,7 @@ struct map_slot_policy {
std::move(old_slot->value));
}
destroy(alloc, old_slot);
return is_relocatable;
}
};
......
......@@ -14,8 +14,11 @@
#include "absl/container/internal/container_memory.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility>
......@@ -23,6 +26,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/internal/test_instance_tracker.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
namespace absl {
......@@ -219,8 +223,7 @@ TEST(DecomposePair, NotDecomposable) {
ADD_FAILURE() << "Must not be called";
return 'A';
};
EXPECT_STREQ("not decomposable",
TryDecomposePair(f));
EXPECT_STREQ("not decomposable", TryDecomposePair(f));
EXPECT_STREQ("not decomposable",
TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
std::make_tuple(0.5)));
......@@ -251,6 +254,31 @@ TEST(MapSlotPolicy, ConstKeyAndValue) {
EXPECT_EQ(tracker.copies(), 0);
}
TEST(MapSlotPolicy, TransferReturnsTrue) {
{
using slot_policy = map_slot_policy<int, float>;
EXPECT_TRUE(
(std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
nullptr, nullptr, nullptr)),
std::true_type>::value));
}
{
struct NonRelocatable {
NonRelocatable() = default;
NonRelocatable(NonRelocatable&&) {}
NonRelocatable& operator=(NonRelocatable&&) { return *this; }
void* self = nullptr;
};
EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
using slot_policy = map_slot_policy<int, NonRelocatable>;
EXPECT_TRUE(
(std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
nullptr, nullptr, nullptr)),
std::false_type>::value));
}
}
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment