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
+20 -1
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));
+80
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))
+218 -18
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()