Change how node construction works.

Split node_constructor into two classes, one for constructing a node
without a value, and then another for holding it once the value is
constructed.

Do the work of constructing values in convenience functions in
allocate.hpp (construct_value_generic, construct_value, construct_pair).
This commit is contained in:
Daniel James
2016-08-14 20:55:40 +01:00
parent 609ae6cb4e
commit 8017d9e684
5 changed files with 247 additions and 139 deletions

View File

@ -808,6 +808,18 @@ namespace boost { namespace unordered { namespace detail { namespace func {
# endif
#else
template <typename Alloc, typename T>
inline void call_construct(Alloc&, T* address)
{
new ((void*) address) T();
}
template <typename Alloc, typename T, typename A0>
inline void call_construct(Alloc&, T* address,
BOOST_FWD_REF(A0) a0)
{
new ((void*) address) T(boost::forward<A0>(a0));
}
template <typename Alloc, typename T>
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 <typename AllocAndPointer, BOOST_UNORDERED_EMPLACE_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 <typename AllocAndPointer, typename U>
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<U>(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 <typename AllocAndPointer, typename Key>
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<Key>(k));
boost::unordered::detail::func::call_construct(
a.alloc_, boost::addressof(a.node_->value_ptr()->second));
return a.release();
}
template <typename AllocAndPointer, typename Key, typename Mapped>
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<Key>(k));
boost::unordered::detail::func::call_construct(
a.alloc_, boost::addressof(a.node_->value_ptr()->second),
boost::forward<Mapped>(m));
return a.release();
}
}}}}
namespace boost { namespace unordered { namespace detail {

View File

@ -324,8 +324,6 @@ namespace boost { namespace unordered { namespace detail {
template <typename NodeAlloc>
struct node_constructor
{
private:
typedef NodeAlloc node_allocator;
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
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 <BOOST_UNORDERED_EMPLACE_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 <typename A0>
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>(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<Alloc>::~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 <typename Alloc>
void node_constructor<Alloc>::construct()
void node_constructor<Alloc>::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 <typename NodeAlloc>
struct node_tmp
{
private:
typedef NodeAlloc node_allocator;
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
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 <typename Alloc>
node_tmp<Alloc>::~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 <typename NodeAlloc>
struct node_holder : private node_constructor<NodeAlloc>
struct node_holder
{
private:
typedef node_constructor<NodeAlloc> base;
typedef NodeAlloc node_allocator;
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
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<node> iterator;
node_constructor<NodeAlloc> constructor_;
node_pointer nodes_;
public:
template <typename Table>
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<node_pointer>(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<node_pointer>(nodes_->next_);
n->init(n);
n->next_ = link_pointer();
return n;
}
template <typename T>
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<NodeAlloc> 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 <typename T1, typename T2>
inline void assign_impl(std::pair<T1 const, T2> const& v) {
this->construct_with_value2(v);
inline node_pointer copy_of(std::pair<T1 const, T2> const& v) {
if (nodes_) {
constructor_.reclaim(pop_node());
}
else {
constructor_.create_node();
}
return boost::unordered::detail::func::construct_value(constructor_, v);
}
template <typename T>
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<NodeAlloc> 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 <typename T1, typename T2>
inline void move_assign_impl(std::pair<T1 const, T2>& 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<T1 const, T2>& 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<node_pointer>(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);
}
}

View File

@ -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);
}
}
}

View File

@ -111,6 +111,8 @@ namespace boost { namespace unordered { namespace detail {
bucket_pointer;
typedef boost::unordered::detail::node_constructor<node_allocator>
node_constructor;
typedef boost::unordered::detail::node_tmp<node_allocator>
node_tmp;
typedef boost::unordered::iterator_detail::
iterator<node> 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<std::ptrdiff_t>(new_count))->next_ =

View File

@ -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 <BOOST_UNORDERED_EMPLACE_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_);
}
}