[DenseMap] Add a C++17-style try_emplace method.

This provides an elegant pattern to solve the "construct if not in map
already" problem we have many times in LLVM. Without try_emplace we
either have to rely on a sentinel value (nullptr) or do two lookups.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@276277 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Benjamin Kramer 2016-07-21 13:37:53 +00:00
parent b11b3529ce
commit 1a324fd49a
2 changed files with 49 additions and 41 deletions

View File

@ -169,30 +169,45 @@ public:
// If the key is already in the map, it returns false and doesn't update the // If the key is already in the map, it returns false and doesn't update the
// value. // value.
std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) { std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) {
BucketT *TheBucket; return try_emplace(KV.first, KV.second);
if (LookupBucketFor(KV.first, TheBucket))
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
false); // Already in map.
// Otherwise, insert the new element.
TheBucket = InsertIntoBucket(KV.first, KV.second, TheBucket);
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
true);
} }
// Inserts key,value pair into the map if the key isn't already in the map. // Inserts key,value pair into the map if the key isn't already in the map.
// If the key is already in the map, it returns false and doesn't update the // If the key is already in the map, it returns false and doesn't update the
// value. // value.
std::pair<iterator, bool> insert(std::pair<KeyT, ValueT> &&KV) { std::pair<iterator, bool> insert(std::pair<KeyT, ValueT> &&KV) {
return try_emplace(std::move(KV.first), std::move(KV.second));
}
// Inserts key,value pair into the map if the key isn't already in the map.
// The value is constructed in-place if the key is not in the map, otherwise
// it is not moved.
template <typename... Ts>
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&... Args) {
BucketT *TheBucket; BucketT *TheBucket;
if (LookupBucketFor(KV.first, TheBucket)) if (LookupBucketFor(Key, TheBucket))
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
false); // Already in map. false); // Already in map.
// Otherwise, insert the new element. // Otherwise, insert the new element.
TheBucket = InsertIntoBucket(std::move(KV.first), TheBucket =
std::move(KV.second), InsertIntoBucket(TheBucket, std::move(Key), std::forward<Ts>(Args)...);
TheBucket); return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
true);
}
// Inserts key,value pair into the map if the key isn't already in the map.
// The value is constructed in-place if the key is not in the map, otherwise
// it is not moved.
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
false); // Already in map.
// Otherwise, insert the new element.
TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
true); true);
} }
@ -211,8 +226,8 @@ public:
false); // Already in map. false); // Already in map.
// Otherwise, insert the new element. // Otherwise, insert the new element.
TheBucket = InsertIntoBucket(std::move(KV.first), std::move(KV.second), Val, TheBucket = InsertIntoBucketWithLookup(TheBucket, std::move(KV.first),
TheBucket); std::move(KV.second), Val);
return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true),
true); true);
} }
@ -249,7 +264,7 @@ public:
if (LookupBucketFor(Key, TheBucket)) if (LookupBucketFor(Key, TheBucket))
return *TheBucket; return *TheBucket;
return *InsertIntoBucket(Key, ValueT(), TheBucket); return *InsertIntoBucket(TheBucket, Key);
} }
ValueT &operator[](const KeyT &Key) { ValueT &operator[](const KeyT &Key) {
@ -261,7 +276,7 @@ public:
if (LookupBucketFor(Key, TheBucket)) if (LookupBucketFor(Key, TheBucket))
return *TheBucket; return *TheBucket;
return *InsertIntoBucket(std::move(Key), ValueT(), TheBucket); return *InsertIntoBucket(TheBucket, std::move(Key));
} }
ValueT &operator[](KeyT &&Key) { ValueT &operator[](KeyT &&Key) {
@ -429,36 +444,19 @@ private:
static_cast<DerivedT *>(this)->shrink_and_clear(); static_cast<DerivedT *>(this)->shrink_and_clear();
} }
template <typename KeyArg, typename... ValueArgs>
BucketT *InsertIntoBucket(const KeyT &Key, const ValueT &Value, BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key,
BucketT *TheBucket) { ValueArgs &&... Values) {
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket); TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
TheBucket->getFirst() = Key; TheBucket->getFirst() = std::forward<KeyArg>(Key);
::new (&TheBucket->getSecond()) ValueT(Value); ::new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...);
return TheBucket;
}
BucketT *InsertIntoBucket(const KeyT &Key, ValueT &&Value,
BucketT *TheBucket) {
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
TheBucket->getFirst() = Key;
::new (&TheBucket->getSecond()) ValueT(std::move(Value));
return TheBucket;
}
BucketT *InsertIntoBucket(KeyT &&Key, ValueT &&Value, BucketT *TheBucket) {
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
TheBucket->getFirst() = std::move(Key);
::new (&TheBucket->getSecond()) ValueT(std::move(Value));
return TheBucket; return TheBucket;
} }
template <typename LookupKeyT> template <typename LookupKeyT>
BucketT *InsertIntoBucket(KeyT &&Key, ValueT &&Value, LookupKeyT &Lookup, BucketT *InsertIntoBucketWithLookup(BucketT *TheBucket, KeyT &&Key,
BucketT *TheBucket) { ValueT &&Value, LookupKeyT &Lookup) {
TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket); TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket);
TheBucket->getFirst() = std::move(Key); TheBucket->getFirst() = std::move(Key);

View File

@ -619,4 +619,14 @@ TEST(DenseMapCustomTest, SmallDenseMapGrowTest) {
EXPECT_TRUE(map.find(32) == map.end()); EXPECT_TRUE(map.find(32) == map.end());
} }
TEST(DenseMapCustomTest, TryEmplaceTest) {
DenseMap<int, std::unique_ptr<int>> Map;
std::unique_ptr<int> P(new int(2));
auto Try1 = Map.try_emplace(0, new int(1));
EXPECT_TRUE(Try1.second);
auto Try2 = Map.try_emplace(0, std::move(P));
EXPECT_FALSE(Try2.second);
EXPECT_EQ(Try1.first, Try2.first);
EXPECT_NE(nullptr, P);
}
} }