diff --git a/doc/modules/ROOT/pages/changes.adoc b/doc/modules/ROOT/pages/changes.adoc index 71445750..5d0df423 100644 --- a/doc/modules/ROOT/pages/changes.adoc +++ b/doc/modules/ROOT/pages/changes.adoc @@ -6,6 +6,11 @@ :github-pr-url: https://github.com/boostorg/unordered/pull :cpp: C++ +== Release 1.89.0 + +* Added `pull(const_iterator)` to open-addressing containers. This operation +allows for efficient removal and retrieval of an element via move construction. + == Release 1.88.0 * Migrated the documentation to a multipage format using Antora. diff --git a/doc/modules/ROOT/pages/reference/unordered_flat_map.adoc b/doc/modules/ROOT/pages/reference/unordered_flat_map.adoc index 659f6d07..a0357a87 100644 --- a/doc/modules/ROOT/pages/reference/unordered_flat_map.adoc +++ b/doc/modules/ROOT/pages/reference/unordered_flat_map.adoc @@ -168,6 +168,7 @@ namespace unordered { void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); + init_type xref:#unordered_flat_map_pull[pull](const_iterator position); void xref:#unordered_flat_map_clear[clear]() noexcept; template @@ -1126,6 +1127,16 @@ Throws:;; Nothing unless `key_equal` or `hasher` throw on swapping. --- +==== pull +```c++ +init_type pull(const_iterator position); +``` + +Move-constructs an `init_value` `x` from the element pointed to by `position`, +erases the element and returns `x`. + +--- + ==== clear ```c++ void clear() noexcept; diff --git a/doc/modules/ROOT/pages/reference/unordered_flat_set.adoc b/doc/modules/ROOT/pages/reference/unordered_flat_set.adoc index 015bec5a..9d1e9892 100644 --- a/doc/modules/ROOT/pages/reference/unordered_flat_set.adoc +++ b/doc/modules/ROOT/pages/reference/unordered_flat_set.adoc @@ -135,6 +135,7 @@ namespace unordered { void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); + init_type xref:#unordered_flat_set_pull[pull](const_iterator position); void xref:#unordered_flat_set_clear[clear]() noexcept; template @@ -932,6 +933,16 @@ Throws:;; Nothing unless `key_equal` or `hasher` throw on swapping. --- +==== pull +```c++ +init_type pull(const_iterator position); +``` + +Move-constructs an `init_value` `x` from the element pointed to by `position`, +erases the element and returns `x`. + +--- + ==== clear ```c++ void clear() noexcept; diff --git a/doc/modules/ROOT/pages/reference/unordered_node_map.adoc b/doc/modules/ROOT/pages/reference/unordered_node_map.adoc index 5ec5c871..6cae4a4b 100644 --- a/doc/modules/ROOT/pages/reference/unordered_node_map.adoc +++ b/doc/modules/ROOT/pages/reference/unordered_node_map.adoc @@ -172,6 +172,7 @@ namespace unordered { node_type xref:#unordered_node_map_extract_by_position[extract](const_iterator position); node_type xref:#unordered_node_map_extract_by_key[extract](const key_type& key); template node_type xref:#unordered_node_map_extract_by_key[extract](K&& key); + init_type xref:#unordered_node_map_pull[pull](const_iterator position); void xref:#unordered_node_map_clear[clear]() noexcept; template @@ -1224,6 +1225,16 @@ Notes:;; The `template` overload only participates in overload resoluti --- +==== pull +```c++ +init_type pull(const_iterator position); +``` + +Move-constructs an `init_value` `x` from the element pointed to by `position`, +erases the element and returns `x`. + +--- + ==== clear ```c++ void clear() noexcept; diff --git a/doc/modules/ROOT/pages/reference/unordered_node_set.adoc b/doc/modules/ROOT/pages/reference/unordered_node_set.adoc index 7a9dd867..9af2fc67 100644 --- a/doc/modules/ROOT/pages/reference/unordered_node_set.adoc +++ b/doc/modules/ROOT/pages/reference/unordered_node_set.adoc @@ -140,6 +140,7 @@ namespace unordered { node_type xref:#unordered_node_set_extract_by_position[extract](const_iterator position); node_type xref:#unordered_node_set_extract_by_key[extract](const key_type& key); template node_type xref:#unordered_node_set_extract_by_key[extract](K&& key); + init_type xref:#unordered_node_set_pull[pull](const_iterator position); void xref:#unordered_node_set_clear[clear]() noexcept; template @@ -1034,6 +1035,16 @@ Notes:;; The `template` overload only participates in overload resoluti --- +==== pull +```c++ +init_type pull(const_iterator position); +``` + +Move-constructs an `init_value` `x` from the element pointed to by `position`, +erases the element and returns `x`. + +--- + ==== clear ```c++ void clear() noexcept; diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 529a514c..47540de8 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -1,6 +1,6 @@ /* Fast open-addressing hash table. * - * Copyright 2022-2024 Joaquin M Lopez Munoz. + * Copyright 2022-2025 Joaquin M Lopez Munoz. * Copyright 2023 Christian Mazakas. * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. @@ -495,6 +495,14 @@ public: else return 0; } + BOOST_FORCEINLINE init_type pull(const_iterator pos) + { + BOOST_ASSERT(pos!=end()); + erase_on_exit e{*this,pos}; + (void)e; + return type_policy::move(type_policy::value_from(*pos.p())); + } + void swap(table& x) noexcept(noexcept(std::declval().swap(std::declval()))) { diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 6ad25d8e..56ec1261 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024 Joaquin M Lopez Munoz +// Copyright (C) 2024-2025 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -438,6 +438,11 @@ namespace boost { return table_.erase(key); } + BOOST_FORCEINLINE init_type pull(const_iterator pos) + { + return table_.pull(pos); + } + void swap(unordered_flat_map& rhs) noexcept( noexcept(std::declval().swap(std::declval()))) { diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index 8e925931..b06a0d77 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024 Joaquin M Lopez Munoz +// Copyright (C) 2024-2025 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -332,6 +332,11 @@ namespace boost { return table_.erase(key); } + BOOST_FORCEINLINE init_type pull(const_iterator pos) + { + return table_.pull(pos); + } + void swap(unordered_flat_set& rhs) noexcept( noexcept(std::declval().swap(std::declval()))) { diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 6f644dc0..a9d7ef44 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024 Joaquin M Lopez Munoz +// Copyright (C) 2024-2025 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -483,6 +483,11 @@ namespace boost { return table_.erase(key); } + BOOST_FORCEINLINE init_type pull(const_iterator pos) + { + return table_.pull(pos); + } + void swap(unordered_node_map& rhs) noexcept( noexcept(std::declval().swap(std::declval()))) { diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index 537f305e..1ac71b41 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024 Joaquin M Lopez Munoz +// Copyright (C) 2024-2025 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -379,6 +379,11 @@ namespace boost { return table_.erase(key); } + BOOST_FORCEINLINE init_type pull(const_iterator pos) + { + return table_.pull(pos); + } + void swap(unordered_node_set& rhs) noexcept( noexcept(std::declval().swap(std::declval()))) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7347173d..902faf6e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -123,6 +123,7 @@ foa_tests(SOURCES unordered/uses_allocator.cpp) foa_tests(SOURCES unordered/link_test_1.cpp unordered/link_test_2.cpp ) foa_tests(SOURCES unordered/scoped_allocator.cpp) foa_tests(SOURCES unordered/hash_is_avalanching_test.cpp) +foa_tests(SOURCES unordered/pull_tests.cpp) foa_tests(SOURCES exception/constructor_exception_tests.cpp) foa_tests(SOURCES exception/copy_exception_tests.cpp) foa_tests(SOURCES exception/assign_exception_tests.cpp) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ea3adea9..118300ca 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,7 +1,7 @@ # Copyright 2006-2008 Daniel James. # Copyright 2022-2023 Christian Mazakas -# Copyright 2024 Joaquin M Lopez Munoz +# Copyright 2024-2025 Joaquin M Lopez Munoz # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -241,6 +241,7 @@ local FOA_TESTS = hash_is_avalanching_test fancy_pointer_noleak pmr_allocator_tests + pull_tests stats_tests node_handle_allocator_tests ; diff --git a/test/unordered/pull_tests.cpp b/test/unordered/pull_tests.cpp new file mode 100644 index 00000000..a2706a2c --- /dev/null +++ b/test/unordered/pull_tests.cpp @@ -0,0 +1,88 @@ +// Copyright 2025 Joaquin M Lopez Munoz. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include "../helpers/test.hpp" +#include "../helpers/unordered.hpp" +#include +#include + +struct move_only_type +{ + move_only_type(int n_): n{n_} {} + move_only_type(move_only_type&&) = default; + move_only_type(const move_only_type&) = delete; + + move_only_type& operator=(move_only_type&&) = default; + + int n; +}; + +bool operator==(const move_only_type& x, const move_only_type& y) +{ + return x.n == y.n; +} + +bool operator<(const move_only_type& x, const move_only_type& y) +{ + return x.n < y.n; +} + +std::size_t hash_value(const move_only_type& x) +{ + return boost::hash()(x.n); +} + +template +struct from_int +{ + T operator()(int n) const { return T(n); } +}; + +template +struct from_int > +{ + std::pair operator()(int n) const { return {n, -n}; } +}; + +template void test_pull() +{ + Container c; + using init_type = typename Container::init_type; + + std::vector l1; + from_int fi; + for(int i = 0; i < 1000; ++i ){ + l1.push_back(fi(i)); + c.insert(fi(i)); + } + + std::vector l2; + for(auto first = c.cbegin(), last = c.cend(); first != last; ) + { + l2.push_back(c.pull(first++)); + } + BOOST_TEST(c.empty()); + + std::sort(l1.begin(), l1.end()); + std::sort(l2.begin(), l2.end()); + BOOST_TEST(l1 == l2); +} + +UNORDERED_AUTO_TEST (pull_) { +#if defined(BOOST_UNORDERED_FOA_TESTS) + test_pull< + boost::unordered_flat_map >(); + test_pull< + boost::unordered_flat_set >(); + test_pull< + boost::unordered_node_map >(); + test_pull< + boost::unordered_node_set >(); +#else + // Closed-addressing containers do not provide pull +#endif +} + +RUN_TESTS()