forked from boostorg/unordered
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:
@@ -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));
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user