diff --git a/include/boost/unordered/detail/allocate.hpp b/include/boost/unordered/detail/allocate.hpp index 4c20b168..733d8e4c 100644 --- a/include/boost/unordered/detail/allocate.hpp +++ b/include/boost/unordered/detail/allocate.hpp @@ -808,6 +808,18 @@ namespace boost { namespace unordered { namespace detail { namespace func { # endif #else + template + inline void call_construct(Alloc&, T* address) + { + new ((void*) address) T(); + } + + template + inline void call_construct(Alloc&, T* address, + BOOST_FWD_REF(A0) a0) + { + new ((void*) address) T(boost::forward(a0)); + } template inline void destroy_value_impl(Alloc&, T* x) { @@ -1049,6 +1061,46 @@ BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(10, boost::) #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES + // Some nicer construct_value functions, might try to + // improve implementation later. + + template + inline typename AllocAndPointer::node_pointer construct_value_generic(AllocAndPointer& a, BOOST_UNORDERED_EMPLACE_ARGS) { + construct_value_impl(a.alloc_, a.node_->value_ptr(), + BOOST_UNORDERED_EMPLACE_FORWARD); + return a.release(); + } + + template + inline typename AllocAndPointer::node_pointer construct_value(AllocAndPointer& a, BOOST_FWD_REF(U) x) { + boost::unordered::detail::func::call_construct( + a.alloc_, a.node_->value_ptr(), boost::forward(x)); + return a.release(); + } + + // TODO: When possible, it might be better to use std::pair's + // constructor for std::piece_construct with std::tuple. + template + inline typename AllocAndPointer::node_pointer construct_pair(AllocAndPointer& a, BOOST_FWD_REF(Key) k) { + boost::unordered::detail::func::call_construct( + a.alloc_, boost::addressof(a.node_->value_ptr()->first), + boost::forward(k)); + boost::unordered::detail::func::call_construct( + a.alloc_, boost::addressof(a.node_->value_ptr()->second)); + return a.release(); + } + + template + inline typename AllocAndPointer::node_pointer construct_pair(AllocAndPointer& a, + BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { + boost::unordered::detail::func::call_construct( + a.alloc_, boost::addressof(a.node_->value_ptr()->first), + boost::forward(k)); + boost::unordered::detail::func::call_construct( + a.alloc_, boost::addressof(a.node_->value_ptr()->second), + boost::forward(m)); + return a.release(); + } }}}} namespace boost { namespace unordered { namespace detail { diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp index ac5bd6d8..64a0a8ec 100644 --- a/include/boost/unordered/detail/buckets.hpp +++ b/include/boost/unordered/detail/buckets.hpp @@ -324,8 +324,6 @@ namespace boost { namespace unordered { namespace detail { template struct node_constructor { - private: - typedef NodeAlloc node_allocator; typedef boost::unordered::detail::allocator_traits node_allocator_traits; @@ -333,50 +331,20 @@ namespace boost { namespace unordered { namespace detail { typedef typename node_allocator_traits::pointer node_pointer; typedef typename node::value_type value_type; - protected: - node_allocator& alloc_; node_pointer node_; bool node_constructed_; - bool value_constructed_; - - public: node_constructor(node_allocator& n) : alloc_(n), node_(), - node_constructed_(false), - value_constructed_(false) + node_constructed_(false) { } ~node_constructor(); - void construct(); - - template - void construct_with_value(BOOST_UNORDERED_EMPLACE_ARGS) - { - construct(); - boost::unordered::detail::func::construct_value_impl( - alloc_, node_->value_ptr(), BOOST_UNORDERED_EMPLACE_FORWARD); - value_constructed_ = true; - } - - template - void construct_with_value2(BOOST_FWD_REF(A0) a0) - { - construct(); - boost::unordered::detail::func::construct_value_impl( - alloc_, node_->value_ptr(), - BOOST_UNORDERED_EMPLACE_ARGS1(boost::forward(a0))); - value_constructed_ = true; - } - - value_type const& value() const { - BOOST_ASSERT(node_ && node_constructed_ && value_constructed_); - return node_->value(); - } + void create_node(); // no throw node_pointer release() @@ -387,6 +355,14 @@ namespace boost { namespace unordered { namespace detail { return p; } + void reclaim(node_pointer p) { + BOOST_ASSERT(!node_); + node_ = p; + node_constructed_ = true; + boost::unordered::detail::func::destroy_value_impl(alloc_, + node_->value_ptr()); + } + private: node_constructor(node_constructor const&); node_constructor& operator=(node_constructor const&); @@ -396,11 +372,6 @@ namespace boost { namespace unordered { namespace detail { node_constructor::~node_constructor() { if (node_) { - if (value_constructed_) { - boost::unordered::detail::func::destroy_value_impl(alloc_, - node_->value_ptr()); - } - if (node_constructed_) { boost::unordered::detail::func::destroy( boost::addressof(*node_)); @@ -411,27 +382,66 @@ namespace boost { namespace unordered { namespace detail { } template - void node_constructor::construct() + void node_constructor::create_node() { - if(!node_) { - node_constructed_ = false; - value_constructed_ = false; + BOOST_ASSERT(!node_); + node_constructed_ = false; - node_ = node_allocator_traits::allocate(alloc_, 1); + node_ = node_allocator_traits::allocate(alloc_, 1); - new ((void*) boost::addressof(*node_)) node(); - node_->init(node_); - node_constructed_ = true; + new ((void*) boost::addressof(*node_)) node(); + node_->init(node_); + node_constructed_ = true; + } + + template + struct node_tmp + { + private: + + typedef NodeAlloc node_allocator; + typedef boost::unordered::detail::allocator_traits + node_allocator_traits; + typedef typename node_allocator_traits::value_type node; + typedef typename node_allocator_traits::pointer node_pointer; + typedef typename node::value_type value_type; + + public: + + node_allocator& alloc_; + node_pointer node_; + + explicit node_tmp(node_pointer n, node_allocator& a): + alloc_(a), + node_(n) + { } - else { - BOOST_ASSERT(node_constructed_); - if (value_constructed_) - { - boost::unordered::detail::func::destroy_value_impl(alloc_, - node_->value_ptr()); - value_constructed_ = false; - } + ~node_tmp(); + + value_type const& value() const { + BOOST_ASSERT(node_ ); + return node_->value(); + } + + // no throw + node_pointer release() + { + node_pointer p = node_; + node_ = node_pointer(); + return p; + } + }; + + template + node_tmp::~node_tmp() + { + if (node_) { + boost::unordered::detail::func::destroy_value_impl(alloc_, + node_->value_ptr()); + boost::unordered::detail::func::destroy( + boost::addressof(*node_)); + node_allocator_traits::deallocate(alloc_, node_, 1); } } @@ -442,11 +452,9 @@ namespace boost { namespace unordered { namespace detail { // Temporary store for nodes. Deletes any that aren't used. template - struct node_holder : private node_constructor + struct node_holder { private: - typedef node_constructor base; - typedef NodeAlloc node_allocator; typedef boost::unordered::detail::allocator_traits node_allocator_traits; @@ -456,13 +464,14 @@ namespace boost { namespace unordered { namespace detail { typedef typename node::link_pointer link_pointer; typedef boost::unordered::iterator_detail::iterator iterator; + node_constructor constructor_; node_pointer nodes_; public: template explicit node_holder(Table& b) : - base(b.node_alloc()), + constructor_(b.node_alloc()), nodes_() { if (b.size_) { @@ -475,61 +484,63 @@ namespace boost { namespace unordered { namespace detail { ~node_holder(); - void node_for_assignment() + node_pointer pop_node() { - if (!this->node_ && nodes_) { - this->node_ = nodes_; - nodes_ = static_cast(nodes_->next_); - this->node_->init(this->node_); - this->node_->next_ = link_pointer(); - - this->node_constructed_ = true; - this->value_constructed_ = true; - } + node_pointer n = nodes_; + nodes_ = static_cast(nodes_->next_); + n->init(n); + n->next_ = link_pointer(); + return n; } template - inline void assign_impl(T const& v) { - if (this->node_ && this->value_constructed_) { - this->node_->value() = v; + inline node_pointer copy_of(T const& v) { + if (nodes_) { + node_tmp a(pop_node(), constructor_.alloc_); + a.node_->value() = v; + return a.release(); } else { - this->construct_with_value2(v); + constructor_.create_node(); + return boost::unordered::detail::func::construct_value(constructor_, v); } } template - inline void assign_impl(std::pair const& v) { - this->construct_with_value2(v); + inline node_pointer copy_of(std::pair const& v) { + if (nodes_) { + constructor_.reclaim(pop_node()); + } + else { + constructor_.create_node(); + } + return boost::unordered::detail::func::construct_value(constructor_, v); } template - inline void move_assign_impl(T& v) { - if (this->node_ && this->value_constructed_) { - this->node_->value() = boost::move(v); + inline node_pointer move_copy_of(T& v) { + if (nodes_) { + node_tmp a(pop_node(), constructor_.alloc_); + a.node_->value() = boost::move(v); + return a.release(); } else { - this->construct_with_value2(boost::move(v)); + constructor_.create_node(); + return boost::unordered::detail::func::construct_value( + constructor_, boost::move(v)); } } template - inline void move_assign_impl(std::pair& v) { - this->construct_with_value2(boost::move(v)); - } - - node_pointer copy_of(value_type const& v) - { - node_for_assignment(); - assign_impl(v); - return base::release(); - } - - node_pointer move_copy_of(value_type& v) - { - node_for_assignment(); - move_assign_impl(v); - return base::release(); + inline node_pointer move_copy_of(std::pair& v) { + if (nodes_) { + constructor_.reclaim(pop_node()); + } + else { + constructor_.create_node(); + } + return boost::unordered::detail::func::construct_value( + constructor_, boost::move(v)); } iterator begin() const @@ -545,10 +556,10 @@ namespace boost { namespace unordered { namespace detail { node_pointer p = nodes_; nodes_ = static_cast(p->next_); - boost::unordered::detail::func::destroy_value_impl(this->alloc_, + boost::unordered::detail::func::destroy_value_impl(constructor_.alloc_, p->value_ptr()); boost::unordered::detail::func::destroy(boost::addressof(*p)); - node_allocator_traits::deallocate(this->alloc_, p, 1); + node_allocator_traits::deallocate(constructor_.alloc_, p, 1); } } diff --git a/include/boost/unordered/detail/equivalent.hpp b/include/boost/unordered/detail/equivalent.hpp index c58108ab..4d5c0f36 100644 --- a/include/boost/unordered/detail/equivalent.hpp +++ b/include/boost/unordered/detail/equivalent.hpp @@ -195,6 +195,7 @@ namespace boost { namespace unordered { namespace detail { typedef typename table::key_equal key_equal; typedef typename table::key_type key_type; typedef typename table::node_constructor node_constructor; + typedef typename table::node_tmp node_tmp; typedef typename table::extractor extractor; typedef typename table::iterator iterator; typedef typename table::c_iterator c_iterator; @@ -381,7 +382,7 @@ namespace boost { namespace unordered { namespace detail { } inline iterator add_node( - node_constructor& a, + node_tmp& a, std::size_t key_hash, iterator pos) { @@ -432,7 +433,7 @@ namespace boost { namespace unordered { namespace detail { return iterator(n); } - iterator emplace_impl(node_constructor& a) + iterator emplace_impl(node_tmp& a) { key_type const& k = this->get_key(a.value()); std::size_t key_hash = this->hash(k); @@ -444,7 +445,7 @@ namespace boost { namespace unordered { namespace detail { return this->add_node(a, key_hash, position); } - void emplace_impl_no_rehash(node_constructor& a) + void emplace_impl_no_rehash(node_tmp& a) { key_type const& k = this->get_key(a.value()); std::size_t key_hash = this->hash(k); @@ -473,9 +474,12 @@ namespace boost { namespace unordered { namespace detail { iterator emplace(BOOST_UNORDERED_EMPLACE_ARGS) { node_constructor a(this->node_alloc()); - a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value_generic(a, BOOST_UNORDERED_EMPLACE_FORWARD), + a.alloc_); - return iterator(emplace_impl(a)); + return iterator(emplace_impl(b)); } //////////////////////////////////////////////////////////////////////// @@ -492,8 +496,12 @@ namespace boost { namespace unordered { namespace detail { std::size_t distance = std::distance(i, j); if(distance == 1) { node_constructor a(this->node_alloc()); - a.construct_with_value2(*i); - emplace_impl(a); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value(a, *i), + a.alloc_); + + emplace_impl(b); } else { // Only require basic exception safety here @@ -501,8 +509,11 @@ namespace boost { namespace unordered { namespace detail { node_constructor a(this->node_alloc()); for (; i != j; ++i) { - a.construct_with_value2(*i); - emplace_impl_no_rehash(a); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value(a, *i), + a.alloc_); + emplace_impl_no_rehash(b); } } } @@ -513,8 +524,11 @@ namespace boost { namespace unordered { namespace detail { { node_constructor a(this->node_alloc()); for (; i != j; ++i) { - a.construct_with_value2(*i); - emplace_impl(a); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value(a, *i), + a.alloc_); + emplace_impl(b); } } @@ -635,12 +649,16 @@ namespace boost { namespace unordered { namespace detail { for (iterator n = src.begin(); n.node_;) { std::size_t key_hash = n.node_->hash_; iterator group_end(n.node_->group_prev_->next_); - constructor.construct_with_value2(*n); - iterator pos = this->add_node(constructor, key_hash, iterator()); + constructor.create_node(); + iterator pos = this->add_node( + boost::unordered::detail::func::construct_value( + constructor, *n), key_hash, iterator()); for (++n; n != group_end; ++n) { - constructor.construct_with_value2(*n); - this->add_node(constructor, key_hash, pos); + constructor.create_node(); + this->add_node( + boost::unordered::detail::func::construct_value( + constructor, *n), key_hash, pos); } } } @@ -652,12 +670,16 @@ namespace boost { namespace unordered { namespace detail { for (iterator n = src.begin(); n.node_;) { std::size_t key_hash = n.node_->hash_; iterator group_end(n.node_->group_prev_->next_); - constructor.construct_with_value2(boost::move(*n)); - iterator pos = this->add_node(constructor, key_hash, iterator()); + constructor.create_node(); + iterator pos = this->add_node( + boost::unordered::detail::func::construct_value( + constructor, boost::move(*n)), key_hash, iterator()); for (++n; n != group_end; ++n) { - constructor.construct_with_value2(boost::move(*n)); - this->add_node(constructor, key_hash, pos); + constructor.create_node(); + this->add_node( + boost::unordered::detail::func::construct_value( + constructor, boost::move(*n)), key_hash, pos); } } } diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index 7a0584f7..00c28c3b 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -111,6 +111,8 @@ namespace boost { namespace unordered { namespace detail { bucket_pointer; typedef boost::unordered::detail::node_constructor node_constructor; + typedef boost::unordered::detail::node_tmp + node_tmp; typedef boost::unordered::iterator_detail:: iterator iterator; @@ -366,7 +368,7 @@ namespace boost { namespace unordered { namespace detail { else if (bucket::extra_node) { node_constructor a(node_alloc()); - a.construct(); + a.create_node(); (constructor.get() + static_cast(new_count))->next_ = diff --git a/include/boost/unordered/detail/unique.hpp b/include/boost/unordered/detail/unique.hpp index ef0ce445..d1012924 100644 --- a/include/boost/unordered/detail/unique.hpp +++ b/include/boost/unordered/detail/unique.hpp @@ -190,6 +190,7 @@ namespace boost { namespace unordered { namespace detail { typedef typename table::key_equal key_equal; typedef typename table::key_type key_type; typedef typename table::node_constructor node_constructor; + typedef typename table::node_tmp node_tmp; typedef typename table::extractor extractor; typedef typename table::iterator iterator; typedef typename table::c_iterator c_iterator; @@ -308,7 +309,7 @@ namespace boost { namespace unordered { namespace detail { // Emplace/Insert inline iterator add_node( - node_constructor& a, + node_tmp& a, std::size_t key_hash) { return add_node(a.release(), key_hash); @@ -356,13 +357,12 @@ namespace boost { namespace unordered { namespace detail { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(this->node_alloc()); - a.construct_with_value(BOOST_UNORDERED_EMPLACE_ARGS3( - boost::unordered::piecewise_construct, - boost::make_tuple(k), - boost::make_tuple())); + a.create_node(); + node_tmp b(boost::unordered::detail::func::construct_pair(a, k), + a.alloc_); this->reserve_for_insert(this->size_ + 1); - return *add_node(a, key_hash); + return *add_node(b, key_hash); } #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -418,26 +418,33 @@ namespace boost { namespace unordered { namespace detail { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(this->node_alloc()); - a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value_generic( + a, BOOST_UNORDERED_EMPLACE_FORWARD), + a.alloc_); // reserve has basic exception safety if the hash function // throws, strong otherwise. this->reserve_for_insert(this->size_ + 1); - return emplace_return(this->add_node(a, key_hash), true); + return emplace_return(this->add_node(b, key_hash), true); } - emplace_return emplace_impl_with_node(node_constructor& a) + emplace_return emplace_impl_with_node(node_constructor& a, node_tmp& b) { - key_type const& k = this->get_key(a.value()); + key_type const& k = this->get_key(b.value()); std::size_t key_hash = this->hash(k); iterator pos = this->find_node(key_hash, k); - if (pos.node_) return emplace_return(pos, false); + if (pos.node_) { + a.reclaim(b.release()); + return emplace_return(pos, false); + } // reserve has basic exception safety if the hash function // throws, strong otherwise. this->reserve_for_insert(this->size_ + 1); - return emplace_return(this->add_node(a, key_hash), true); + return emplace_return(this->add_node(b, key_hash), true); } template @@ -446,8 +453,12 @@ namespace boost { namespace unordered { namespace detail { // Don't have a key, so construct the node first in order // to be able to lookup the position. node_constructor a(this->node_alloc()); - a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD); - return emplace_impl_with_node(a); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value_generic( + a, BOOST_UNORDERED_EMPLACE_FORWARD), + a.alloc_); + return emplace_impl_with_node(a, b); } //////////////////////////////////////////////////////////////////////// @@ -492,13 +503,16 @@ namespace boost { namespace unordered { namespace detail { iterator pos = this->find_node(key_hash, k); if (!pos.node_) { - a.construct_with_value2(*i); + a.create_node(); + node_tmp b( + boost::unordered::detail::func::construct_value(a, *i), + a.alloc_); if(this->size_ + 1 > this->max_load_) this->reserve_for_insert(this->size_ + boost::unordered::detail::insert_size(i, j)); // Nothing after this point can throw. - this->add_node(a, key_hash); + this->add_node(b, key_hash); } } @@ -508,8 +522,11 @@ namespace boost { namespace unordered { namespace detail { node_constructor a(this->node_alloc()); do { - a.construct_with_value2(*i); - emplace_impl_with_node(a); + if (!a.node_) { a.create_node(); } + node_tmp b( + boost::unordered::detail::func::construct_value(a, *i), + a.alloc_); + emplace_impl_with_node(a, b); } while(++i != j); } @@ -587,8 +604,10 @@ namespace boost { namespace unordered { namespace detail { this->create_buckets(this->bucket_count_); for(iterator n = src.begin(); n.node_; ++n) { - constructor.construct_with_value2(*n); - this->add_node(constructor, n.node_->hash_); + constructor.create_node(); + this->add_node( + boost::unordered::detail::func::construct_value( + constructor, *n), n.node_->hash_); } } @@ -597,8 +616,10 @@ namespace boost { namespace unordered { namespace detail { this->create_buckets(this->bucket_count_); for(iterator n = src.begin(); n.node_; ++n) { - constructor.construct_with_value2(boost::move(*n)); - this->add_node(constructor, n.node_->hash_); + constructor.create_node(); + this->add_node( + boost::unordered::detail::func::construct_value( + constructor, boost::move(*n)), n.node_->hash_); } }