From 22cbffee3e95d7fe4763c411c630195248f33e8b Mon Sep 17 00:00:00 2001 From: Til Date: Wed, 18 May 2022 17:01:58 +0200 Subject: [PATCH 1/2] add try_emplace --- .github/workflows/bazel.yml | 4 +++- .github/workflows/cmake.yml | 4 +++- CHANGELOG.md | 2 ++ phtree/phtree.h | 16 +++++++++++++++ phtree/phtree_test.cc | 39 +++++++++++++++++++++++++++++++++++-- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 030eaaea..4f3bc274 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -1,6 +1,8 @@ name: Bazel build -on: [push, pull_request] +on: + push: {} + pull_request: {} jobs: build: diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 22599941..c1cac462 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -1,6 +1,8 @@ name: CMake build -on: [push, pull_request] +on: + push: {} + pull_request: {} env: BUILD_TYPE: Release diff --git a/CHANGELOG.md b/CHANGELOG.md index ee34068f..b89754a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added +- Added try_emplace(key, value) and try_emplace(iter_hint, key, value) + [#40](https://github.com/tzaeschke/phtree-cpp/issues/40) - Added FilterBoxAABB and FilterSphereAABB as examples for filtering a PH-Tree with box keys [#33](https://github.com/tzaeschke/phtree-cpp/issues/33) ### Changed diff --git a/phtree/phtree.h b/phtree/phtree.h index 44194f7c..f05ed7d4 100644 --- a/phtree/phtree.h +++ b/phtree/phtree.h @@ -92,6 +92,22 @@ class PhTree { return tree_.emplace_hint(iterator, converter_.pre(key), std::forward(args)...); } + /* + * See emplace(). + */ + template + std::pair try_emplace(const Key& key, Args&&... args) { + return tree_.emplace(converter_.pre(key), std::forward(args)...); + } + + /* + * See emplace_hint(). + */ + template + std::pair try_emplace(const ITERATOR& iterator, const Key& key, Args&&... args) { + return tree_.emplace_hint(iterator, converter_.pre(key), std::forward(args)...); + } + /* * See std::map::insert(). * diff --git a/phtree/phtree_test.cc b/phtree/phtree_test.cc index b70116b1..6ab13614 100644 --- a/phtree/phtree_test.cc +++ b/phtree/phtree_test.cc @@ -184,10 +184,12 @@ void SmokeTestBasicOps(size_t N) { ASSERT_EQ(tree.end(), tree.find(p)); Id id(i); - if (i % 2 == 0) { + if (i % 4 == 0) { ASSERT_TRUE(tree.emplace(p, i).second); - } else { + } else if (i % 4 == 1) { ASSERT_TRUE(tree.insert(p, id).second); + } else { + ASSERT_TRUE(tree.try_emplace(p, i).second); } ASSERT_EQ(tree.count(p), 1); ASSERT_NE(tree.end(), tree.find(p)); @@ -567,6 +569,39 @@ TEST(PhTreeTest, TestUpdateWithEmplaceHint) { ASSERT_EQ(2, tree.size()); } +TEST(PhTreeTest, TestUpdateWithTryEmplaceHint) { + const dimension_t dim = 3; + TestTree tree; + size_t N = 10000; + std::array deltas{0, 1, 10, 100}; + std::vector> points; + populate(tree, points, N); + + size_t d_n = 0; + for (auto& p : points) { + auto pOld = p; + d_n = (d_n + 1) % deltas.size(); + int delta = deltas[d_n]; + TestPoint pNew{pOld[0] + delta, pOld[1] + delta, pOld[2] + delta}; + auto iter = tree.find(pOld); + int n = tree.erase(iter); + ASSERT_EQ(1, n); + tree.try_emplace(iter, pNew, 42); + ASSERT_EQ(1, tree.count(pNew)); + if (delta != 0.0) { + ASSERT_EQ(0, tree.count(pOld)); + } + p = pNew; + } + + ASSERT_EQ(N, tree.size()); + tree.clear(); + + tree.try_emplace(tree.end(), {11, 21, 31}, 421); + tree.try_emplace(tree.begin(), {1, 2, 3}, 42); + ASSERT_EQ(2, tree.size()); +} + TEST(PhTreeTest, TestEraseByIterator) { const dimension_t dim = 3; TestTree tree; From 347150591c6ebbe6f42d016959be186b09f641aa Mon Sep 17 00:00:00 2001 From: Til Date: Wed, 18 May 2022 18:56:02 +0200 Subject: [PATCH 2/2] add try_emplace --- .github/workflows/bazel.yml | 4 +--- .github/workflows/cmake.yml | 4 +--- README.md | 2 ++ phtree/phtree.h | 28 ++++++++++++++-------------- phtree/phtree_multimap.h | 22 +++++++++++++++++++--- phtree/phtree_multimap_d_test.cc | 6 ++++-- phtree/v16/phtree_v16.h | 22 +++++++++++----------- 7 files changed, 52 insertions(+), 36 deletions(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 4f3bc274..030eaaea 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -1,8 +1,6 @@ name: Bazel build -on: - push: {} - pull_request: {} +on: [push, pull_request] jobs: build: diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index c1cac462..22599941 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -1,8 +1,6 @@ name: CMake build -on: - push: {} - pull_request: {} +on: [push, pull_request] env: BUILD_TYPE: Release diff --git a/README.md b/README.md index bd7f27af..dd9c515b 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ PhPointD<3> p{1.1, 1.0, 10.}; // Some operations tree.emplace(p, my_data); tree.emplace_hint(hint, p, my_data); +tree.try_emplace(p, my_data); +tree.try_emplace(hint, p, my_data); tree.insert(p, my_data); tree[p] = my_data; tree.count(p); diff --git a/phtree/phtree.h b/phtree/phtree.h index f05ed7d4..db6d1661 100644 --- a/phtree/phtree.h +++ b/phtree/phtree.h @@ -69,7 +69,7 @@ class PhTree { */ template std::pair emplace(const Key& key, Args&&... args) { - return tree_.emplace(converter_.pre(key), std::forward(args)...); + return tree_.try_emplace(converter_.pre(key), std::forward(args)...); } /* @@ -89,7 +89,17 @@ class PhTree { */ template std::pair emplace_hint(const ITERATOR& iterator, const Key& key, Args&&... args) { - return tree_.emplace_hint(iterator, converter_.pre(key), std::forward(args)...); + return tree_.try_emplace(iterator, converter_.pre(key), std::forward(args)...); + } + + /* + * See std::map::insert(). + * + * @return a pair consisting of the inserted element (or to the element that prevented the + * insertion) and a bool denoting whether the insertion took place. + */ + std::pair insert(const Key& key, const T& value) { + return tree_.insert(converter_.pre(key), value); } /* @@ -97,7 +107,7 @@ class PhTree { */ template std::pair try_emplace(const Key& key, Args&&... args) { - return tree_.emplace(converter_.pre(key), std::forward(args)...); + return tree_.try_emplace(converter_.pre(key), std::forward(args)...); } /* @@ -105,17 +115,7 @@ class PhTree { */ template std::pair try_emplace(const ITERATOR& iterator, const Key& key, Args&&... args) { - return tree_.emplace_hint(iterator, converter_.pre(key), std::forward(args)...); - } - - /* - * See std::map::insert(). - * - * @return a pair consisting of the inserted element (or to the element that prevented the - * insertion) and a bool denoting whether the insertion took place. - */ - std::pair insert(const Key& key, const T& value) { - return tree_.insert(converter_.pre(key), value); + return tree_.try_emplace(iterator, converter_.pre(key), std::forward(args)...); } /* diff --git a/phtree/phtree_multimap.h b/phtree/phtree_multimap.h index d79e272e..bf62222e 100644 --- a/phtree/phtree_multimap.h +++ b/phtree/phtree_multimap.h @@ -229,7 +229,7 @@ class PhTreeMultiMap { */ template std::pair emplace(const Key& key, Args&&... args) { - auto& outer_iter = tree_.emplace(converter_.pre(key)).first; + auto& outer_iter = tree_.try_emplace(converter_.pre(key)).first; auto bucket_iter = outer_iter.emplace(std::forward(args)...); size_ += bucket_iter.second ? 1 : 0; return {const_cast(*bucket_iter.first), bucket_iter.second}; @@ -252,7 +252,7 @@ class PhTreeMultiMap { */ template std::pair emplace_hint(const ITERATOR& iterator, const Key& key, Args&&... args) { - auto result_ph = tree_.emplace_hint(iterator.GetIteratorOfPhTree(), converter_.pre(key)); + auto result_ph = tree_.try_emplace(iterator.GetIteratorOfPhTree(), converter_.pre(key)); auto& bucket = result_ph.first; if (result_ph.second) { // new bucket @@ -281,6 +281,22 @@ class PhTreeMultiMap { return emplace(key, value); } + /* + * See emplace(). + */ + template + std::pair try_emplace(const Key& key, Args&&... args) { + return emplace(key, std::forward(args)...); + } + + /* + * See emplace_hint(). + */ + template + std::pair try_emplace(const ITERATOR& iterator, const Key& key, Args&&... args) { + return emplace_hint(iterator, key, std::forward(args)...); + } + /* * @return '1', if a value is associated with the provided key, otherwise '0'. */ @@ -405,7 +421,7 @@ class PhTreeMultiMap { const Key& old_key, const Key& new_key, const T& value, bool always_erase = false) { // Be smart: insert first, if the target-map already contains the entry we can avoid erase() auto new_key_pre = converter_.pre(new_key); - auto& new_bucket = tree_.emplace(new_key_pre).first; + auto& new_bucket = tree_.try_emplace(new_key_pre).first; auto new_result = new_bucket.emplace(value); if (!new_result.second) { // Entry is already in correct place -> abort diff --git a/phtree/phtree_multimap_d_test.cc b/phtree/phtree_multimap_d_test.cc index ea496f8f..2980a182 100644 --- a/phtree/phtree_multimap_d_test.cc +++ b/phtree/phtree_multimap_d_test.cc @@ -152,10 +152,12 @@ void SmokeTestBasicOps(size_t N) { } Id id(i); - if (i % 2 == 0) { + if (i % 4 == 0) { ASSERT_TRUE(tree.emplace(p, id).second); - } else { + } else if (i % 4 == 0) { ASSERT_TRUE(tree.insert(p, id).second); + } else{ + ASSERT_TRUE(tree.try_emplace(p, id).second); } ASSERT_EQ(tree.count(p), i % NUM_DUPL + 1); ASSERT_NE(tree.end(), tree.find(p)); diff --git a/phtree/v16/phtree_v16.h b/phtree/v16/phtree_v16.h index fb95ce0f..6da848a8 100644 --- a/phtree/v16/phtree_v16.h +++ b/phtree/v16/phtree_v16.h @@ -92,7 +92,7 @@ class PhTreeV16 { * entry instead of inserting a new one. */ template - std::pair emplace(const KeyT& key, Args&&... args) { + std::pair try_emplace(const KeyT& key, Args&&... args) { auto* current_entry = &root_; bool is_inserted = false; while (current_entry->IsNode()) { @@ -104,7 +104,7 @@ class PhTreeV16 { } /* - * The emplace_hint() method uses an iterator as hint for insertion. + * The try_emplace(hint, key, value) method uses an iterator as hint for insertion. * The hint is ignored if it is not useful or is equal to end(). * * Iterators should normally not be used after the tree has been modified. As an exception to @@ -116,12 +116,12 @@ class PhTreeV16 { * auto iter = tree.find(key1); * auto value = iter.second(); // The value may become invalid in erase() * erase(iter); - * emplace_hint(iter, key2, value); // the iterator can still be used as hint here + * try_emplace(iter, key2, value); // the iterator can still be used as hint here */ template - std::pair emplace_hint(const ITERATOR& iterator, const KeyT& key, Args&&... args) { + std::pair try_emplace(const ITERATOR& iterator, const KeyT& key, Args&&... args) { if constexpr (!std::is_same_v>) { - return emplace(key, std::forward(args)...); + return try_emplace(key, std::forward(args)...); } else { // This function can be used to insert a value close to a known value // or close to a recently removed value. The hint can only be used if the new key is @@ -129,20 +129,20 @@ class PhTreeV16 { // The idea behind using the 'parent' is twofold: // - The 'parent' node is one level above the iterator position, it is spatially // larger and has a better probability of containing the new position, allowing for - // fast track emplace. + // fast track try_emplace. // - Using 'parent' allows a scenario where the iterator was previously used with // erase(iterator). This is safe because erase() will never erase the 'parent' node. if (!iterator.GetParentNodeEntry()) { - // No hint available, use standard emplace() - return emplace(key, std::forward(args)...); + // No hint available, use standard try_emplace() + return try_emplace(key, std::forward(args)...); } auto* parent_entry = iterator.GetParentNodeEntry(); if (NumberOfDivergingBits(key, parent_entry->GetKey()) > parent_entry->GetNodePostfixLen() + 1) { // replace higher up in the tree - return emplace(key, std::forward(args)...); + return try_emplace(key, std::forward(args)...); } // replace in node @@ -167,7 +167,7 @@ class PhTreeV16 { * insertion) and a bool denoting whether the insertion took place. */ std::pair insert(const KeyT& key, const T& value) { - return emplace(key, value); + return try_emplace(key, value); } /* @@ -175,7 +175,7 @@ class PhTreeV16 { * and returned. */ T& operator[](const KeyT& key) { - return emplace(key).first; + return try_emplace(key).first; } /*