From 13cd5aa4ce0aa27fbd52d732e453a798688e7d14 Mon Sep 17 00:00:00 2001 From: LeonineKing1199 Date: Fri, 3 Dec 2021 15:10:59 -0800 Subject: [PATCH 1/3] Implement initial draft of heterogeneous `extract()` --- .../boost/unordered/detail/implementation.hpp | 12 +- include/boost/unordered/unordered_map.hpp | 15 ++- test/unordered/transparent_tests.cpp | 107 +++++++++++++++++- 3 files changed, 127 insertions(+), 7 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index f4874d57..e7bae279 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3655,14 +3655,15 @@ namespace boost { // Extract and erase - inline node_pointer extract_by_key(const_key_type& k) + template node_pointer extract_by_key_impl(Key const& k) { if (!this->size_) { return node_pointer(); } - std::size_t key_hash = this->hash(k); + std::size_t key_hash = policy::apply_hash(this->hash_function(), k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, bucket_index); + link_pointer prev = + this->find_previous_node_impl(this->key_eq(), k, bucket_index); if (!prev) { return node_pointer(); } @@ -3679,6 +3680,11 @@ namespace boost { return n; } + inline node_pointer extract_by_key(const_key_type& k) + { + return extract_by_key_impl(k); + } + // Reserve and rehash void reserve_for_insert(std::size_t); diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 1577797d..a41e1d9c 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -427,7 +427,20 @@ namespace boost { node_type extract(const key_type& k) { - return node_type(table_.extract_by_key(k), table_.node_alloc()); + return node_type(table_.extract_by_key_impl(k), table_.node_alloc()); + } + + template + typename boost::enable_if_c< + detail::is_transparent::value && + detail::is_transparent::value && + !boost::is_convertible::value && + !boost::is_convertible::value, + node_type>::type + extract(BOOST_FWD_REF(Key) k) + { + return node_type(table_.extract_by_key_impl(boost::forward(k)), + table_.node_alloc()); } insert_return_type insert(BOOST_RV_REF(node_type) np) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index bbcb240b..18cd7200 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -522,9 +522,9 @@ template void test_non_transparent_equal_range() template struct convertible_to_iterator { - operator typename UnorderedMap::iterator() + operator typename UnorderedMap::const_iterator() { - return typename UnorderedMap::iterator(); + return typename UnorderedMap::const_iterator(); } }; @@ -539,7 +539,7 @@ transparent_unordered_map::iterator erase_overload_compile_test() { convertible_to_iterator c; transparent_unordered_map map; - transparent_unordered_map::iterator pos = map.begin(); + transparent_unordered_map::const_iterator pos = map.begin(); pos = c; return map.erase(c); } @@ -620,6 +620,103 @@ template void test_non_transparent_erase() BOOST_TEST_EQ(key::count_, key_count); } +// test that in the presence of the member function template `extract()`, we still +// invoke the correct iterator overloads when the type is implicitly convertible +// +transparent_unordered_map::node_type extract_overload_compile_test() +{ + convertible_to_iterator c; + transparent_unordered_map map; + transparent_unordered_map::const_iterator pos = map.begin(); + pos = c; + return map.extract(c); +} + +template void test_transparent_extract() +{ + typedef typename UnorderedMap::node_type node_type; + typedef typename UnorderedMap::const_iterator const_iterator; + + count_reset(); + + UnorderedMap map; + + node_type nh = map.extract(0); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == 0); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST(map.size() == 3); + + int const expected_key_count = static_cast(2 * map.size()); + BOOST_TEST(key::count_ == expected_key_count); + + nh = map.extract(1); + BOOST_TEST(map.size() == 2); + BOOST_TEST(nh.key().x_ == 1); + BOOST_TEST(nh.mapped() == 1338); + + nh.mapped() = 1340; + + map.insert(boost::move(nh)); + + BOOST_TEST(map.size() == 3); + + const_iterator pos = map.find(1); + BOOST_TEST(pos != map.end()); + BOOST_TEST(pos->second == 1340); + + nh = map.extract(1337); + BOOST_TEST(nh.empty()); + + BOOST_TEST(key::count_ == expected_key_count); +} + +template void test_non_transparent_extract() +{ + typedef typename UnorderedMap::node_type node_type; + typedef typename UnorderedMap::const_iterator const_iterator; + + count_reset(); + + UnorderedMap map; + + node_type nh = map.extract(0); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == 1); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST(map.size() == 3); + + int key_count = 1 + static_cast(2 * map.size()); + BOOST_TEST(key::count_ == key_count); + + nh = map.extract(1); + BOOST_TEST(map.size() == 2); + BOOST_TEST(nh.key().x_ == 1); + BOOST_TEST(nh.mapped() == 1338); + BOOST_TEST(key::count_ == ++key_count); + + nh.mapped() = 1340; + + map.insert(boost::move(nh)); + + BOOST_TEST(map.size() == 3); + + const_iterator pos = map.find(1); + BOOST_TEST(pos != map.end()); + BOOST_TEST(pos->second == 1340); + BOOST_TEST(key::count_ == ++key_count); + + nh = map.extract(1337); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == ++key_count); +} + UNORDERED_AUTO_TEST (unordered_map_transparent_count) { { typedef boost::unordered_map(); test_transparent_equal_range(); test_transparent_erase(); + test_transparent_extract(); } { @@ -641,6 +739,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } { @@ -653,6 +752,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } { @@ -665,6 +765,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } } From 4041d06e95092af776ebddb8b9b3b5eefd798063 Mon Sep 17 00:00:00 2001 From: LeonineKing1199 Date: Mon, 6 Dec 2021 15:04:40 -0800 Subject: [PATCH 2/3] Clean up tests --- test/unordered/transparent_tests.cpp | 46 +++++++++++++++------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 18cd7200..50ec0ee3 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -620,8 +620,9 @@ template void test_non_transparent_erase() BOOST_TEST_EQ(key::count_, key_count); } -// test that in the presence of the member function template `extract()`, we still -// invoke the correct iterator overloads when the type is implicitly convertible +// test that in the presence of the member function template `extract()`, we +// still invoke the correct iterator overloads when the type is implicitly +// convertible // transparent_unordered_map::node_type extract_overload_compile_test() { @@ -643,20 +644,20 @@ template void test_transparent_extract() node_type nh = map.extract(0); BOOST_TEST(nh.empty()); - BOOST_TEST(key::count_ == 0); + BOOST_TEST_EQ(key::count_, 0); map[key(0)] = 1337; map[key(1)] = 1338; map[key(2)] = 1339; - BOOST_TEST(map.size() == 3); + BOOST_TEST_EQ(map.size(), 3); int const expected_key_count = static_cast(2 * map.size()); - BOOST_TEST(key::count_ == expected_key_count); + BOOST_TEST_EQ(key::count_, expected_key_count); nh = map.extract(1); - BOOST_TEST(map.size() == 2); - BOOST_TEST(nh.key().x_ == 1); - BOOST_TEST(nh.mapped() == 1338); + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST_EQ(nh.key().x_, 1); + BOOST_TEST_EQ(nh.mapped(), 1338); nh.mapped() = 1340; @@ -666,12 +667,12 @@ template void test_transparent_extract() const_iterator pos = map.find(1); BOOST_TEST(pos != map.end()); - BOOST_TEST(pos->second == 1340); + BOOST_TEST_EQ(pos->second, 1340); nh = map.extract(1337); BOOST_TEST(nh.empty()); - BOOST_TEST(key::count_ == expected_key_count); + BOOST_TEST_EQ(key::count_, expected_key_count); } template void test_non_transparent_extract() @@ -685,36 +686,39 @@ template void test_non_transparent_extract() node_type nh = map.extract(0); BOOST_TEST(nh.empty()); - BOOST_TEST(key::count_ == 1); + BOOST_TEST_EQ(key::count_, 1); map[key(0)] = 1337; map[key(1)] = 1338; map[key(2)] = 1339; - BOOST_TEST(map.size() == 3); + BOOST_TEST_EQ(map.size(), 3); int key_count = 1 + static_cast(2 * map.size()); - BOOST_TEST(key::count_ == key_count); + BOOST_TEST_EQ(key::count_, key_count); nh = map.extract(1); - BOOST_TEST(map.size() == 2); - BOOST_TEST(nh.key().x_ == 1); - BOOST_TEST(nh.mapped() == 1338); - BOOST_TEST(key::count_ == ++key_count); + ++key_count; + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST_EQ(nh.key().x_, 1); + BOOST_TEST_EQ(nh.mapped(), 1338); + BOOST_TEST_EQ(key::count_, key_count); nh.mapped() = 1340; map.insert(boost::move(nh)); - BOOST_TEST(map.size() == 3); + BOOST_TEST_EQ(map.size(), 3); const_iterator pos = map.find(1); + ++key_count; BOOST_TEST(pos != map.end()); - BOOST_TEST(pos->second == 1340); - BOOST_TEST(key::count_ == ++key_count); + BOOST_TEST_EQ(pos->second, 1340); + BOOST_TEST_EQ(key::count_, key_count); nh = map.extract(1337); + ++key_count; BOOST_TEST(nh.empty()); - BOOST_TEST(key::count_ == ++key_count); + BOOST_TEST_EQ(key::count_, key_count); } UNORDERED_AUTO_TEST (unordered_map_transparent_count) { From ea5cabb27f480d57f0f96037433e18c25f52d06b Mon Sep 17 00:00:00 2001 From: LeonineKing1199 Date: Tue, 7 Dec 2021 10:48:09 -0800 Subject: [PATCH 3/3] Update test to use two implicitly convertible types (const and non-const iterators) and the associated compile-time tests --- test/unordered/transparent_tests.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 50ec0ee3..b52f31d1 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -521,6 +521,14 @@ template void test_non_transparent_equal_range() } template struct convertible_to_iterator +{ + operator typename UnorderedMap::iterator() + { + return typename UnorderedMap::iterator(); + } +}; + +template struct convertible_to_const_iterator { operator typename UnorderedMap::const_iterator() { @@ -539,6 +547,15 @@ transparent_unordered_map::iterator erase_overload_compile_test() { convertible_to_iterator c; transparent_unordered_map map; + transparent_unordered_map::iterator pos = map.begin(); + pos = c; + return map.erase(c); +} + +transparent_unordered_map::const_iterator erase_const_overload_compile_test() +{ + convertible_to_const_iterator c; + transparent_unordered_map map; transparent_unordered_map::const_iterator pos = map.begin(); pos = c; return map.erase(c); @@ -624,9 +641,9 @@ template void test_non_transparent_erase() // still invoke the correct iterator overloads when the type is implicitly // convertible // -transparent_unordered_map::node_type extract_overload_compile_test() +transparent_unordered_map::node_type extract_const_overload_compile_test() { - convertible_to_iterator c; + convertible_to_const_iterator c; transparent_unordered_map map; transparent_unordered_map::const_iterator pos = map.begin(); pos = c;