Add a key-returning callback insert to Set. (#4072)

This turns out to be super useful now that we have the `key_context`
mechanism and can do much more meaningful heterogeneous lookups, where
the stored key can be *very* different from the lookup key.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Chandler Carruth
2024-06-22 00:57:41 +00:00
committed by GitHub
co-authored by Richard Smith
parent b70cfd0be9
commit 9d95c6836d
2 changed files with 41 additions and 2 deletions
+32
View File
@@ -204,6 +204,21 @@ class SetBase
auto Insert(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT())
-> InsertResult;
// Insert a key into the map and call the provided callback if necessary to
// produce a new key when no existing value is found.
//
// Example: `m.Insert(key_equivalent, [] { return real_key; });`
//
// The point of this function is when the lookup key is _different_from the
// stored key. However, we don't restrict it in case that blocks generic
// usage.
template <typename LookupKeyT, typename KeyCallbackT>
auto Insert(LookupKeyT lookup_key, KeyCallbackT key_cb,
KeyContextT key_context = KeyContextT()) -> InsertResult
requires(
!std::same_as<KeyT, KeyCallbackT> &&
std::convertible_to<decltype(std::declval<KeyCallbackT>()()), KeyT>);
// Insert a key into the set and call the provided callback to allow in-place
// construction of the key if not already present. The lookup key is passed
// through to the callback so it needn't be captured and can be kept in a
@@ -331,6 +346,23 @@ auto SetBase<InputKeyT, InputKeyContextT>::Insert(LookupKeyT lookup_key,
key_context);
}
template <typename InputKeyT, typename InputKeyContextT>
template <typename LookupKeyT, typename KeyCallbackT>
auto SetBase<InputKeyT, InputKeyContextT>::Insert(LookupKeyT lookup_key,
KeyCallbackT key_cb,
KeyContextT key_context)
-> InsertResult
requires(!std::same_as<KeyT, KeyCallbackT> &&
std::convertible_to<decltype(std::declval<KeyCallbackT>()()), KeyT>)
{
return Insert(
lookup_key,
[&key_cb](LookupKeyT /*lookup_key*/, void* key_storage) {
new (key_storage) KeyT(key_cb());
},
key_context);
}
template <typename InputKeyT, typename InputKeyContextT>
template <typename LookupKeyT, typename InsertCallbackT>
auto SetBase<InputKeyT, InputKeyContextT>::Insert(LookupKeyT lookup_key,
+9 -2
View File
@@ -343,12 +343,17 @@ TEST(SetContextTest, Basic) {
auto i_result = s.Insert(1, IndexKeyContext<TestData>(keys));
EXPECT_FALSE(i_result.is_inserted());
EXPECT_TRUE(s.Contains(1, key_context));
EXPECT_TRUE(s.Insert(
TestData(200), [] { return 2; }, key_context)
.is_inserted());
EXPECT_TRUE(s.Contains(2, key_context));
EXPECT_TRUE(s.Contains(TestData(200), key_context));
// Verify all the elements.
ExpectSetElementsAre(s, {1});
ExpectSetElementsAre(s, {1, 2});
// Fill up a bunch to ensure we trigger growth a few times.
for (int i : llvm::seq(2, 512)) {
for (int i : llvm::seq(3, 512)) {
SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
EXPECT_TRUE(s.Insert(i, key_context).is_inserted());
}
@@ -359,6 +364,8 @@ TEST(SetContextTest, Basic) {
}
EXPECT_FALSE(s.Contains(0, key_context));
EXPECT_FALSE(s.Contains(512, key_context));
EXPECT_FALSE(s.Contains(TestData(0), key_context));
EXPECT_FALSE(s.Contains(TestData(51200), key_context));
// Verify all the elements.
ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512)));