diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 9f561ec0..057e47c2 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -82,6 +82,15 @@ namespace boost{ namespace unordered{ namespace detail{ + +template +struct insert_return_type +{ + Iterator position; + bool inserted; + NodeType node; +}; + namespace foa{ static const std::size_t default_bucket_count = 0; diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 9d046861..98d24e38 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -56,6 +55,11 @@ namespace boost { */ element_type() = default; element_type(element_type const&) = delete; + element_type(element_type&& rhs) noexcept + { + p = rhs.p; + rhs.p = nullptr; + } }; static value_type& value_from(element_type const& x) { return *(x.p); } @@ -122,7 +126,7 @@ namespace boost { } }; - template struct node_handle + template struct node_map_handle { private: using type_policy = NodeMapTypes; @@ -132,7 +136,6 @@ namespace boost { friend class boost::unordered::unordered_node_map; public: - using value_type = typename NodeMapTypes::value_type; using key_type = typename NodeMapTypes::key_type; using mapped_type = typename NodeMapTypes::mapped_type; using allocator_type = Allocator; @@ -167,24 +170,22 @@ namespace boost { } public: - constexpr node_handle() noexcept = default; + constexpr node_map_handle() noexcept = default; - node_handle(node_handle&& nh) noexcept + node_map_handle(node_map_handle&& nh) noexcept { // neither of these move constructors are allowed to throw exceptions // so we can get away with rote placement new // new (a) Allocator(std::move(nh.al())); - type_policy::construct(al(), reinterpret_cast(x), - type_policy::move(nh.element())); - + new (x) element_type(std::move(nh.element())); empty_ = false; reinterpret_cast(nh.a)->~Allocator(); nh.empty_ = true; } - ~node_handle() + ~node_map_handle() { if (!empty()) { type_policy::destroy(al(), reinterpret_cast(x)); @@ -208,13 +209,6 @@ namespace boost { explicit operator bool() const noexcept { return !empty(); } BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept { return empty_; } }; - - template struct insert_return_type - { - Iterator position; - bool inserted; - NodeType node; - }; } // namespace detail template @@ -249,7 +243,9 @@ namespace boost { typename boost::allocator_const_pointer::type; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; - using node_type = detail::node_handle; + using node_type = detail::node_map_handle::type>; using insert_return_type = detail::insert_return_type; @@ -457,6 +453,18 @@ namespace boost { } } + iterator insert(const_iterator, node_type&& nh) + { + if (nh.empty()) { + return end(); + } + + BOOST_ASSERT(get_allocator() == nh.get_allocator()); + + auto itp = table_.emplace_impl(map_types::move(nh.element())); + return itp.first; + } + template std::pair insert_or_assign(key_type const& key, M&& obj) { diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index 01d26258..50cd6e5a 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -51,6 +51,11 @@ namespace boost { */ element_type() = default; element_type(element_type const&) = delete; + element_type(element_type&& rhs) noexcept + { + p = rhs.p; + rhs.p = nullptr; + } }; static value_type& value_from(element_type const& x) { return *x.p; } @@ -102,6 +107,84 @@ namespace boost { } } }; + + template struct node_set_handle + { + private: + using type_policy = NodeSetTypes; + using element_type = typename type_policy::element_type; + + template + friend class boost::unordered::unordered_node_set; + + public: + using value_type = typename NodeSetTypes::value_type; + using allocator_type = Allocator; + + private: + alignas(element_type) unsigned char x[sizeof(element_type)]; + alignas(Allocator) unsigned char a[sizeof(Allocator)]; + bool empty_; + + element_type& element() noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(x); + } + + element_type const& element() const noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(x); + } + + Allocator& al() noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(a); + } + + Allocator const& al() const noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(a); + } + + public: + constexpr node_set_handle() noexcept = default; + + node_set_handle(node_set_handle&& nh) noexcept + { + // neither of these move constructors are allowed to throw exceptions + // so we can get away with rote placement new + // + new (a) Allocator(std::move(nh.al())); + new (x) element_type(std::move(nh.element())); + empty_ = false; + + reinterpret_cast(nh.a)->~Allocator(); + nh.empty_ = true; + } + + ~node_set_handle() + { + if (!empty()) { + type_policy::destroy(al(), reinterpret_cast(x)); + reinterpret_cast(a)->~Allocator(); + empty_ = true; + } + } + + value_type& value() const + { + BOOST_ASSERT(!empty()); + return const_cast(type_policy::extract(element())); + } + + allocator_type get_allocator() const noexcept { return al(); } + explicit operator bool() const noexcept { return !empty(); } + BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept { return empty_; } + }; } // namespace detail template @@ -135,6 +218,11 @@ namespace boost { typename boost::allocator_const_pointer::type; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; + using node_type = detail::node_set_handle::type>; + using insert_return_type = + detail::insert_return_type; unordered_node_set() : unordered_node_set(0) {} @@ -339,6 +427,34 @@ namespace boost { this->insert(ilist.begin(), ilist.end()); } + insert_return_type insert(node_type&& nh) + { + if (nh.empty()) { + return {end(), false, node_type{}}; + } + + BOOST_ASSERT(get_allocator() == nh.get_allocator()); + + auto itp = table_.emplace_impl(set_types::move(nh.element())); + if (itp.second) { + return {itp.first, true, node_type{}}; + } else { + return {itp.first, false, std::move(nh)}; + } + } + + iterator insert(const_iterator, node_type&& nh) + { + if (nh.empty()) { + return end(); + } + + BOOST_ASSERT(get_allocator() == nh.get_allocator()); + + auto itp = table_.emplace_impl(set_types::move(nh.element())); + return itp.first; + } + template BOOST_FORCEINLINE std::pair emplace(Args&&... args) { @@ -383,6 +499,52 @@ namespace boost { table_.swap(rhs.table_); } + node_type extract(const_iterator pos) + { + BOOST_ASSERT(pos != end()); + node_type nh; + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + return nh; + } + + node_type extract(key_type const& key) + { + auto pos = find(key); + node_type nh; + if (pos != end()) { + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + } else { + nh.empty_ = true; + } + return nh; + } + + template + typename std::enable_if< + boost::unordered::detail::transparent_non_iterable::value, + node_type>::type + extract(K const& key) + { + auto pos = find(key); + node_type nh; + if (pos != end()) { + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + } else { + nh.empty_ = true; + } + return nh; + } + template void merge(unordered_node_set& source) { diff --git a/test/unordered/extract_tests.cpp b/test/unordered/extract_tests.cpp index 1cfdadb0..3c505420 100644 --- a/test/unordered/extract_tests.cpp +++ b/test/unordered/extract_tests.cpp @@ -4,7 +4,6 @@ // 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/unordered.hpp" #include "../helpers/equivalent.hpp" @@ -115,12 +114,13 @@ namespace extract_tests { #ifdef BOOST_UNORDERED_FOA_TESTS boost::unordered_node_map > >* - test_node_map; + test::equal_to, test::allocator1 >* test_node_map; - UNORDERED_TEST( - extract_tests1, ((test_node_map))((default_generator)(generate_collisions))) + boost::unordered_node_set >* test_node_set; + + UNORDERED_TEST(extract_tests1, + ((test_node_map)(test_node_set))((default_generator)(generate_collisions))) #else boost::unordered_set >* test_set; diff --git a/test/unordered/node_handle_tests.cpp b/test/unordered/node_handle_tests.cpp index 3f48ed42..3ecf0d04 100644 --- a/test/unordered/node_handle_tests.cpp +++ b/test/unordered/node_handle_tests.cpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include "../helpers/postfix.hpp" -#include "../helpers/unordered.hpp" #include "../helpers/prefix.hpp" +#include "../helpers/unordered.hpp" #include "../helpers/helpers.hpp" #include "../helpers/metafunctions.hpp" @@ -17,42 +17,19 @@ #include #include -#ifdef BOOST_UNORDERED_FOA_TESTS -UNORDERED_AUTO_TEST (example1_5) { - typedef boost::unordered_node_map::insert_return_type - insert_return_type; +template