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
@@ -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
@@ -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>
+29
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));
+30
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));