From df03ffc8ec2abad69535dfc5fd77fb221bbdb155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Fri, 28 Jun 2024 14:04:00 +0200 Subject: [PATCH] Fixes #241: flat_map should support same interface as std::map --- doc/container.qbk | 1 + include/boost/container/detail/flat_tree.hpp | 37 +++++++++++++++++-- .../container/detail/node_alloc_holder.hpp | 13 +++++++ include/boost/container/detail/tree.hpp | 25 +++++++++++++ include/boost/container/flat_map.hpp | 34 +++++++++++++++++ include/boost/container/map.hpp | 17 +++++++++ test/flat_map_test.cpp | 9 +++++ test/map_test.cpp | 9 +++++ 8 files changed, 142 insertions(+), 3 deletions(-) diff --git a/doc/container.qbk b/doc/container.qbk index 1581b37..4786cd2 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -1421,6 +1421,7 @@ use [*Boost.Container]? There are several reasons for that: * [@https://github.com/boostorg/container/issues/266 GitHub #266: ['"small_vector is misaligned on the stack in 32 bits"]]. * [@https://github.com/boostorg/container/issues/259 GitHub #259: ['"Global variables"]]. * [@https://github.com/boostorg/container/issues/245 GitHub #245: ['"flat_tree::insert ordered range doesn't assert sorting"]]. + * [@https://github.com/boostorg/container/issues/241 GitHub #241: ['"flat_map should support same interface as std::map"]]. [endsect] diff --git a/include/boost/container/detail/flat_tree.hpp b/include/boost/container/detail/flat_tree.hpp index b356e94..98bd702 100644 --- a/include/boost/container/detail/flat_tree.hpp +++ b/include/boost/container/detail/flat_tree.hpp @@ -1127,9 +1127,6 @@ class flat_tree return ret; } - inline iterator erase(const_iterator position) - { return this->m_data.m_seq.erase(position); } - size_type erase(const key_type& k) { std::pair itp = this->equal_range(k); @@ -1149,6 +1146,40 @@ class flat_tree return ret; } + template + inline typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + , size_type>::type + erase(const K& k) + { + std::pair itp = this->equal_range(k); + size_type ret = static_cast(itp.second - itp.first); + if (ret) { + this->m_data.m_seq.erase(itp.first, itp.second); + } + return ret; + } + + template + inline typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + , size_type>::type + erase_unique(const K& k) + { + const_iterator i = static_cast(*this).find(k); + size_type ret = static_cast(i != this->cend()); + if (ret) + this->erase(i); + return ret; + } + + inline iterator erase(const_iterator position) + { return this->m_data.m_seq.erase(position); } + inline iterator erase(const_iterator first, const_iterator last) { return this->m_data.m_seq.erase(first, last); } diff --git a/include/boost/container/detail/node_alloc_holder.hpp b/include/boost/container/detail/node_alloc_holder.hpp index 96680dd..32023e3 100644 --- a/include/boost/container/detail/node_alloc_holder.hpp +++ b/include/boost/container/detail/node_alloc_holder.hpp @@ -536,6 +536,19 @@ struct node_alloc_holder return this->icont().erase_and_dispose(k, chain_holder.get_chain_builder()); } + template + inline size_type erase_key(const Key& k, KeyCompare cmp, version_1) + { + return this->icont().erase_and_dispose(k, cmp, Destroyer(this->node_alloc())); + } + + template + inline size_type erase_key(const Key& k, KeyCompare cmp, version_2) + { + allocator_multialloc_chain_node_deallocator chain_holder(this->node_alloc()); + return this->icont().erase_and_dispose(k, cmp, chain_holder.get_chain_builder()); + } + protected: struct cloner { diff --git a/include/boost/container/detail/tree.hpp b/include/boost/container/detail/tree.hpp index a189651..1a3aa42 100644 --- a/include/boost/container/detail/tree.hpp +++ b/include/boost/container/detail/tree.hpp @@ -1142,6 +1142,31 @@ class tree return ret; } + template + inline typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + , size_type>::type + erase(const K& k) + { return AllocHolder::erase_key(k, KeyNodeCompare(key_comp()), alloc_version()); } + + template + inline typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + , size_type>::type + erase_unique(const K& k) + { + iterator i = this->find(k); + size_type ret = static_cast(i != this->end()); + + if (ret) + this->erase(i); + return ret; + } + iterator erase(const_iterator first, const_iterator last) { BOOST_ASSERT(first == last || (first != this->cend() && (priv_is_linked)(first))); diff --git a/include/boost/container/flat_map.hpp b/include/boost/container/flat_map.hpp index 9a8abb8..cf6d650 100644 --- a/include/boost/container/flat_map.hpp +++ b/include/boost/container/flat_map.hpp @@ -1315,6 +1315,23 @@ class flat_map inline size_type erase(const key_type& x) { return m_flat_tree.erase_unique(x); } + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Effects: If present, erases the element in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements (0/1). + template + inline BOOST_CONTAINER_DOC1ST + (size_type + , typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + BOOST_MOVE_I size_type>::type) + erase(const K& x) + { return m_flat_tree.erase_unique(x); } + //! Effects: Erases all the elements in the range [first, last). //! //! Returns: Returns last. @@ -2678,6 +2695,23 @@ class flat_multimap inline size_type erase(const key_type& x) { return m_flat_tree.erase(x); } + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Effects: Erases all elements in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements. + template + inline BOOST_CONTAINER_DOC1ST + (size_type + , typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + BOOST_MOVE_I size_type>::type) + erase(const K& x) + { return m_flat_tree.erase(x); } + //! Effects: Erases all the elements in the range [first, last). //! //! Returns: Returns last. diff --git a/include/boost/container/map.hpp b/include/boost/container/map.hpp index 69c0526..cb13c03 100644 --- a/include/boost/container/map.hpp +++ b/include/boost/container/map.hpp @@ -969,6 +969,23 @@ class map inline size_type erase(const key_type& x) { return this->base_t::erase_unique(x); } + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Effects: If present, erases the element in the container with key equivalent to x. + //! + //! Returns: Returns the number of erased elements (0/1). + template + inline BOOST_CONTAINER_DOC1ST + (size_type + , typename dtl::enable_if_c< + dtl::is_transparent::value && //transparent + !dtl::is_convertible::value && //not convertible to iterator + !dtl::is_convertible::value //not convertible to const_iterator + BOOST_MOVE_I size_type>::type) + erase(const K& x) + { return this->base_t::erase_unique(x); } + #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) //! Effects: Erases the element pointed to by p. diff --git a/test/flat_map_test.cpp b/test/flat_map_test.cpp index 84f5b3e..49d88f8 100644 --- a/test/flat_map_test.cpp +++ b/test/flat_map_test.cpp @@ -560,6 +560,15 @@ bool test_heterogeneous_lookups() if(cmmap1.equal_range(find_me).second->second != 'e') return false; + //erase + if (map1.erase(find_me) != 1) + return false; + if (map1.erase(find_me) != 0) + return false; + if (mmap1.erase(find_me) != 2) + return false; + if (mmap1.erase(find_me) != 0) + return false; return true; } diff --git a/test/map_test.cpp b/test/map_test.cpp index dd13491..4a3fddb 100644 --- a/test/map_test.cpp +++ b/test/map_test.cpp @@ -361,6 +361,15 @@ bool test_heterogeneous_lookups() if(cmmap1.equal_range(find_me).second->second != 'e') return false; + //erase + if (map1.erase(find_me) != 1) + return false; + if (map1.erase(find_me) != 0) + return false; + if (mmap1.erase(find_me) != 2) + return false; + if (mmap1.erase(find_me) != 0) + return false; return true; }