mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-11 21:44:01 +00:00
Add a test that unnecessary copies aren't made of objects inserted.
Make the exception tests track the allocator usage. Speed up the exception tests a bit. [SVN r3490]
This commit is contained in:
parent
ec310f7b80
commit
8d8f9e1942
@ -5,7 +5,8 @@
|
||||
|
||||
import testing ;
|
||||
|
||||
alias framework : /boost/test//boost_unit_test_framework/<optimization>speed ;
|
||||
#alias framework : /boost/test//boost_unit_test_framework/<optimization>speed ;
|
||||
alias framework : /boost/test//boost_unit_test_framework ;
|
||||
|
||||
project unordered-test/exception-tests
|
||||
: requirements
|
||||
|
@ -27,7 +27,7 @@ struct erase_test_base : public test::exception_base
|
||||
void check(T const& x) const {
|
||||
std::string scope(test::scope);
|
||||
|
||||
BOOST_CHECK(scope.find("hash::") != std::string::npos ||
|
||||
HASH_CHECK(scope.find("hash::") != std::string::npos ||
|
||||
scope.find("equal_to::") != std::string::npos ||
|
||||
scope == "operator==(object, object)");
|
||||
|
||||
|
@ -24,7 +24,7 @@ struct self_swap_base : public test::exception_base
|
||||
std::string scope(test::scope);
|
||||
|
||||
#if BOOST_UNORDERED_SWAP_METHOD != 2
|
||||
BOOST_CHECK(
|
||||
HASH_CHECK(
|
||||
scope == "hash::operator(hash)" ||
|
||||
scope == "hash::operator=(hash)" ||
|
||||
scope == "equal_to::operator(equal_to)" ||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include "../helpers/fwd.hpp"
|
||||
#include <map>
|
||||
|
||||
#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq))
|
||||
@ -37,7 +38,6 @@
|
||||
}
|
||||
|
||||
#define SCOPE(scope_name) \
|
||||
BOOST_ITEST_SCOPE(scope_name); \
|
||||
for(::test::scope_guard unordered_test_guard( \
|
||||
BOOST_STRINGIZE(scope_name)); \
|
||||
!unordered_test_guard.dismissed(); \
|
||||
@ -53,6 +53,8 @@
|
||||
#define DISABLE_EXCEPTIONS \
|
||||
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false)
|
||||
|
||||
#define HASH_CHECK(test) if(!(test)) BOOST_ERROR(BOOST_STRINGIZE(test))
|
||||
|
||||
namespace test {
|
||||
static char const* scope = "";
|
||||
bool exceptions_enabled = false;
|
||||
@ -175,6 +177,161 @@ namespace test
|
||||
{
|
||||
namespace exception
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// This annoymous namespace won't cause ODR violations as I won't
|
||||
// be linking multiple translation units together. I'll probably
|
||||
// move this into a cpp file before a full release, but for now it's
|
||||
// the most convenient way.
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
struct malloc_allocator
|
||||
{
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T const* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
template <class U> struct rebind { typedef malloc_allocator<U> other; };
|
||||
|
||||
malloc_allocator() {}
|
||||
template <class Y> malloc_allocator(malloc_allocator<Y> const& x) {}
|
||||
malloc_allocator(malloc_allocator const& x) {}
|
||||
|
||||
pointer address(reference r) { return &r; }
|
||||
const_pointer address(const_reference r) { return &r; }
|
||||
|
||||
pointer allocate(size_type n) {
|
||||
return static_cast<T*>(malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, const_pointer u) { return allocate(n); }
|
||||
void deallocate(pointer p, size_type n) { free(p); }
|
||||
void construct(pointer p, T const& t) { new(p) T(t); }
|
||||
void destroy(pointer p) { p->~T(); }
|
||||
|
||||
size_type max_size() const {
|
||||
return (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
|
||||
bool operator==(malloc_allocator const& x) const { return true; }
|
||||
bool operator!=(malloc_allocator const& x) const { return false; }
|
||||
};
|
||||
|
||||
struct memory_area {
|
||||
void const* start;
|
||||
void const* end;
|
||||
|
||||
memory_area(void const* s, void const* e)
|
||||
: start(s), end(e)
|
||||
{
|
||||
}
|
||||
|
||||
// This is a bit dodgy as it defines overlapping
|
||||
// areas as 'equal', so this isn't a total ordering.
|
||||
// But it is for non-overlapping memory regions - which
|
||||
// is what'll be stored.
|
||||
//
|
||||
// All searches will be for areas entirely contained by
|
||||
// a member of the set - so it should find the area that contains
|
||||
// the region that is searched for.
|
||||
bool operator<(memory_area const& other) const {
|
||||
return end < other.start;
|
||||
}
|
||||
};
|
||||
|
||||
struct memory_track {
|
||||
explicit memory_track(int tag = -1) :
|
||||
tag_(tag) {}
|
||||
|
||||
int tag_;
|
||||
};
|
||||
|
||||
typedef std::map<memory_area, memory_track, std::less<memory_area>,
|
||||
malloc_allocator<std::pair<memory_area const, memory_track> > >
|
||||
allocated_memory_type;
|
||||
allocated_memory_type allocated_memory;
|
||||
unsigned int count_allocators = 0;
|
||||
unsigned int count_allocations = 0;
|
||||
unsigned int count_constructions = 0;
|
||||
}
|
||||
|
||||
void allocator_ref()
|
||||
{
|
||||
if(count_allocators == 0) {
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
}
|
||||
++count_allocators;
|
||||
}
|
||||
|
||||
void allocator_unref()
|
||||
{
|
||||
HASH_CHECK(count_allocators > 0);
|
||||
if(count_allocators > 0) {
|
||||
--count_allocators;
|
||||
if(count_allocators == 0) {
|
||||
bool no_allocations_left = (count_allocations == 0);
|
||||
bool no_constructions_left = (count_constructions == 0);
|
||||
bool allocated_memory_empty = allocated_memory.empty();
|
||||
|
||||
// Clearing the data before the checks terminate the tests.
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
|
||||
HASH_CHECK(no_allocations_left);
|
||||
HASH_CHECK(no_constructions_left);
|
||||
HASH_CHECK(allocated_memory_empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag)
|
||||
{
|
||||
if(n == 0) {
|
||||
BOOST_ERROR("Allocating 0 length array.");
|
||||
}
|
||||
else {
|
||||
++count_allocations;
|
||||
allocated_memory[memory_area(ptr, (char*) ptr + n * size)] =
|
||||
memory_track(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag)
|
||||
{
|
||||
allocated_memory_type::iterator pos
|
||||
= allocated_memory.find(memory_area(ptr, ptr));
|
||||
if(pos == allocated_memory.end()) {
|
||||
BOOST_ERROR("Deallocating unknown pointer.");
|
||||
} else {
|
||||
HASH_CHECK(pos->first.start == ptr);
|
||||
HASH_CHECK(pos->first.end == (char*) ptr + n * size);
|
||||
HASH_CHECK(pos->second.tag_ == tag);
|
||||
allocated_memory.erase(pos);
|
||||
}
|
||||
HASH_CHECK(count_allocations > 0);
|
||||
if(count_allocations > 0) --count_allocations;
|
||||
}
|
||||
|
||||
void track_construct(void* ptr, std::size_t /*size*/, int tag)
|
||||
{
|
||||
++count_constructions;
|
||||
}
|
||||
|
||||
void track_destroy(void* ptr, std::size_t /*size*/, int tag)
|
||||
{
|
||||
HASH_CHECK(count_constructions > 0);
|
||||
if(count_constructions > 0) --count_constructions;
|
||||
}
|
||||
}
|
||||
|
||||
class object;
|
||||
class hash;
|
||||
class equal_to;
|
||||
@ -379,6 +536,7 @@ namespace exception
|
||||
class allocator
|
||||
{
|
||||
public:
|
||||
int tag_;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
@ -389,32 +547,38 @@ namespace exception
|
||||
|
||||
template <class U> struct rebind { typedef allocator<U> other; };
|
||||
|
||||
explicit allocator(int = 0)
|
||||
explicit allocator(int t = 0) : tag_(t)
|
||||
{
|
||||
SCOPE(allocator::allocator()) {
|
||||
EPOINT("Mock allocator default constructor.");
|
||||
}
|
||||
detail::allocator_ref();
|
||||
}
|
||||
|
||||
template <class Y> allocator(allocator<Y> const&)
|
||||
template <class Y> allocator(allocator<Y> const& x) : tag_(x.tag_)
|
||||
{
|
||||
SCOPE(allocator::allocator()) {
|
||||
EPOINT("Mock allocator template copy constructor.");
|
||||
}
|
||||
detail::allocator_ref();
|
||||
}
|
||||
|
||||
allocator(allocator const&)
|
||||
allocator(allocator const& x) : tag_(x.tag_)
|
||||
{
|
||||
SCOPE(allocator::allocator()) {
|
||||
EPOINT("Mock allocator copy constructor.");
|
||||
}
|
||||
detail::allocator_ref();
|
||||
}
|
||||
|
||||
~allocator() {}
|
||||
~allocator() {
|
||||
detail::allocator_unref();
|
||||
}
|
||||
|
||||
allocator& operator=(allocator const&) {
|
||||
allocator& operator=(allocator const& x) {
|
||||
SCOPE(allocator::allocator()) {
|
||||
EPOINT("Mock allocator assignment operator.");
|
||||
tag_ = x.tag_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -446,6 +610,7 @@ namespace exception
|
||||
ptr = (T*) malloc(n * sizeof(T));
|
||||
if(!ptr) throw std::bad_alloc();
|
||||
}
|
||||
detail::track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return pointer(ptr);
|
||||
|
||||
//return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
@ -461,15 +626,17 @@ namespace exception
|
||||
ptr = (T*) malloc(n * sizeof(T));
|
||||
if(!ptr) throw std::bad_alloc();
|
||||
}
|
||||
detail::track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return pointer(ptr);
|
||||
|
||||
//return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type)
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
//::operator delete((void*) p);
|
||||
if(p) {
|
||||
detail::track_deallocate((void*) p, n, sizeof(T), tag_);
|
||||
using namespace std;
|
||||
free(p);
|
||||
}
|
||||
@ -480,9 +647,13 @@ namespace exception
|
||||
EPOINT("Mock allocator construct function.");
|
||||
new(p) T(t);
|
||||
}
|
||||
detail::track_construct((void*) p, sizeof(T), tag_);
|
||||
}
|
||||
|
||||
void destroy(pointer p) { p->~T(); }
|
||||
void destroy(pointer p) {
|
||||
detail::track_destroy((void*) p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
}
|
||||
|
||||
size_type max_size() const {
|
||||
SCOPE(allocator::construct(pointer, T)) {
|
||||
@ -496,12 +667,12 @@ namespace exception
|
||||
// two can throw. So they don't.
|
||||
|
||||
template <class T>
|
||||
inline bool operator==(allocator<T> const&, allocator<T> const&)
|
||||
inline bool operator==(allocator<T> const& x, allocator<T> const& y)
|
||||
{
|
||||
//SCOPE(operator==(allocator, allocator)) {
|
||||
// EPOINT("Mock allocator equality operator.");
|
||||
//}
|
||||
return true;
|
||||
return x.tag_ == y.tag_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -510,9 +681,10 @@ namespace exception
|
||||
//SCOPE(operator!=(allocator, allocator)) {
|
||||
// EPOINT("Mock allocator inequality operator.");
|
||||
//}
|
||||
return false;
|
||||
return x.tag_ != y.tag_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -154,6 +154,10 @@ namespace test
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// This annoymous namespace won't cause ODR violations as I won't
|
||||
// be linking multiple translation units together. I'll probably
|
||||
// move this into a cpp file before a full release, but for now it's
|
||||
// the most convenient way.
|
||||
namespace {
|
||||
struct memory_area {
|
||||
void const* start;
|
||||
@ -186,7 +190,8 @@ namespace test
|
||||
int tag_;
|
||||
};
|
||||
|
||||
std::map<memory_area, memory_track> allocated_memory;
|
||||
typedef std::map<memory_area, memory_track> allocated_memory_type;
|
||||
allocated_memory_type allocated_memory;
|
||||
unsigned int count_allocators = 0;
|
||||
unsigned int count_allocations = 0;
|
||||
unsigned int count_constructions = 0;
|
||||
@ -238,7 +243,7 @@ namespace test
|
||||
|
||||
void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag)
|
||||
{
|
||||
std::map<memory_area, memory_track>::iterator pos
|
||||
allocated_memory_type::iterator pos
|
||||
= allocated_memory.find(memory_area(ptr, ptr));
|
||||
if(pos == allocated_memory.end()) {
|
||||
BOOST_ERROR("Deallocating unknown pointer.");
|
||||
@ -255,7 +260,7 @@ namespace test
|
||||
|
||||
void track_construct(void* ptr, std::size_t /*size*/, int tag)
|
||||
{
|
||||
std::map<memory_area, memory_track>::iterator pos
|
||||
allocated_memory_type::iterator pos
|
||||
= allocated_memory.find(memory_area(ptr, ptr));
|
||||
if(pos == allocated_memory.end())
|
||||
BOOST_ERROR("Constructing unknown pointer.");
|
||||
@ -266,7 +271,7 @@ namespace test
|
||||
|
||||
void track_destroy(void* ptr, std::size_t /*size*/, int tag)
|
||||
{
|
||||
std::map<memory_area, memory_track>::iterator pos
|
||||
allocated_memory_type::iterator pos
|
||||
= allocated_memory.find(memory_area(ptr, ptr));
|
||||
if(pos == allocated_memory.end())
|
||||
BOOST_ERROR("Destroying unknown pointer.");
|
||||
@ -349,7 +354,7 @@ namespace test
|
||||
return tag_ != x.tag_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
bool equivalent_impl(allocator<T> const& x, allocator<T> const& y, test::derived_type) {
|
||||
return x == y;
|
||||
|
@ -18,6 +18,7 @@ test-suite unordered-tests
|
||||
[ run copy_tests.cpp ]
|
||||
[ run assign_tests.cpp ]
|
||||
[ run insert_tests.cpp ]
|
||||
[ run unnecessary_copy_tests.cpp ]
|
||||
[ run erase_tests.cpp ]
|
||||
[ run erase_equiv_tests.cpp ]
|
||||
[ run find_tests.cpp ]
|
||||
|
@ -74,11 +74,10 @@ void swap_tests2(X* ptr = 0)
|
||||
X x(v.begin(), v.end(), 0, hasher(1), key_equal(1));
|
||||
X y(0, hasher(2), key_equal(2));
|
||||
swap_test_impl(x, y);
|
||||
swap_test_impl(x, y);
|
||||
}
|
||||
|
||||
{
|
||||
test::random_values<X> vx(1000), vy(1000);
|
||||
test::random_values<X> vx(100), vy(50);
|
||||
X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1));
|
||||
X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2));
|
||||
swap_test_impl(x, y);
|
||||
@ -87,7 +86,7 @@ void swap_tests2(X* ptr = 0)
|
||||
|
||||
#if BOOST_UNORDERED_SWAP_METHOD == 1
|
||||
{
|
||||
test::random_values<X> vx(1000), vy(1000);
|
||||
test::random_values<X> vx(100), vy(50);
|
||||
X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1));
|
||||
X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2));
|
||||
try {
|
||||
@ -97,15 +96,14 @@ void swap_tests2(X* ptr = 0)
|
||||
}
|
||||
#else
|
||||
{
|
||||
test::random_values<X> vx(1000), vy(1000);
|
||||
test::random_values<X> vx(50), vy(100);
|
||||
X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1));
|
||||
X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2));
|
||||
swap_test_impl(x, y);
|
||||
swap_test_impl(x, y);
|
||||
}
|
||||
|
||||
{
|
||||
test::random_values<X> vx(1000), vy(1000);
|
||||
test::random_values<X> vx(100), vy(100);
|
||||
X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1), allocator_type(1));
|
||||
X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2));
|
||||
swap_test_impl(x, y);
|
||||
|
49
test/unordered/unnecessary_copy_tests.cpp
Normal file
49
test/unordered/unnecessary_copy_tests.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
// Copyright 2006 Daniel James.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
|
||||
struct count_copies
|
||||
{
|
||||
static int count;
|
||||
count_copies() { ++count; }
|
||||
count_copies(count_copies const& x) { ++count; }
|
||||
private:
|
||||
count_copies& operator=(count_copies const&);
|
||||
};
|
||||
|
||||
std::size_t hash_value(count_copies const& x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator==(count_copies const& x, count_copies const& y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int count_copies::count;
|
||||
|
||||
template <class T>
|
||||
void unnecessary_copy_test(T*)
|
||||
{
|
||||
count_copies::count = 0;
|
||||
T x;
|
||||
typename T::value_type a;
|
||||
BOOST_TEST(count_copies::count == 1);
|
||||
x.insert(a);
|
||||
BOOST_TEST(count_copies::count == 2);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
unnecessary_copy_test((boost::unordered_set<count_copies>*) 0);
|
||||
unnecessary_copy_test((boost::unordered_multiset<count_copies>*) 0);
|
||||
unnecessary_copy_test((boost::unordered_map<int, count_copies>*) 0);
|
||||
unnecessary_copy_test((boost::unordered_multimap<int, count_copies>*) 0);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user