Merge support for emplace for compilers with rvalue references and variadic templates arguments, and better use of C++0x allocators.

Merged revisions 44058-44075,44078-44084,44086-44108,44110-44365,44367,44369-44414,44416-44419,44421-44457,44467-44469,44471-44511,44513-44535,44537-44737 via svnmerge from 
https://svn.boost.org/svn/boost/branches/unordered/trunk

................
  r44467 | danieljames | 2008-04-16 18:35:56 +0100 (Wed, 16 Apr 2008) | 2 lines
  
  Add C++-0x support to the test allocators.
................
  r44468 | danieljames | 2008-04-16 18:36:06 +0100 (Wed, 16 Apr 2008) | 2 lines
  
  Add a C++-0x node_constructor.
................
  r44469 | danieljames | 2008-04-16 18:36:16 +0100 (Wed, 16 Apr 2008) | 2 lines
  
  C++-0x constructor for node.
................
  r44516 | danieljames | 2008-04-17 21:41:48 +0100 (Thu, 17 Apr 2008) | 16 lines
  
  Merge in my work so far on implementing emplace for compilers with variadic
  template & rvalue references.
  
  Merged revisions 44059-44062 via svnmerge from 
  https://svn.boost.org/svn/boost/branches/unordered/dev
  
  ........
    r44059 | danieljames | 2008-04-05 17:41:25 +0100 (Sat, 05 Apr 2008) | 1 line
    
    First stab at implementing emplace - only for compilers with variadic template & rvalue references.
  ........
    r44062 | danieljames | 2008-04-05 19:12:09 +0100 (Sat, 05 Apr 2008) | 1 line
    
    Better variable template arguments, need to add proper support to BoostBook.
  ........
................
  r44616 | danieljames | 2008-04-20 13:30:19 +0100 (Sun, 20 Apr 2008) | 1 line
  
  Merge with trunk, fixes tabs.
................
  r44618 | danieljames | 2008-04-20 13:42:38 +0100 (Sun, 20 Apr 2008) | 2 lines
  
  Some extra compile tests.
................
  r44619 | danieljames | 2008-04-20 13:42:50 +0100 (Sun, 20 Apr 2008) | 2 lines
  
  Fix an error message.
................
  r44703 | danieljames | 2008-04-21 20:19:50 +0100 (Mon, 21 Apr 2008) | 15 lines
  
  Merge latest changes from trunk.
  
  Merged revisions 44616-44702 via svnmerge from 
  https://svn.boost.org/svn/boost/trunk
  
  ........
    r44650 | danieljames | 2008-04-20 22:08:57 +0100 (Sun, 20 Apr 2008) | 1 line
    
    Update an include.
  ........
    r44697 | danieljames | 2008-04-21 16:55:40 +0100 (Mon, 21 Apr 2008) | 1 line
    
    Factor out the code for choosing the bucket count, and which bucket that hash values map to make it easier to experiment with alternative policies.
  ........
................
  r44733 | danieljames | 2008-04-23 07:55:43 +0100 (Wed, 23 Apr 2008) | 2 lines
  
  Remove 'reserve_extra'.
................
  r44734 | danieljames | 2008-04-23 07:55:55 +0100 (Wed, 23 Apr 2008) | 2 lines
  
  More unnecessary copy tests - showing some weakness in the emplace implementation.
................
  r44735 | danieljames | 2008-04-23 07:56:06 +0100 (Wed, 23 Apr 2008) | 2 lines
  
  More tests.
................
  r44736 | danieljames | 2008-04-23 07:56:19 +0100 (Wed, 23 Apr 2008) | 2 lines
  
  Comment out a test which requires a C++0x std::pair.
................
  r44737 | danieljames | 2008-04-23 07:56:35 +0100 (Wed, 23 Apr 2008) | 2 lines
  
  Avoid creating unnecessary copies in unordered_set::emplace and unordered_map::emplace.
................


[SVN r44738]
This commit is contained in:
Daniel James
2008-04-23 07:09:58 +00:00
parent c56213442d
commit 94932ae026
12 changed files with 978 additions and 64 deletions

View File

@ -262,6 +262,60 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</method>
</method-group>
<method-group name="modifiers">
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>std::pair&lt;iterator, bool&gt;</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent value.</para>
</description>
<returns>
<para>The bool component of the return type is true if an insert took place.</para>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="hint">
<paramtype>const_iterator</paramtype>
</parameter>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent value.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. </para>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="insert">
<parameter name="obj">
<paramtype>value_type const&amp;</paramtype>
@ -892,6 +946,59 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</method>
</method-group>
<method-group name="modifiers">
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="hint">
<paramtype>const_iterator</paramtype>
</parameter>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. </para>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="insert">
<parameter name="obj">
<paramtype>value_type const&amp;</paramtype>
@ -1533,6 +1640,60 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</method>
</method-group>
<method-group name="modifiers">
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>std::pair&lt;iterator, bool&gt;</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent key.</para>
</description>
<returns>
<para>The bool component of the return type is true if an insert took place.</para>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="hint">
<paramtype>const_iterator</paramtype>
</parameter>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent key.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. </para>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="insert">
<parameter name="obj">
<paramtype>value_type const&amp;</paramtype>
@ -2208,6 +2369,59 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</method>
</method-group>
<method-group name="modifiers">
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="emplace">
<template>
<template-type-parameter name="Args">
</template-type-parameter>
<template-varargs></template-varargs>
</template>
<parameter name="hint">
<paramtype>const_iterator</paramtype>
</parameter>
<parameter name="args">
<paramtype>Args&amp;&amp;...</paramtype>
</parameter>
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
</returns>
<throws>
<para>If an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
</throws>
<notes>
<para>The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. </para>
<para>Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.</para>
<para>Pointers and references to elements are never invalidated.</para>
</notes>
</method>
<method name="insert">
<parameter name="obj">
<paramtype>value_type const&amp;</paramtype>

View File

@ -32,6 +32,13 @@
#include <boost/mpl/aux_/config/eti.hpp>
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/not.hpp>
#endif
#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0582)
#define BOOST_UNORDERED_BORLAND_BOOL(x) (bool)(x)
#else

View File

@ -108,15 +108,114 @@ namespace boost {
struct node : node_base
{
public:
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <typename... Args>
node(Args&&... args)
: node_base(), value_(std::forward<Args>(args)...) {}
#else
node(value_type const& v) : node_base(), value_(v) {}
#endif
value_type value_;
};
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
// allocators
//
// Stores all the allocators that we're going to need.
struct allocators
{
node_allocator node_alloc_;
bucket_allocator bucket_alloc_;
allocators(value_allocator const& a)
: node_alloc_(a), bucket_alloc_(a)
{}
void destroy(link_ptr ptr)
{
node_ptr n(node_alloc_.address(*static_cast<node*>(&*ptr)));
node_alloc_.destroy(n);
node_alloc_.deallocate(n, 1);
}
void swap(allocators& x)
{
unordered_detail::hash_swap(node_alloc_, x.node_alloc_);
unordered_detail::hash_swap(bucket_alloc_, x.bucket_alloc_);
}
bool operator==(allocators const& x)
{
return node_alloc_ == x.node_alloc_;
}
};
// node_constructor
//
// Used to construct nodes in an exception safe manner.
class node_constructor
{
allocators& allocators_;
node_ptr node_;
bool node_constructed_;
public:
node_constructor(allocators& a)
: allocators_(a),
node_(), node_constructed_(false)
{
}
~node_constructor()
{
if (node_) {
if (node_constructed_)
allocators_.node_alloc_.destroy(node_);
allocators_.node_alloc_.deallocate(node_, 1);
}
}
template <typename... Args>
void construct(Args&&... args)
{
BOOST_ASSERT(!node_);
node_constructed_ = false;
node_ = allocators_.node_alloc_.allocate(1);
allocators_.node_alloc_.construct(node_, std::forward<Args>(args)...);
node_constructed_ = true;
}
node_ptr get() const
{
BOOST_ASSERT(node_);
return node_;
}
// no throw
link_ptr release()
{
node_ptr p = node_;
unordered_detail::reset(node_);
return link_ptr(allocators_.bucket_alloc_.address(*p));
}
private:
node_constructor(node_constructor const&);
node_constructor& operator=(node_constructor const&);
};
#else
// allocators
//
// Stores all the allocators that we're going to need.
struct allocators
{
node_allocator node_alloc_;
@ -151,6 +250,10 @@ namespace boost {
}
};
// node_constructor
//
// Used to construct nodes in an exception safe manner.
class node_constructor
{
allocators& allocators_;
@ -201,6 +304,27 @@ namespace boost {
value_constructed_ = true;
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <typename... Args>
void construct(Args&&... args)
{
BOOST_ASSERT(!node_);
value_constructed_ = false;
node_base_constructed_ = false;
node_ = allocators_.node_alloc_.allocate(1);
allocators_.node_base_alloc_.construct(
allocators_.node_base_alloc_.address(*node_),
node_base());
node_base_constructed_ = true;
allocators_.value_alloc_.construct(
allocators_.value_alloc_.address(node_->value_), std::forward<Args>(args)...);
value_constructed_ = true;
}
#endif
node_ptr get() const
{
BOOST_ASSERT(node_);
@ -219,6 +343,7 @@ namespace boost {
node_constructor(node_constructor const&);
node_constructor& operator=(node_constructor const&);
};
#endif
// Methods for navigating groups of elements with equal keys.
@ -1251,9 +1376,9 @@ namespace boost {
// accessors
// no throw
value_allocator get_allocator() const
node_allocator get_allocator() const
{
return data_.allocators_.value_alloc_;
return data_.allocators_.node_alloc_;
}
// no throw
@ -1356,26 +1481,6 @@ namespace boost {
return need_to_reserve;
}
// basic exception safety
//
// This version of reserve is called when inserting a range
// into a container with equivalent keys, it creates more buckets
// if the resulting load factor would be over 80% of the load
// factor. This is to try to avoid excessive rehashes.
bool reserve_extra(size_type n)
{
using namespace std;
bool need_to_reserve = n >= max_load_;
// throws - basic:
if (need_to_reserve) {
rehash_impl(double_to_size_t(floor(
n / (double) mlf_ * 1.25)) + 1);
}
BOOST_ASSERT(n < max_load_ || n > max_size());
return need_to_reserve;
}
public:
// no throw
@ -1422,6 +1527,41 @@ namespace boost {
return v.first;
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
struct no_key {};
template <typename Arg1, typename... Args>
static typename boost::enable_if<
boost::mpl::and_<
boost::mpl::not_<boost::is_same<key_type, value_type> >,
boost::is_same<Arg1, key_type>
>,
key_type>::type const& extract_key(Arg1 const& k, Args const&...)
{
return k;
}
template <typename First, typename Second>
static typename boost::enable_if<
boost::mpl::and_<
boost::mpl::not_<boost::is_same<key_type, value_type> >,
boost::is_same<key_type,
typename boost::remove_const<
typename boost::remove_reference<First>::type
>::type>
>,
key_type>::type const& extract_key(std::pair<First, Second> const& v)
{
return v.first;
}
template <typename... Args>
static no_key extract_key(Args const&...)
{
return no_key();
}
#endif
public:
// if hash function throws, basic exception safety
@ -1526,16 +1666,70 @@ namespace boost {
// strong otherwise
iterator_base insert(value_type const& v)
{
key_type const& k = extract_key(v);
size_type hash_value = hash_function()(k);
bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value);
link_ptr position = find_iterator(bucket, k);
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(v);
return insert_impl(a);
}
// Insert (equivalent key containers)
// if hash function throws, basic exception safety
// strong otherwise
iterator_base insert_hint(iterator_base const& it, value_type const& v)
{
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(v);
return insert_hint_impl(it, a);
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
// Insert (equivalent key containers)
// (I'm using an overloaded insert for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template <class... Args>
iterator_base insert(Args&&... args)
{
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(std::forward<Args>(args)...);
return insert_impl(a);
}
// Insert (equivalent key containers)
// (I'm using an overloaded insert for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template <class... Args>
iterator_base insert_hint(iterator_base const& it, Args&&... args)
{
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(std::forward<Args>(args)...);
return insert_hint_impl(it, a);
}
#endif
iterator_base insert_impl(node_constructor& a)
{
key_type const& k = extract_key(a.get()->value_);
size_type hash_value = hash_function()(k);
bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value);
link_ptr position = find_iterator(bucket, k);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(reserve(size() + 1))
@ -1550,17 +1744,13 @@ namespace boost {
);
}
// Insert (equivalent key containers)
// if hash function throws, basic exception safety
// strong otherwise
iterator_base insert_hint(iterator_base const& it, value_type const& v)
iterator_base insert_hint_impl(iterator_base const& it, node_constructor& a)
{
// equal can throw, but with no effects
if (it == data_.end() || !equal(extract_key(v), *it)) {
if (it == data_.end() || !equal(extract_key(a.get()->value_), *it)) {
// Use the standard insert if the iterator doesn't point
// to a matching key.
return insert(v);
return insert_impl(a);
}
else {
// Find the first node in the group - so that the node
@ -1570,15 +1760,10 @@ namespace boost {
while(data_.prev_in_group(start)->next_ == start)
start = data_.prev_in_group(start);
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(v);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
bucket_ptr base = reserve(size() + 1) ?
get_bucket(extract_key(v)) : it.bucket_;
get_bucket(extract_key(a.get()->value_)) : it.bucket_;
// Nothing after this point can throw
@ -1602,7 +1787,7 @@ namespace boost {
}
else {
// Only require basic exception safety here
reserve_extra(size() + distance);
reserve(size() + distance);
node_constructor a(data_.allocators_);
for (; i != j; ++i) {
@ -1729,6 +1914,104 @@ namespace boost {
return insert(v).first;
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
// Insert (unique keys)
// (I'm using an overloaded insert for both 'insert' and 'emplace')
//
// TODO:
// For sets: create a local key without creating the node?
// For maps: use the first argument as the key.
// if hash function throws, basic exception safety
// strong otherwise
template<typename... Args>
std::pair<iterator_base, bool> insert(Args&&... args)
{
return insert_impl(
extract_key(std::forward<Args>(args)...),
std::forward<Args>(args)...);
}
template<typename... Args>
std::pair<iterator_base, bool> insert_impl(key_type const& k, Args&&... args)
{
// No side effects in this initial code
size_type hash_value = hash_function()(k);
bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value);
link_ptr pos = find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return std::pair<iterator_base, bool>(
iterator_base(bucket, pos), false);
} else {
// Doesn't already exist, add to bucket.
// Side effects only in this block.
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(data_.allocators_);
a.construct(std::forward<Args>(args)...);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(reserve(size() + 1))
bucket = data_.bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
link_ptr n = data_.link_node_in_bucket(a, bucket);
return std::pair<iterator_base, bool>(
iterator_base(bucket, n), true);
}
}
template<typename... Args>
std::pair<iterator_base, bool> insert_impl(no_key, Args&&... args)
{
// Construct the node regardless - in order to get the key.
// It will be discarded if it isn't used
node_constructor a(data_.allocators_);
a.construct(std::forward<Args>(args)...);
// No side effects in this initial code
key_type const& k = extract_key(a.get()->value_);
size_type hash_value = hash_function()(k);
bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value);
link_ptr pos = find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return std::pair<iterator_base, bool>(
iterator_base(bucket, pos), false);
} else {
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(reserve(size() + 1))
bucket = data_.bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
return std::pair<iterator_base, bool>(iterator_base(bucket,
data_.link_node_in_bucket(a, bucket)), true);
}
}
// Insert (unique keys)
// (I'm using an overloaded insert for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template<typename... Args>
iterator_base insert_hint(iterator_base const& it, Args&&... args)
{
// Life is complicated - just call the normal implementation.
return insert(std::forward<Args>(args)...).first;
}
#endif
// Insert from iterators (unique keys)
template <typename I>

View File

@ -199,6 +199,21 @@ namespace boost
// modifiers
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args)
{
return boost::unordered_detail::pair_cast<iterator, bool>(
base.insert(std::forward<Args>(args)...));
}
template <class... Args>
iterator emplace(const_iterator hint, Args&&... args)
{
return iterator(base.insert_hint(get(hint), std::forward<Args>(args)...));
}
#endif
std::pair<iterator, bool> insert(const value_type& obj)
{
return boost::unordered_detail::pair_cast<iterator, bool>(
@ -553,6 +568,20 @@ namespace boost
// modifiers
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class... Args>
iterator emplace(Args&&... args)
{
return iterator(base.insert(std::forward<Args>(args)...));
}
template <class... Args>
iterator emplace(const_iterator hint, Args&&... args)
{
return iterator(base.insert_hint(get(hint), std::forward<Args>(args)...));
}
#endif
iterator insert(const value_type& obj)
{
return iterator(base.insert(obj));

View File

@ -196,6 +196,22 @@ namespace boost
// modifiers
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args)
{
return boost::unordered_detail::pair_cast<iterator, bool>(
base.insert(std::forward<Args>(args)...));
}
template <class... Args>
iterator emplace(const_iterator hint, Args&&... args)
{
return iterator(
base.insert_hint(get(hint), std::forward<Args>(args)...));
}
#endif
std::pair<iterator, bool> insert(const value_type& obj)
{
return boost::unordered_detail::pair_cast<iterator, bool>(
@ -520,6 +536,20 @@ namespace boost
// modifiers
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class... Args>
iterator emplace(Args&&... args)
{
return iterator(base.insert(std::forward<Args>(args)...));
}
template <class... Args>
iterator emplace(const_iterator hint, Args&&... args)
{
return iterator(base.insert_hint(get(hint), std::forward<Args>(args)...));
}
#endif
iterator insert(const value_type& obj)
{
return iterator(base.insert(obj));

View File

@ -36,6 +36,25 @@ struct insert_test_base : public test::exception_base
}
};
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class T>
struct emplace_test1 : public insert_test_base<T>
{
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
void run(T& x, strong_type& strong) const {
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
it = this->values.begin(), end = this->values.end(); it != end; ++it)
{
strong.store(x);
x.emplace(*it);
}
}
};
#endif
template <class T>
struct insert_test1 : public insert_test_base<T>
{
@ -204,7 +223,15 @@ struct insert_test_rehash3 : public insert_test_base<T>
}
};
RUN_EXCEPTION_TESTS(
(insert_test1)(insert_test2)(insert_test3)(insert_test4)
(insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3),
CONTAINER_SEQ)
#define BASIC_TESTS \
(insert_test1)(insert_test2)(insert_test3)(insert_test4) \
(insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3)
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
#define ALL_TESTS (emplace_test1)BASIC_TESTS
#else
#define ALL_TESTS BASIC_TESTS
#endif
RUN_EXCEPTION_TESTS(ALL_TESTS, CONTAINER_SEQ)

View File

@ -347,6 +347,16 @@ namespace exception
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template<class... Args> void construct(pointer p, Args&&... args) {
UNORDERED_SCOPE(allocator::construct(pointer, Args&&...)) {
UNORDERED_EPOINT("Mock allocator construct function.");
new(p) T(std::forward<Args>(args)...);
}
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
}
#endif
void destroy(pointer p) {
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
p->~T();

View File

@ -24,6 +24,7 @@ namespace minimal
class copy_constructible;
class default_copy_constructible;
class assignable;
template <class T> class hash;
template <class T> class equal_to;
template <class T> class ptr;
@ -207,6 +208,13 @@ namespace minimal
}
void construct(pointer p, T const& t) { new((void*)p.ptr_) T(t); }
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template<class... Args> void construct(pointer p, Args&&... args) {
new((void*)p.ptr_) T(std::forward<Args>(args)...);
}
#endif
void destroy(pointer p) { ((T*)p.ptr_)->~T(); }
size_type max_size() const { return 1000; }

View File

@ -212,6 +212,13 @@ namespace test
new(p) T(t);
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template<class... Args> void construct(pointer p, Args&&... args) {
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
new(p) T(std::forward<Args>(args)...);
}
#endif
void destroy(pointer p) {
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
p->~T();

View File

@ -25,6 +25,7 @@
typedef long double comparison_type;
template <class T> void sink(T const&) {}
template <class T> T rvalue(T const& v) { return v; }
template <class X, class T>
void container_test(X& r, T&)
@ -144,11 +145,20 @@ void unordered_set_test(X&, Key const&)
}
template <class X, class Key, class T>
void unordered_map_test(X&, Key const&, T const&)
void unordered_map_test(X& r, Key const& k, T const& v)
{
typedef BOOST_DEDUCED_TYPENAME X::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME X::key_type key_type;
BOOST_MPL_ASSERT((boost::is_same<value_type, std::pair<key_type const, T> >));
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
Key k_lvalue(k);
T v_lvalue(v);
r.emplace(k, v);
r.emplace(k_lvalue, v_lvalue);
r.emplace(rvalue(k), rvalue(v));
#endif
}
template <class X, class T>
@ -156,6 +166,9 @@ void unordered_unique_test(X& r, T const& t)
{
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
test::check_return_type<std::pair<iterator, bool> >::equals(r.insert(t));
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
test::check_return_type<std::pair<iterator, bool> >::equals(r.emplace(t));
#endif
}
template <class X, class T>
@ -163,6 +176,9 @@ void unordered_equivalent_test(X& r, T const& t)
{
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
test::check_return_type<iterator>::equals(r.insert(t));
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
test::check_return_type<iterator>::equals(r.emplace(t));
#endif
}
template <class X, class Key, class T>
@ -264,6 +280,9 @@ void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq)
const_iterator q = a.cbegin();
test::check_return_type<iterator>::equals(a.insert(q, t));
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
test::check_return_type<iterator>::equals(a.emplace(q, t));
#endif
a.insert(i, j);
test::check_return_type<size_type>::equals(a.erase(k));

View File

@ -218,6 +218,74 @@ void insert_tests2(X*, test::random_generator generator = test::default_generato
}
}
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class X>
void unique_emplace_tests1(X*, test::random_generator generator = test::default_generator)
{
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
typedef test::ordered<X> ordered;
std::cerr<<"emplace(value) tests for containers with unique keys.\n";
X x;
test::ordered<X> tracker = test::create_ordered(x);
test::random_values<X> v(1000, generator);
for(BOOST_DEDUCED_TYPENAME test::random_values<X>::iterator it = v.begin();
it != v.end(); ++it)
{
BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count();
float b = x.max_load_factor();
std::pair<iterator, bool> r1 = x.emplace(*it);
std::pair<BOOST_DEDUCED_TYPENAME ordered::iterator, bool> r2 = tracker.insert(*it);
BOOST_CHECK(r1.second == r2.second);
BOOST_CHECK(*r1.first == *r2.first);
tracker.compare_key(x, *it);
if(x.size() < b * old_bucket_count)
BOOST_CHECK(x.bucket_count() == old_bucket_count);
}
test::check_equivalent_keys(x);
}
template <class X>
void equivalent_emplace_tests1(X*, test::random_generator generator = test::default_generator)
{
std::cerr<<"emplace(value) tests for containers with equivalent keys.\n";
X x;
test::ordered<X> tracker = test::create_ordered(x);
test::random_values<X> v(1000, generator);
for(BOOST_DEDUCED_TYPENAME test::random_values<X>::iterator it = v.begin();
it != v.end(); ++it)
{
BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count();
float b = x.max_load_factor();
BOOST_DEDUCED_TYPENAME X::iterator r1 = x.emplace(*it);
BOOST_DEDUCED_TYPENAME test::ordered<X>::iterator r2 = tracker.insert(*it);
BOOST_CHECK(*r1 == *r2);
tracker.compare_key(x, *it);
if(x.size() < b * old_bucket_count)
BOOST_CHECK(x.bucket_count() == old_bucket_count);
}
test::check_equivalent_keys(x);
}
#endif
template <class X>
void map_tests(X*, test::random_generator generator = test::default_generator)
{
@ -283,6 +351,18 @@ UNORDERED_TEST(insert_tests2,
((default_generator)(generate_collisions))
)
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
UNORDERED_TEST(unique_emplace_tests1,
((test_set)(test_map))
((default_generator)(generate_collisions))
)
UNORDERED_TEST(equivalent_emplace_tests1,
((test_multiset)(test_multimap))
((default_generator)(generate_collisions))
)
#endif
UNORDERED_TEST(map_tests,
((test_map))
((default_generator)(generate_collisions))

View File

@ -11,15 +11,34 @@ namespace unnecessary_copy_tests
{
struct count_copies
{
static int count;
count_copies() { ++count; }
count_copies(count_copies const&) { ++count; }
static int copies;
static int moves;
count_copies() : tag_(0) { ++copies; }
explicit count_copies(int tag) : tag_(tag) { ++copies; }
count_copies(count_copies const&, count_copies const& x) : tag_(x.tag_) { ++copies; }
count_copies(count_copies const& x) : tag_(x.tag_) { ++copies; }
#if defined(BOOST_HAS_RVALUE_REFS)
count_copies(count_copies&& x) : tag_(x.tag_) {
x.tag_ = -1; ++moves;
}
#endif
int tag_;
private:
count_copies& operator=(count_copies const&);
};
bool operator==(count_copies const&, count_copies const&) {
return true;
bool operator==(count_copies const& x, count_copies const& y) {
return x.tag_ == y.tag_;
}
template <class T>
T source() {
return T();
}
void reset() {
count_copies::copies = 0;
count_copies::moves = 0;
}
}
@ -29,29 +48,36 @@ namespace boost
namespace unnecessary_copy_tests
#endif
{
std::size_t hash_value(unnecessary_copy_tests::count_copies const&) {
return 0;
std::size_t hash_value(unnecessary_copy_tests::count_copies const& x) {
return x.tag_;
}
}
#define COPY_COUNT(n) \
if(count_copies::copies != n) { \
BOOST_ERROR("Wrong number of copies."); \
std::cerr<<"Number of copies: "<<count_copies::copies<<std::endl; \
}
#define MOVE_COUNT(n) \
if(count_copies::moves != n) { \
BOOST_ERROR("Wrong number of moves."); \
std::cerr<<"Number of moves: "<<count_copies::moves<<std::endl; \
}
namespace unnecessary_copy_tests
{
int count_copies::count;
int count_copies::copies;
int count_copies::moves;
template <class T>
void unnecessary_copy_test(T*)
void unnecessary_copy_insert_test(T*)
{
count_copies::count = 0;
reset();
T x;
BOOST_DEDUCED_TYPENAME T::value_type a;
BOOST_CHECK(count_copies::count == 1);
if(count_copies::count != 1)
std::cerr<<count_copies::count<<" copies.\n";
COPY_COUNT(1);
x.insert(a);
BOOST_CHECK(count_copies::count == 2);
if(count_copies::count != 1)
std::cerr<<count_copies::count<<" copies.\n";
COPY_COUNT(2);
}
boost::unordered_set<count_copies>* set;
@ -59,7 +85,181 @@ namespace unnecessary_copy_tests
boost::unordered_map<int, count_copies>* map;
boost::unordered_multimap<int, count_copies>* multimap;
UNORDERED_TEST(unnecessary_copy_test, ((set)(multiset)(map)(multimap)))
UNORDERED_TEST(unnecessary_copy_insert_test,
((set)(multiset)(map)(multimap)))
#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)
template <class T>
void unnecessary_copy_emplace_test(T*)
{
reset();
T x;
BOOST_DEDUCED_TYPENAME T::value_type a;
COPY_COUNT(1);
x.emplace(a);
COPY_COUNT(2);
}
template <class T>
void unnecessary_copy_emplace_rvalue_test(T*)
{
reset();
T x;
x.emplace(source<BOOST_DEDUCED_TYPENAME T::value_type>());
COPY_COUNT(1);
}
template <class T>
void unnecessary_copy_emplace_move_test(T*)
{
reset();
T x;
BOOST_DEDUCED_TYPENAME T::value_type a;
COPY_COUNT(1); MOVE_COUNT(0);
x.emplace(std::move(a));
COPY_COUNT(1); MOVE_COUNT(1);
}
UNORDERED_TEST(unnecessary_copy_emplace_test,
((set)(multiset)(map)(multimap)))
UNORDERED_TEST(unnecessary_copy_emplace_rvalue_test,
((set)(multiset)(map)(multimap)))
UNORDERED_TEST(unnecessary_copy_emplace_move_test,
((set)(multiset)(map)(multimap)))
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test)
{
reset();
boost::unordered_set<count_copies> x;
count_copies a;
x.insert(a);
COPY_COUNT(2); MOVE_COUNT(0);
//
// 0 arguments
//
// The container will have to create a copy in order to compare with
// the existing element.
reset();
x.emplace();
COPY_COUNT(1); MOVE_COUNT(0);
//
// 1 argument
//
// Emplace should be able to tell that there already is an element
// without creating a new one.
reset();
x.emplace(a);
COPY_COUNT(0); MOVE_COUNT(0);
// A new object is created by source, but it shouldn't be moved or
// copied.
reset();
x.emplace(source<count_copies>());
COPY_COUNT(1); MOVE_COUNT(0);
// No move should take place.
reset();
x.emplace(std::move(a));
COPY_COUNT(0); MOVE_COUNT(0);
// Just in case a did get moved...
count_copies b;
// The container will have to create a copy in order to compare with
// the existing element.
reset();
x.emplace(b.tag_);
COPY_COUNT(1); MOVE_COUNT(0);
//
// 2 arguments
//
// The container will have to create b copy in order to compare with
// the existing element.
reset();
x.emplace(b, b);
COPY_COUNT(1); MOVE_COUNT(0);
}
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test)
{
reset();
boost::unordered_map<count_copies, count_copies> x;
// TODO: Run tests for pairs without const etc.
std::pair<count_copies const, count_copies> a;
x.emplace(a);
COPY_COUNT(4); MOVE_COUNT(0);
//
// 0 arguments
//
// COPY_COUNT(1) would be okay here.
reset();
x.emplace();
COPY_COUNT(2); MOVE_COUNT(0);
//
// 1 argument
//
reset();
x.emplace(a);
COPY_COUNT(0); MOVE_COUNT(0);
// A new object is created by source, but it shouldn't be moved or
// copied.
reset();
x.emplace(source<std::pair<count_copies, count_copies> >());
COPY_COUNT(2); MOVE_COUNT(0);
count_copies part;
reset();
std::pair<count_copies const&, count_copies const&> a_ref(part, part);
x.emplace(a_ref);
COPY_COUNT(0); MOVE_COUNT(0);
// No move should take place.
reset();
x.emplace(std::move(a));
COPY_COUNT(0); MOVE_COUNT(0);
// Just in case a did get moved
std::pair<count_copies const, count_copies> b;
// This test requires a C++0x std::pair. Which gcc hasn't got yet.
//reset();
//x.emplace(b.first.tag_);
//COPY_COUNT(2); MOVE_COUNT(0);
//
// 2 arguments
//
reset();
x.emplace(b.first, b.second);
COPY_COUNT(0); MOVE_COUNT(0);
reset();
x.emplace(source<count_copies>(), source<count_copies>());
COPY_COUNT(2); MOVE_COUNT(0);
// source<count_copies> creates a single copy.
reset();
x.emplace(b.first, source<count_copies>());
COPY_COUNT(1); MOVE_COUNT(0);
reset();
x.emplace(b.first.tag_, b.second.tag_);
COPY_COUNT(2); MOVE_COUNT(0);
}
#endif
}
RUN_TESTS()