mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
Unordered: Implement allocator propagation on assignment.
It's pretty messy because I'm trying to avoid swapping allocators in these cases. I'm also not sure of the exception requirements of allocator swap and assignment. [SVN r73756]
This commit is contained in:
@ -114,6 +114,17 @@ namespace boost { namespace unordered { namespace detail {
|
||||
size_(),
|
||||
allocators_(b.allocators_)
|
||||
{
|
||||
swap(b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buckets(table<T>& x, move_tag)
|
||||
: buckets_(),
|
||||
bucket_count_(x.bucket_count_),
|
||||
allocators_(x.allocators_)
|
||||
{
|
||||
swap(x);
|
||||
x.size_ = 0;
|
||||
}
|
||||
|
||||
inline ~buckets()
|
||||
@ -152,10 +163,10 @@ namespace boost { namespace unordered { namespace detail {
|
||||
std::swap(size_, other.size_);
|
||||
}
|
||||
|
||||
void move(buckets& other)
|
||||
void move_buckets_from(buckets& other)
|
||||
{
|
||||
BOOST_ASSERT(node_alloc() == other.node_alloc());
|
||||
if(this->buckets_) { this->delete_buckets(); }
|
||||
BOOST_ASSERT(!this->buckets_);
|
||||
this->buckets_ = other.buckets_;
|
||||
this->bucket_count_ = other.bucket_count_;
|
||||
this->size_ = other.size_;
|
||||
@ -754,6 +765,7 @@ namespace boost { namespace unordered { namespace detail {
|
||||
node_ptr first_node = a.release();
|
||||
node::set_hash(first_node, hash);
|
||||
node_ptr end = prev->next_ = first_node;
|
||||
++dst.size_;
|
||||
|
||||
for(n = n->next_; n != group_end; n = n->next_) {
|
||||
a.construct(boost::move(node::get_value(n)));
|
||||
|
@ -21,6 +21,11 @@
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
// Some forward declarations for buckets and tables
|
||||
|
||||
template <typename T> class table;
|
||||
template <class A, bool Unique> class buckets;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This section implements buckets and nodes. Here's a rough
|
||||
|
@ -43,7 +43,7 @@ namespace boost { namespace unordered { namespace detail {
|
||||
// Members
|
||||
|
||||
float mlf_;
|
||||
std::size_t max_load_;
|
||||
std::size_t max_load_; // Only use if this->buckets_.
|
||||
|
||||
// Helper methods
|
||||
|
||||
@ -195,20 +195,18 @@ namespace boost { namespace unordered { namespace detail {
|
||||
table(table& x, move_tag m)
|
||||
: buckets(x, m),
|
||||
functions(x),
|
||||
mlf_(1.0f),
|
||||
max_load_(0)
|
||||
{
|
||||
this->partial_swap(x);
|
||||
}
|
||||
mlf_(x.mlf_),
|
||||
max_load_(this->buckets_ ? calculate_max_load() : 0) {}
|
||||
|
||||
// TODO: Why do I use x's bucket count?
|
||||
table(table& x, node_allocator const& a, move_tag m)
|
||||
: buckets(a, x.bucket_count_),
|
||||
functions(x),
|
||||
mlf_(x.mlf_),
|
||||
max_load_(0)
|
||||
max_load_(x.max_load_)
|
||||
{
|
||||
if(a == x.node_alloc()) {
|
||||
this->partial_swap(x);
|
||||
this->buckets::swap(x, false_type());
|
||||
}
|
||||
else if(x.size_) {
|
||||
// Use a temporary table because move_buckets_to leaves the
|
||||
@ -230,44 +228,75 @@ namespace boost { namespace unordered { namespace detail {
|
||||
}
|
||||
|
||||
void assign(table const& x)
|
||||
{
|
||||
assign(x, integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_copy_assignment::value>());
|
||||
}
|
||||
|
||||
void assign(table const& x, false_type)
|
||||
{
|
||||
table tmp(x, this->node_alloc());
|
||||
this->fast_swap(tmp);
|
||||
this->swap(tmp, false_type());
|
||||
}
|
||||
|
||||
void assign(table const& x, true_type)
|
||||
{
|
||||
table tmp(x, x.node_alloc());
|
||||
// Need to delete before setting the allocator so that buckets
|
||||
// aren't deleted with the wrong allocator.
|
||||
if(this->buckets_) this->delete_buckets();
|
||||
// TODO: Can allocator assignment throw?
|
||||
this->allocators_ = x.allocators_;
|
||||
this->swap(tmp, false_type());
|
||||
}
|
||||
|
||||
void move_assign(table& x)
|
||||
{
|
||||
// This can throw, but it only affects the function objects
|
||||
// that aren't in use so it is strongly exception safe, via.
|
||||
// double buffering.
|
||||
set_hash_functions<hasher, key_equal> new_func_this(*this, x);
|
||||
|
||||
move_assign(x, integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_move_assignment::value>());
|
||||
}
|
||||
|
||||
void move_assign(table& x, true_type)
|
||||
{
|
||||
if(this->buckets_) this->delete_buckets();
|
||||
this->allocators_ = x.allocators_; // TODO: Move allocators, not copy.
|
||||
move_assign_no_alloc(x);
|
||||
}
|
||||
|
||||
void move_assign(table& x, false_type)
|
||||
{
|
||||
if(this->node_alloc() == x.node_alloc()) {
|
||||
this->buckets::move(x); // no throw
|
||||
this->mlf_ = x.mlf_;
|
||||
this->max_load_ = x.max_load_;
|
||||
if(this->buckets_) this->delete_buckets();
|
||||
move_assign_no_alloc(x);
|
||||
}
|
||||
else {
|
||||
// Create new buckets in separate buckets
|
||||
// which will clean up if anything throws an exception.
|
||||
|
||||
buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_));
|
||||
set_hash_functions<hasher, key_equal> new_func_this(*this, x);
|
||||
|
||||
if (x.size_) {
|
||||
// Use a temporary table because move_buckets_to leaves the
|
||||
// source container in a complete mess.
|
||||
buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_));
|
||||
buckets tmp(x, move_tag());
|
||||
tmp.move_buckets_to(b);
|
||||
b.swap(*this);
|
||||
this->mlf_ = x.mlf_;
|
||||
this->max_load_ = calculate_max_load();
|
||||
}
|
||||
else {
|
||||
b.swap(*this);
|
||||
this->mlf_ = x.mlf_;
|
||||
this->clear();
|
||||
}
|
||||
|
||||
this->mlf_ = x.mlf_;
|
||||
if (this->buckets_) this->max_load_ = calculate_max_load();
|
||||
new_func_this.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void move_assign_no_alloc(table& x)
|
||||
{
|
||||
set_hash_functions<hasher, key_equal> new_func_this(*this, x);
|
||||
// No throw from here.
|
||||
this->move_buckets_from(x);
|
||||
this->mlf_ = x.mlf_;
|
||||
this->max_load_ = x.max_load_;
|
||||
new_func_this.commit();
|
||||
}
|
||||
|
||||
@ -276,40 +305,28 @@ namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
void swap(table& x)
|
||||
{
|
||||
// TODO: Should I actually swap the buckets even if this
|
||||
// is with self?
|
||||
if(this != &x) {
|
||||
{
|
||||
set_hash_functions<hasher, key_equal> op1(*this, x);
|
||||
set_hash_functions<hasher, key_equal> op2(x, *this);
|
||||
|
||||
this->buckets::swap(x, integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_swap::value>());
|
||||
|
||||
op1.commit();
|
||||
op2.commit();
|
||||
}
|
||||
|
||||
std::swap(this->mlf_, x.mlf_);
|
||||
std::swap(this->max_load_, x.max_load_);
|
||||
}
|
||||
swap(x, integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_swap::value>());
|
||||
}
|
||||
|
||||
// Swap everything but the allocators
|
||||
void fast_swap(table& x)
|
||||
// Only swaps the allocators if Propagate::value
|
||||
template <typename Propagate>
|
||||
void swap(table& x, Propagate p)
|
||||
{
|
||||
{
|
||||
set_hash_functions<hasher, key_equal> op1(*this, x);
|
||||
set_hash_functions<hasher, key_equal> op2(x, *this);
|
||||
op1.commit();
|
||||
op2.commit();
|
||||
}
|
||||
partial_swap(x);
|
||||
set_hash_functions<hasher, key_equal> op1(*this, x);
|
||||
set_hash_functions<hasher, key_equal> op2(x, *this);
|
||||
// I think swap can throw if Propagate::value,
|
||||
// since the allocators' swap can throw. Not sure though.
|
||||
this->buckets::swap(x, p);
|
||||
std::swap(this->mlf_, x.mlf_);
|
||||
std::swap(this->max_load_, x.max_load_);
|
||||
op1.commit();
|
||||
op2.commit();
|
||||
}
|
||||
|
||||
// Swap everything but the allocators, and the functions objects.
|
||||
void partial_swap(table& x)
|
||||
void swap_contents(table& x)
|
||||
{
|
||||
this->buckets::swap(x, false_type());
|
||||
std::swap(this->mlf_, x.mlf_);
|
||||
@ -393,24 +410,22 @@ namespace boost { namespace unordered { namespace detail {
|
||||
template <class T>
|
||||
inline bool table<T>::reserve_for_insert(std::size_t size)
|
||||
{
|
||||
if(size >= max_load_) {
|
||||
if (!this->buckets_) {
|
||||
std::size_t old_bucket_count = this->bucket_count_;
|
||||
this->bucket_count_ = (std::max)(this->bucket_count_,
|
||||
this->min_buckets_for_size(size));
|
||||
this->create_buckets();
|
||||
if (!this->buckets_) {
|
||||
std::size_t old_bucket_count = this->bucket_count_;
|
||||
this->bucket_count_ = (std::max)(this->bucket_count_,
|
||||
this->min_buckets_for_size(size));
|
||||
this->create_buckets();
|
||||
this->max_load_ = calculate_max_load();
|
||||
return old_bucket_count != this->bucket_count_;
|
||||
}
|
||||
else if(size >= max_load_) {
|
||||
std::size_t num_buckets
|
||||
= this->min_buckets_for_size((std::max)(size,
|
||||
this->size_ + (this->size_ >> 1)));
|
||||
if (num_buckets != this->bucket_count_) {
|
||||
this->rehash_impl(num_buckets);
|
||||
this->max_load_ = calculate_max_load();
|
||||
return old_bucket_count != this->bucket_count_;
|
||||
}
|
||||
else {
|
||||
std::size_t num_buckets
|
||||
= this->min_buckets_for_size((std::max)(size,
|
||||
this->size_ + (this->size_ >> 1)));
|
||||
if (num_buckets != this->bucket_count_) {
|
||||
this->rehash_impl(num_buckets);
|
||||
this->max_load_ = calculate_max_load();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,7 +443,6 @@ namespace boost { namespace unordered { namespace detail {
|
||||
if(!this->size_) {
|
||||
if(this->buckets_) this->delete_buckets();
|
||||
this->bucket_count_ = next_prime(min_buckets);
|
||||
this->max_load_ = 0;
|
||||
}
|
||||
else {
|
||||
// no throw:
|
||||
|
@ -316,7 +316,14 @@ namespace boost { namespace unordered { namespace detail {
|
||||
void insert_range_impl(key_type const&, InputIt i, InputIt j)
|
||||
{
|
||||
node_constructor a(*this);
|
||||
|
||||
|
||||
// Special case for empty buckets so that we can use
|
||||
// max_load_ (which isn't valid when buckets_ is null).
|
||||
if (!this->buckets_) {
|
||||
insert_range_empty(a, extractor::extract(*i), i, j);
|
||||
if (++i == j) return;
|
||||
}
|
||||
|
||||
do {
|
||||
// Note: can't use get_key as '*i' might not be value_type - it
|
||||
// could be a pair with first_types as key_type without const or a
|
||||
@ -329,6 +336,16 @@ namespace boost { namespace unordered { namespace detail {
|
||||
} while(++i != j);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range_empty(node_constructor& a, key_type const& k,
|
||||
InputIt i, InputIt j)
|
||||
{
|
||||
std::size_t hash = this->hash_function()(k);
|
||||
a.construct(*i);
|
||||
this->reserve_for_insert(this->size_ + insert_size(i, j));
|
||||
add_node(a, hash % this->bucket_count_, hash);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range_impl2(node_constructor& a, key_type const& k,
|
||||
InputIt i, InputIt j)
|
||||
|
@ -137,7 +137,7 @@ namespace test
|
||||
}
|
||||
|
||||
void track_deallocate(void* ptr, std::size_t n, std::size_t size,
|
||||
int tag)
|
||||
int tag, bool check_tag_ = true)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME allocated_memory_type::iterator pos =
|
||||
allocated_memory.find(
|
||||
@ -147,7 +147,7 @@ namespace test
|
||||
} else {
|
||||
BOOST_TEST(pos->first.start == ptr);
|
||||
BOOST_TEST(pos->first.end == (char*) ptr + n * size);
|
||||
BOOST_TEST(pos->second.tag_ == tag);
|
||||
if (check_tag_) BOOST_TEST(pos->second.tag_ == tag);
|
||||
allocated_memory.erase(pos);
|
||||
}
|
||||
BOOST_TEST(count_allocations > 0);
|
||||
@ -168,6 +168,38 @@ namespace test
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually required by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
test::detail::memory_tracker<std::allocator<int> > tracker;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Value>
|
||||
struct bool_type {
|
||||
enum { value = Value };
|
||||
};
|
||||
|
||||
struct true_type {
|
||||
enum { value = true };
|
||||
};
|
||||
|
||||
struct false_type {
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
template <typename Alloc>
|
||||
struct is_propagate_on_swap : false_type {};
|
||||
template <typename Alloc>
|
||||
struct is_propagate_on_assign : false_type {};
|
||||
template <typename Alloc>
|
||||
struct is_propagate_on_move : false_type {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
210
test/objects/cxx11_allocator.hpp
Normal file
210
test/objects/cxx11_allocator.hpp
Normal file
@ -0,0 +1,210 @@
|
||||
|
||||
// Copyright 2006-2011 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)
|
||||
|
||||
#if !defined(BOOST_UNORDERED_TEST_CXX11_ALLOCATOR_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_CXX11_ALLOCATOR_HEADER
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/limits.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
#include "../helpers/memory.hpp"
|
||||
|
||||
namespace test
|
||||
{
|
||||
enum allocator_flags
|
||||
{
|
||||
allocator_false = 0,
|
||||
select_copy = 1,
|
||||
propagate_swap = 2,
|
||||
propagate_assign = 4,
|
||||
propagate_move = 8,
|
||||
allocator_flags_all = 15,
|
||||
no_select_copy = allocator_flags_all - select_copy,
|
||||
no_propagate_swap = allocator_flags_all - propagate_swap,
|
||||
no_propagate_assign = allocator_flags_all - propagate_assign,
|
||||
no_propagate_move = allocator_flags_all - propagate_move
|
||||
};
|
||||
|
||||
template <int Flag>
|
||||
struct copy_allocator_base
|
||||
{
|
||||
// select_on_copy goes here.
|
||||
};
|
||||
|
||||
template <>
|
||||
struct copy_allocator_base<allocator_false> {};
|
||||
|
||||
template <int Flag>
|
||||
struct swap_allocator_base
|
||||
{
|
||||
struct propagate_on_container_swap {
|
||||
enum { value = Flag != allocator_false }; };
|
||||
};
|
||||
|
||||
template <int Flag>
|
||||
struct assign_allocator_base
|
||||
{
|
||||
struct propagate_on_container_copy_assignment {
|
||||
enum { value = Flag != allocator_false }; };
|
||||
};
|
||||
|
||||
template <int Flag>
|
||||
struct move_allocator_base
|
||||
{
|
||||
struct propagate_on_container_move_assignment {
|
||||
enum { value = Flag != allocator_false }; };
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
// boostinspect:nounnamed
|
||||
bool force_equal_allocator_value = false;
|
||||
}
|
||||
|
||||
struct force_equal_allocator
|
||||
{
|
||||
bool old_value_;
|
||||
|
||||
explicit force_equal_allocator(bool value)
|
||||
: old_value_(force_equal_allocator_value)
|
||||
{ force_equal_allocator_value = value; }
|
||||
|
||||
~force_equal_allocator()
|
||||
{ force_equal_allocator_value = old_value_; }
|
||||
};
|
||||
|
||||
template <typename T, allocator_flags Flags = propagate_swap>
|
||||
struct cxx11_allocator :
|
||||
public copy_allocator_base<Flags & select_copy>,
|
||||
public swap_allocator_base<Flags & propagate_swap>,
|
||||
public assign_allocator_base<Flags & propagate_assign>,
|
||||
public move_allocator_base<Flags & propagate_move>
|
||||
{
|
||||
int tag_;
|
||||
|
||||
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 <typename U> struct rebind {
|
||||
typedef cxx11_allocator<U, Flags> other;
|
||||
};
|
||||
|
||||
explicit cxx11_allocator(int t = 0) : tag_(t)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
template <typename Y> cxx11_allocator(
|
||||
cxx11_allocator<Y, Flags> const& x)
|
||||
: tag_(x.tag_)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x)
|
||||
: tag_(x.tag_)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
~cxx11_allocator()
|
||||
{
|
||||
detail::tracker.allocator_unref();
|
||||
}
|
||||
|
||||
pointer address(reference r)
|
||||
{
|
||||
return pointer(&r);
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r)
|
||||
{
|
||||
return const_pointer(&r);
|
||||
}
|
||||
|
||||
pointer allocate(size_type n) {
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, void const* u)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
// Only checking tags when propagating swap.
|
||||
// Note that tags will be tested
|
||||
// properly in the normal allocator.
|
||||
detail::tracker.track_deallocate((void*) p, n, sizeof(T), tag_,
|
||||
(Flags & propagate_swap));
|
||||
::operator delete((void*) p);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t) {
|
||||
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
|
||||
new(p) T(t);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
|
||||
template<typename... Args> void construct(T* p, Args&&... args) {
|
||||
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
|
||||
new(p) T(std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p) {
|
||||
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
}
|
||||
|
||||
size_type max_size() const {
|
||||
return (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
{
|
||||
return force_equal_allocator_value || (tag_ == x.tag_);
|
||||
}
|
||||
|
||||
bool operator!=(cxx11_allocator const& x) const
|
||||
{
|
||||
return !(*this == x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, allocator_flags Flags>
|
||||
bool equivalent_impl(
|
||||
cxx11_allocator<T, Flags> const& x,
|
||||
cxx11_allocator<T, Flags> const& y,
|
||||
test::derived_type)
|
||||
{
|
||||
return x.tag_ == y.tag_;
|
||||
}
|
||||
|
||||
template <typename T, allocator_flags Flags>
|
||||
struct is_propagate_on_swap<cxx11_allocator<T, Flags> >
|
||||
: bool_type<(bool)(Flags & propagate_swap)> {};
|
||||
template <typename T, allocator_flags Flags>
|
||||
struct is_propagate_on_assign<cxx11_allocator<T, Flags> >
|
||||
: bool_type<(bool)(Flags & propagate_assign)> {};
|
||||
template <typename T, allocator_flags Flags>
|
||||
struct is_propagate_on_move<cxx11_allocator<T, Flags> >
|
||||
: bool_type<(bool)(Flags & propagate_move)> {};
|
||||
}
|
||||
|
||||
#endif
|
@ -9,11 +9,9 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/limits.hpp>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include "../helpers/fwd.hpp"
|
||||
#include "../helpers/count.hpp"
|
||||
#include "../helpers/memory.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace test
|
||||
{
|
||||
@ -28,16 +26,6 @@ namespace test
|
||||
object generate(object const*);
|
||||
implicitly_convertible generate(implicitly_convertible const*);
|
||||
|
||||
struct true_type
|
||||
{
|
||||
enum { value = true };
|
||||
};
|
||||
|
||||
struct false_type
|
||||
{
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
class object : globally_counted_object
|
||||
{
|
||||
friend class hash;
|
||||
@ -195,18 +183,6 @@ namespace test
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually require by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
test::detail::memory_tracker<std::allocator<int> > tracker;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class allocator
|
||||
{
|
||||
@ -308,12 +284,15 @@ namespace test
|
||||
{
|
||||
return tag_ != x.tag_;
|
||||
}
|
||||
|
||||
typedef true_type propagate_on_container_copy_assignment;
|
||||
typedef true_type propagate_on_container_move_assignment;
|
||||
typedef true_type propagate_on_container_swap;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_propagate_on_swap<allocator<T> > : false_type {};
|
||||
template <typename T>
|
||||
struct is_propagate_on_assign<allocator<T> > : false_type {};
|
||||
template <typename T>
|
||||
struct is_propagate_on_move<allocator<T> > : false_type {};
|
||||
|
||||
template <class T>
|
||||
bool equivalent_impl(allocator<T> const& x, allocator<T> const& y,
|
||||
test::derived_type)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
@ -68,7 +69,9 @@ void assign_tests2(T*,
|
||||
BOOST_DEDUCED_TYPENAME T::key_equal eq2(2);
|
||||
BOOST_DEDUCED_TYPENAME T::allocator_type al1(1);
|
||||
BOOST_DEDUCED_TYPENAME T::allocator_type al2(2);
|
||||
|
||||
|
||||
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
|
||||
|
||||
std::cerr<<"assign_tests2.1\n";
|
||||
{
|
||||
test::check_instances check_;
|
||||
@ -92,7 +95,14 @@ void assign_tests2(T*,
|
||||
x2 = x1;
|
||||
BOOST_TEST(test::equivalent(x2.hash_function(), hf1));
|
||||
BOOST_TEST(test::equivalent(x2.key_eq(), eq1));
|
||||
BOOST_TEST(test::equivalent(x2.get_allocator(), al2));
|
||||
if (test::is_propagate_on_assign<allocator_type>::value) {
|
||||
BOOST_TEST(test::equivalent(x2.get_allocator(), al1));
|
||||
BOOST_TEST(!test::equivalent(x2.get_allocator(), al2));
|
||||
}
|
||||
else {
|
||||
BOOST_TEST(test::equivalent(x2.get_allocator(), al2));
|
||||
BOOST_TEST(!test::equivalent(x2.get_allocator(), al1));
|
||||
}
|
||||
test::check_container(x2, v1);
|
||||
}
|
||||
}
|
||||
@ -110,16 +120,56 @@ boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multimap;
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_set_prop_assign;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_multiset_prop_assign;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_map_prop_assign;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_multimap_prop_assign;
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_set_no_prop_assign;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_multiset_no_prop_assign;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_map_no_prop_assign;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_multimap_no_prop_assign;
|
||||
|
||||
using test::default_generator;
|
||||
using test::generate_collisions;
|
||||
|
||||
UNORDERED_TEST(assign_tests1,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
UNORDERED_TEST(assign_tests1, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_assign)(test_multiset_prop_assign)(test_map_prop_assign)(test_multimap_prop_assign)
|
||||
(test_set_no_prop_assign)(test_multiset_no_prop_assign)(test_map_no_prop_assign)(test_multimap_no_prop_assign)
|
||||
)
|
||||
((default_generator)(generate_collisions))
|
||||
)
|
||||
|
||||
UNORDERED_TEST(assign_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
UNORDERED_TEST(assign_tests2, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_assign)(test_multiset_prop_assign)(test_map_prop_assign)(test_multimap_prop_assign)
|
||||
(test_set_no_prop_assign)(test_multiset_no_prop_assign)(test_map_no_prop_assign)(test_multimap_no_prop_assign)
|
||||
)
|
||||
((default_generator)(generate_collisions))
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
@ -140,7 +141,7 @@ namespace move_tests
|
||||
BOOST_TEST(y.max_load_factor() == 2.0); // Not necessarily required.
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
/*
|
||||
|
||||
{
|
||||
test::check_instances check_;
|
||||
|
||||
@ -161,7 +162,57 @@ namespace move_tests
|
||||
BOOST_TEST(y.max_load_factor() == 1.0); // Not necessarily required.
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
*/ }
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void move_assign_tests2(T*,
|
||||
test::random_generator const& generator = test::default_generator)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME T::hasher hf(1);
|
||||
BOOST_DEDUCED_TYPENAME T::key_equal eq(1);
|
||||
BOOST_DEDUCED_TYPENAME T::allocator_type al1(1);
|
||||
BOOST_DEDUCED_TYPENAME T::allocator_type al2(2);
|
||||
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
|
||||
|
||||
{
|
||||
test::random_values<T> v(500, generator);
|
||||
test::random_values<T> v2(0, generator);
|
||||
T y(v.begin(), v.end(), 0, hf, eq, al1);
|
||||
test::object_count count;
|
||||
y = create(v2, count, hf, eq, al2, 2.0);
|
||||
BOOST_TEST(y.empty());
|
||||
test::check_container(y, v2);
|
||||
test::check_equivalent_keys(y);
|
||||
BOOST_TEST(y.max_load_factor() == 2.0);
|
||||
if (test::is_propagate_on_move<allocator_type>::value) {
|
||||
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
|
||||
}
|
||||
else {
|
||||
BOOST_TEST(test::equivalent(y.get_allocator(), al1));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
test::random_values<T> v(500, generator);
|
||||
test::object_count count;
|
||||
T y(0, hf, eq, al1);
|
||||
y = create(v, count, hf, eq, al2, 0.5);
|
||||
#if defined(BOOST_HAS_NRVO)
|
||||
if (test::is_propagate_on_move<allocator_type>::value) {
|
||||
BOOST_TEST(count == test::global_object_count);
|
||||
}
|
||||
#endif
|
||||
test::check_container(y, v);
|
||||
test::check_equivalent_keys(y);
|
||||
BOOST_TEST(y.max_load_factor() == 0.5);
|
||||
if (test::is_propagate_on_move<allocator_type>::value) {
|
||||
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
|
||||
}
|
||||
else {
|
||||
BOOST_TEST(test::equivalent(y.get_allocator(), al1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
@ -176,19 +227,68 @@ namespace move_tests
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multimap;
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_move> >*
|
||||
test_set_prop_move;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_move> >*
|
||||
test_multiset_prop_move;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_move> >*
|
||||
test_map_prop_move;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_move> >*
|
||||
test_multimap_prop_move;
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_move> >*
|
||||
test_set_no_prop_move;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_move> >*
|
||||
test_multiset_no_prop_move;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_move> >*
|
||||
test_map_no_prop_move;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_move> >*
|
||||
test_multimap_no_prop_move;
|
||||
|
||||
using test::default_generator;
|
||||
using test::generate_collisions;
|
||||
|
||||
UNORDERED_TEST(move_construct_tests1,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
UNORDERED_TEST(move_construct_tests1, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)
|
||||
(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move)
|
||||
)
|
||||
)
|
||||
UNORDERED_TEST(move_assign_tests1,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
UNORDERED_TEST(move_assign_tests1, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)
|
||||
(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move)
|
||||
)
|
||||
)
|
||||
UNORDERED_TEST(move_construct_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
UNORDERED_TEST(move_construct_tests2, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)
|
||||
(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move)
|
||||
)
|
||||
((default_generator)(generate_collisions))
|
||||
)
|
||||
UNORDERED_TEST(move_assign_tests2, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)
|
||||
(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
@ -107,15 +108,24 @@ void swap_tests2(X* ptr = 0,
|
||||
}
|
||||
|
||||
{
|
||||
test::force_equal_allocator force_(
|
||||
!test::is_propagate_on_swap<allocator_type>::value);
|
||||
test::check_instances check_;
|
||||
|
||||
test::random_values<X> vx(50, generator), vy(100, generator);
|
||||
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);
|
||||
|
||||
if (test::is_propagate_on_swap<allocator_type>::value ||
|
||||
x.get_allocator() == y.get_allocator())
|
||||
{
|
||||
swap_test_impl(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
test::force_equal_allocator force_(
|
||||
!test::is_propagate_on_swap<allocator_type>::value);
|
||||
test::check_instances check_;
|
||||
|
||||
test::random_values<X> vx(100, generator), vy(100, generator);
|
||||
@ -123,31 +133,74 @@ void swap_tests2(X* ptr = 0,
|
||||
allocator_type(1));
|
||||
X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2),
|
||||
allocator_type(2));
|
||||
swap_test_impl(x, y);
|
||||
swap_test_impl(x, y);
|
||||
|
||||
if (test::is_propagate_on_swap<allocator_type>::value ||
|
||||
x.get_allocator() == y.get_allocator())
|
||||
{
|
||||
swap_test_impl(x, y);
|
||||
swap_test_impl(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_set;
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_set;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multiset;
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multiset;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_map;
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_map;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multimap;
|
||||
test::hash, test::equal_to,
|
||||
test::allocator<test::object> >* test_multimap;
|
||||
|
||||
UNORDERED_TEST(swap_tests1,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
)
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_swap> >*
|
||||
test_set_prop_swap;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_swap> >*
|
||||
test_multiset_prop_swap;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_swap> >*
|
||||
test_map_prop_swap;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_swap> >*
|
||||
test_multimap_prop_swap;
|
||||
|
||||
UNORDERED_TEST(swap_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap))
|
||||
)
|
||||
boost::unordered_set<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_swap> >*
|
||||
test_set_no_prop_swap;
|
||||
boost::unordered_multiset<test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_swap> >*
|
||||
test_multiset_no_prop_swap;
|
||||
boost::unordered_map<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_swap> >*
|
||||
test_map_no_prop_swap;
|
||||
boost::unordered_multimap<test::object, test::object,
|
||||
test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_swap> >*
|
||||
test_multimap_no_prop_swap;
|
||||
|
||||
UNORDERED_TEST(swap_tests1, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_swap)(test_multiset_prop_swap)(test_map_prop_swap)(test_multimap_prop_swap)
|
||||
(test_set_no_prop_swap)(test_multiset_no_prop_swap)(test_map_no_prop_swap)(test_multimap_no_prop_swap)
|
||||
))
|
||||
|
||||
UNORDERED_TEST(swap_tests2, (
|
||||
(test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_prop_swap)(test_multiset_prop_swap)(test_map_prop_swap)(test_multimap_prop_swap)
|
||||
(test_set_no_prop_swap)(test_multiset_no_prop_swap)(test_map_no_prop_swap)(test_multimap_no_prop_swap)
|
||||
))
|
||||
|
||||
}
|
||||
RUN_TESTS()
|
||||
|
Reference in New Issue
Block a user