Unordered: Merge to release.

Using Boost.Move and better C++11 support.


[SVN r73987]
This commit is contained in:
Daniel James
2011-08-21 19:19:12 +00:00
parent 3fd5635d7d
commit a4372314c2
43 changed files with 7656 additions and 4602 deletions

View File

@ -134,4 +134,21 @@ First official release.
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
iterators which returns `value_type` by copy.
[h2 Boost 1.48.0]
This is major change which has been converted to use Boost.Move's move
emulation, and be more compliant with the C++11 standard. This has resulted
in some breaking changes:
* Equality comparison has been changed to the C++11 specification.
In a container with equivalent keys, elements in a group with equal
keys used to have to be in the same order to be considered equal,
now they can be a permutation of each other.
* The behaviour of swap is different when the two containers to be
swapped has unequal allocators. It used to allocate new nodes using
the appropriate allocators, it now swaps the allocators if
the allocator has a member structure `propagate_on_container_swap`,
such that `propagate_on_container_swap::value` is true.
[endsect]

54
doc/compliance.qbk Normal file
View File

@ -0,0 +1,54 @@
[/ Copyright 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) ]
[section:compliance C++11 Compliance]
/TODO/: Look into C++11's `std::pair`.
[section:allocator_compliance Use of allocators]
* Objects are not constructed using the allocator. The node containing them
is constructed using the allocator's `construct` function, but then the
object is constructed in a buffer in that node by calling the constructor
directly.
* Similarly the object is destructed by calling its destructor directly, and
then the allocator's `destroy` method is used to destruct the node.
* For most compilers `select_on_container_copy` is only detected for an
exact signature match in the allocator itself - not in a base. There is full
detection for g++ 4.4 or laster, Visual C++ 2008 or later, Clang and maybe
other compilers which support SFINAE for expressions.
* `pointer_traits` aren't used. Instead, pointer types are obtained from
rebound allocators.
* /TODO/: Any other defficiences of `allocator_traits` emulation.
* Pointers of base types are used to store the location of a derived type.
(/TODO/: I'm not sure if that isn't compliant).
[endsect]
[section:move Move emulation]
Move emulation is implemented using Boost.Move. If rvalue references are
available it will use them, but if not it uses a close, but imperfect emulation
and to get the advantage of using movable container elements, you'll need to
use Boost.Move.
* Non-copyable objects can be stored in the containers, but without support
for rvalue references the container will not be movable.
* The number of arguments used in emplace is limited to /TODO/.
* Argument forwarding is not perfect.
* /TODO/: Constructor call for pairs.
[endsect]
[section:other Other]
* When swapping, `Pred` and `Hash` are not currently swapped by calling
`swap`, their copy constructors are used.
* As a consequence when swapping an exception may be throw from their
copy constructor.
[endsect]
[endsect]

View File

@ -126,7 +126,7 @@ a little to accomodate non-C++0x compilers.
It isn't clear how to swap containers when their allocators aren't equal.
This is
[@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431
[@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#431
Issue 431: Swapping containers with unequal allocators]. This has been resolved
with the new allocator specification, so this should be fixed when
support is added.

1040
doc/ref.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -40,8 +40,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<row>
<entry><emphasis>Pred</emphasis></entry>
<entry>A binary function object that implements an equivalence relation on values of type <code>Value</code>.
A binary function object that induces an equivalence relation on values of type Key.
It takes two arguments of type Key and returns a value of type bool.</entry></row>
A binary function object that induces an equivalence relation on values of type <code>Value</code>.
It takes two arguments of type <code>Value</code> and returns a value of type bool.</entry></row>
<row>
<entry><emphasis>Alloc</emphasis></entry>
<entry>An allocator whose value type is the same as the container's value type.</entry></row></tbody></tgroup></informaltable></para>
@ -360,7 +360,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent value.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
<para><code>hint</code> is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value.</para>
@ -383,7 +383,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>std::pair&lt;iterator, bool&gt;</type>
<description>
<para>Inserts obj in the container if and only if there is no element in the container with an equivalent value.</para>
<para>Inserts <code>obj</code> in the container if and only if there is no element in the container with an equivalent value.</para>
</description>
<returns>
<para>The bool component of the return type is true if an insert took place.</para>
@ -406,7 +406,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container if and only if there is no element in the container with an equivalent value.</para>
<para>Inserts <code>obj</code> in the container if and only if there is no element in the container with an equivalent value.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
@ -815,8 +815,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="Equality Comparisons">
<function name="operator==">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -839,8 +839,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</function>
<function name="operator!=">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -865,8 +865,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="swap">
<function name="swap">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -909,7 +909,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</template-type-parameter>
</template>
<purpose><simpara>
An unordered associative container that stores values. The same key can be stored multiple times.
An unordered associative container that stores values. The same key can be stored multiple times.
</simpara></purpose>
<description>
<para>Based on chapter 23 of
@ -929,8 +929,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<row>
<entry><emphasis>Pred</emphasis></entry>
<entry>A binary function object that implements an equivalence relation on values of type <code>Value</code>.
A binary function object that induces an equivalence relation on values of type Key.
It takes two arguments of type Key and returns a value of type bool.</entry></row>
A binary function object that induces an equivalence relation on values of type <code>Value</code>.
It takes two arguments of type <code>Value</code> and returns a value of type bool.</entry></row>
<row>
<entry><emphasis>Alloc</emphasis></entry>
<entry>An allocator whose value type is the same as the container's value type.</entry></row></tbody></tgroup></informaltable></para>
@ -1248,7 +1248,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
<para><code>hint</code> is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
@ -1271,7 +1271,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container.</para>
<para>Inserts <code>obj</code> in the container.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
@ -1293,7 +1293,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container.</para>
<para>Inserts <code>obj</code> in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
@ -1321,7 +1321,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>void</type>
<description>
<para>Inserts a range of elements into the container.</para>
<para>Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value.</para>
</description>
<throws>
<para>When inserting a single element, if an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
@ -1702,8 +1702,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="Equality Comparisons">
<function name="operator==">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -1726,8 +1726,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</function>
<function name="operator!=">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -1752,8 +1752,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="swap">
<function name="swap">
<template>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Value">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -1798,7 +1798,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<default><type>std::equal_to&lt;Key&gt;</type></default>
</template-type-parameter>
<template-type-parameter name="Alloc">
<default><type>std::allocator&lt;std::pair&lt;Key const, Mapped&gt; &gt;</type></default>
<default><type>std::allocator&lt;std::pair&lt;Key const, Mapped&gt;&gt;</type></default>
</template-type-parameter>
</template>
<purpose><simpara>
@ -1825,8 +1825,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<row>
<entry><emphasis>Pred</emphasis></entry>
<entry>A binary function object that implements an equivalence relation on values of type <code>Key</code>.
A binary function object that induces an equivalence relation on values of type Key.
It takes two arguments of type Key and returns a value of type bool.</entry></row>
A binary function object that induces an equivalence relation on values of type <code>Key</code>.
It takes two arguments of type <code>Key</code> and returns a value of type bool.</entry></row>
<row>
<entry><emphasis>Alloc</emphasis></entry>
<entry>An allocator whose value type is the same as the container's value type.</entry></row></tbody></tgroup></informaltable></para>
@ -1882,7 +1882,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<typedef name="iterator">
<type><emphasis>implementation-defined</emphasis></type>
<description>
<para>A iterator whose value type is <type>value_type</type>. </para>
<para>An iterator whose value type is <type>value_type</type>. </para>
<para>The iterator category is at least a forward iterator.</para>
<para>Convertible to <type>const_iterator</type>.</para>
</description>
@ -2148,7 +2148,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container if and only if there is no element in the container with an equivalent key.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
<para><code>hint</code> is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.</para>
@ -2171,7 +2171,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>std::pair&lt;iterator, bool&gt;</type>
<description>
<para>Inserts obj in the container if and only if there is no element in the container with an equivalent key.</para>
<para>Inserts <code>obj</code> in the container if and only if there is no element in the container with an equivalent key.</para>
</description>
<returns>
<para>The bool component of the return type is true if an insert took place.</para>
@ -2194,7 +2194,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container if and only if there is no element in the container with an equivalent key.</para>
<para>Inserts <code>obj</code> in the container if and only if there is no element in the container with an equivalent key.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
@ -2638,10 +2638,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="Equality Comparisons">
<function name="operator==">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -2664,10 +2664,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</function>
<function name="operator!=">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -2692,10 +2692,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="swap">
<function name="swap">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -2736,11 +2736,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<default><type>std::equal_to&lt;Key&gt;</type></default>
</template-type-parameter>
<template-type-parameter name="Alloc">
<default><type>std::allocator&lt;std::pair&lt;Key const, Mapped&gt; &gt;</type></default>
<default><type>std::allocator&lt;std::pair&lt;Key const, Mapped&gt;&gt;</type></default>
</template-type-parameter>
</template>
<purpose><simpara>
An unordered associative container that associates keys with another value. The same key can be stored multiple times.
An unordered associative container that associates keys with another value. The same key can be stored multiple times.
</simpara></purpose>
<description>
<para>Based on chapter 23 of
@ -2763,8 +2763,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<row>
<entry><emphasis>Pred</emphasis></entry>
<entry>A binary function object that implements an equivalence relation on values of type <code>Key</code>.
A binary function object that induces an equivalence relation on values of type Key.
It takes two arguments of type Key and returns a value of type bool.</entry></row>
A binary function object that induces an equivalence relation on values of type <code>Key</code>.
It takes two arguments of type <code>Key</code> and returns a value of type bool.</entry></row>
<row>
<entry><emphasis>Alloc</emphasis></entry>
<entry>An allocator whose value type is the same as the container's value type.</entry></row></tbody></tgroup></informaltable></para>
@ -2820,7 +2820,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<typedef name="iterator">
<type><emphasis>implementation-defined</emphasis></type>
<description>
<para>A iterator whose value type is <type>value_type</type>. </para>
<para>An iterator whose value type is <type>value_type</type>. </para>
<para>The iterator category is at least a forward iterator.</para>
<para>Convertible to <type>const_iterator</type>.</para>
</description>
@ -3085,7 +3085,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<type>iterator</type>
<description>
<para>Inserts an object, constructed with the arguments <code>args</code>, in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
<para><code>hint</code> is a suggestion to where the element should be inserted.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
@ -3108,7 +3108,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container.</para>
<para>Inserts <code>obj</code> in the container.</para>
</description>
<returns>
<para>An iterator pointing to the inserted element.</para>
@ -3130,7 +3130,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>iterator</type>
<description>
<para>Inserts obj in the container.</para>
<para>Inserts <code>obj</code> in the container.</para>
<para>hint is a suggestion to where the element should be inserted.</para>
</description>
<returns>
@ -3158,7 +3158,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</parameter>
<type>void</type>
<description>
<para>Inserts a range of elements into the container.</para>
<para>Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key.</para>
</description>
<throws>
<para>When inserting a single element, if an exception is thrown by an operation other than a call to <code>hasher</code> the function has no effect.</para>
@ -3539,10 +3539,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="Equality Comparisons">
<function name="operator==">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -3565,10 +3565,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</function>
<function name="operator!=">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">
@ -3593,10 +3593,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<free-function-group name="swap">
<function name="swap">
<template>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Key">
</template-type-parameter>
<template-type-parameter name="Mapped">
</template-type-parameter>
<template-type-parameter name="Hash">
</template-type-parameter>
<template-type-parameter name="Pred">

View File

@ -31,6 +31,7 @@
[include:unordered buckets.qbk]
[include:unordered hash_equality.qbk]
[include:unordered comparison.qbk]
[include:unordered compliance.qbk]
[include:unordered rationale.qbk]
[include:unordered changes.qbk]
[xinclude ref.xml]

View File

@ -1,9 +1,12 @@
// Copyright 2005-2009 Daniel James.
// Copyright 2005-2011 Daniel James.
// Copyright 2009 Pablo Halpern.
//
// 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)
// A couple of templates to make using allocators easier.
//
// Written by Daniel James using some code from Pablo Halpern's
// allocator traits implementation.
#ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED
@ -13,6 +16,8 @@
#endif
#include <boost/config.hpp>
#include <boost/detail/select_type.hpp>
#include <boost/utility/enable_if.hpp>
#if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \
&& !defined(__BORLANDC__)
@ -23,24 +28,257 @@
# include <boost/detail/allocator_utilities.hpp>
#endif
namespace boost { namespace unordered_detail {
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS
# include <memory>
#endif
#if !defined(BOOST_NO_0X_HDR_TYPE_TRAITS)
#include <type_traits>
namespace boost { namespace unordered { namespace detail {
using std::integral_constant;
using std::true_type;
using std::false_type;
}}}
#else
namespace boost { namespace unordered { namespace detail {
template <typename T, T Value>
struct integral_constant { enum { value = Value }; };
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
}}}
#endif
// TODO: Use std::addressof if available?
#include <boost/utility/addressof.hpp>
namespace boost { namespace unordered { namespace detail {
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS
template <typename Alloc>
struct allocator_traits : std::allocator_traits<Alloc> {};
template <typename Alloc, typename T>
struct rebind_wrap
{
typedef typename allocator_traits<Alloc>::rebind_alloc<T> type;
};
#else
// rebind_wrap
//
// Rebind allocators. For some problematic libraries, use rebind_to
// from <boost/detail/allocator_utilities.hpp>.
#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
template <class Alloc, class T>
# if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
template <typename Alloc, typename T>
struct rebind_wrap : ::boost::detail::allocator::rebind_to<Alloc, T> {};
#else
template <class Alloc, class T>
# else
template <typename Alloc, typename T>
struct rebind_wrap
{
typedef BOOST_DEDUCED_TYPENAME
Alloc::BOOST_NESTED_TEMPLATE rebind<T>::other
type;
};
# endif
struct convertible_from_anything
{
template<typename T> convertible_from_anything(T const&);
};
typedef char (&no_type)[1];
typedef char (&yes_type)[2];
template <typename T> struct sfinae {
typedef yes_type type;
};
// Infrastructure for providing a default type for Tp::tname if absent.
#define BOOST_DEFAULT_TYPE_TMPLT(tname) \
template <typename Tp, typename Default> \
struct default_type_ ## tname { \
template <typename T> \
static BOOST_DEDUCED_TYPENAME sfinae< \
BOOST_DEDUCED_TYPENAME T::tname>::type test(int); \
template <typename T> \
static no_type test(long); \
\
enum { value = sizeof(test<Tp>(0)) == sizeof(yes_type) }; \
\
struct DefaultWrap { typedef Default tname; }; \
\
typedef BOOST_DEDUCED_TYPENAME \
boost::detail::if_true<value>:: \
BOOST_NESTED_TEMPLATE then<Tp, DefaultWrap> \
::type::tname type; \
}
#define BOOST_DEFAULT_TYPE(T,tname, arg) \
BOOST_DEDUCED_TYPENAME default_type_ ## tname<T, arg>::type
BOOST_DEFAULT_TYPE_TMPLT(pointer);
BOOST_DEFAULT_TYPE_TMPLT(const_pointer);
BOOST_DEFAULT_TYPE_TMPLT(void_pointer);
BOOST_DEFAULT_TYPE_TMPLT(const_void_pointer);
BOOST_DEFAULT_TYPE_TMPLT(difference_type);
BOOST_DEFAULT_TYPE_TMPLT(size_type);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap);
#if !defined(BOOST_NO_SFINAE_EXPR) || BOOST_WORKAROUND(BOOST_MSVC, >= 1500)
// Specialization is only needed for Visual C++. Without it SFINAE doesn't
// kick in.
template <unsigned int>
struct expr_sfinae;
template <>
struct expr_sfinae<sizeof(yes_type)> {
typedef yes_type type;
};
template <typename T>
struct has_select_on_container_copy_construction
{
// This needs to be a template for Visual C++.
template <typename T2>
static yes_type to_yes_type(const T2&);
template <typename T2>
static typename expr_sfinae<sizeof(to_yes_type(
((T2 const*)0)->select_on_container_copy_construction()
))>::type check(T2*);
static no_type check(void*);
enum { value = sizeof(check((T*) 0)) == sizeof(yes_type) };
};
#else
template <typename T>
struct has_select_on_container_copy_construction
{
typedef T (T::*SelectFunc)() const;
template <SelectFunc e> struct sfinae { typedef yes_type type; };
template <class U>
static typename sfinae<&U::select_on_container_copy_construction>::type
test(int);
template <class U>
static no_type test(...);
enum { value = sizeof(test<T>(1)) == sizeof(yes_type) };
};
#endif
template <typename Alloc>
inline BOOST_DEDUCED_TYPENAME boost::enable_if<
has_select_on_container_copy_construction<Alloc>, Alloc
>::type call_select_on_container_copy_construction(const Alloc& rhs)
{
return rhs.select_on_container_copy_construction();
}
template <typename Alloc>
inline BOOST_DEDUCED_TYPENAME boost::disable_if<
has_select_on_container_copy_construction<Alloc>, Alloc
>::type call_select_on_container_copy_construction(const Alloc& rhs)
{
return rhs;
}
template <typename Alloc>
struct allocator_traits
{
typedef Alloc allocator_type;
typedef typename Alloc::value_type value_type;
typedef BOOST_DEFAULT_TYPE(Alloc, pointer, value_type*)
pointer;
// For now always use the allocator's const_pointer.
//typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer,
// BOOST_DEDUCED_TYPENAME pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<const value_type>::other)
// const_pointer;
typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer, value_type const*)
const_pointer;
// I'm not using void pointers for now.
//typedef BOOST_DEFAULT_TYPE(Alloc, void_pointer,
// BOOST_NESTED_TEMPLATE pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<void>::other)
// void_pointer;
//typedef BOOST_DEFAULT_TYPE(Alloc, const_void_pointer,
// BOOST_DEDUCED_TYPENAME pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<const void>::other)
// const_void_pointer;
typedef BOOST_DEFAULT_TYPE(Alloc, difference_type, std::ptrdiff_t)
difference_type;
typedef BOOST_DEFAULT_TYPE(Alloc, size_type, std::size_t)
size_type;
// TODO: rebind_alloc and rebind_traits
static pointer allocate(Alloc& a, size_type n)
{ return a.allocate(n); }
// I never use this, so I'll just comment it out for now.
//
//static pointer allocate(Alloc& a, size_type n, const_void_pointer hint)
// { return DEFAULT_FUNC(allocate, pointer)(a, n, hint); }
static void deallocate(Alloc& a, pointer p, size_type n)
{ a.deallocate(p, n); }
// Only support the basic copy constructor
// template <typename T, typename... Args>
// static void construct(Alloc& a, T* p, Args&&... args) {
// DEFAULT_FUNC(construct,void)(a, p, std::forward<Args>(args)...);
// }
template <typename T>
static void construct(Alloc& a, T* p, T const& x) {
a.construct(p, x);
}
template <typename T>
static void destroy(Alloc& a, T* p) {
// DEFAULT_FUNC(destroy,void)(a, p);
a.destroy(p);
}
static size_type max_size(const Alloc& a)
{ return a.max_size(); }
// Allocator propagation on construction
static Alloc select_on_container_copy_construction(Alloc const& rhs)
{
return boost::unordered::detail::
call_select_on_container_copy_construction(rhs);
}
// Allocator propagation on assignment and swap.
// Return true if lhs is modified.
typedef BOOST_DEFAULT_TYPE(
Alloc, propagate_on_container_copy_assignment, false_type)
propagate_on_container_copy_assignment;
typedef BOOST_DEFAULT_TYPE(
Alloc,propagate_on_container_move_assignment, false_type)
propagate_on_container_move_assignment;
typedef BOOST_DEFAULT_TYPE(
Alloc,propagate_on_container_swap,false_type)
propagate_on_container_swap;
};
#endif
// allocator_array_constructor
@ -49,10 +287,11 @@ namespace boost { namespace unordered_detail {
// clean up if an exception is thrown before the container takes charge
// of it.
template <class Allocator>
template <typename Allocator>
struct allocator_array_constructor
{
typedef BOOST_DEDUCED_TYPENAME Allocator::pointer pointer;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<Allocator>::pointer
pointer;
Allocator& alloc_;
pointer ptr_;
@ -69,21 +308,23 @@ namespace boost { namespace unordered_detail {
~allocator_array_constructor() {
if (ptr_) {
for(pointer p = ptr_; p != constructed_; ++p)
alloc_.destroy(p);
allocator_traits<Allocator>::destroy(alloc_,
boost::addressof(*p));
alloc_.deallocate(ptr_, length_);
allocator_traits<Allocator>::deallocate(alloc_, ptr_, length_);
}
}
template <class V>
template <typename V>
void construct(V const& v, std::size_t l)
{
BOOST_ASSERT(!ptr_);
length_ = l;
ptr_ = alloc_.allocate(length_);
ptr_ = allocator_traits<Allocator>::allocate(alloc_, length_);
pointer end = ptr_ + static_cast<std::ptrdiff_t>(length_);
for(constructed_ = ptr_; constructed_ != end; ++constructed_)
alloc_.construct(constructed_, v);
allocator_traits<Allocator>::construct(alloc_,
boost::addressof(*constructed_), v);
}
pointer get() const
@ -102,7 +343,7 @@ namespace boost { namespace unordered_detail {
allocator_array_constructor& operator=(
allocator_array_constructor const&);
};
}}
}}}
#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
# undef BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES

View File

@ -1,183 +1,808 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2005-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)
#ifndef BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/unordered/detail/node.hpp>
#include <boost/unordered/detail/util.hpp>
namespace boost { namespace unordered_detail {
namespace boost { namespace unordered { namespace detail {
////////////////////////////////////////////////////////////////////////////
// Buckets
//
// Now the main data structure:
//
// buckets<A, Unique> functions<H, P>
// | |
// +---------------+--------------+
// |
// table<T>
//
// T is a class which contains typedefs for all the types we need.
template <class A, class G>
inline std::size_t hash_buckets<A, G>::max_bucket_count() const {
// -1 to account for the sentinel.
return prev_prime(this->bucket_alloc().max_size() - 1);
}
// buckets
//
// This is responsible for allocating and deallocating buckets and nodes.
//
// Notes:
// 1. For the sake exception safety the consturctors don't allocate
// anything.
// 2. It's the callers responsibility to allocate the buckets before calling
// any of the methods (other than getters and setters).
template <class A, class G>
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
hash_buckets<A, G>::get_bucket(std::size_t num) const
template <class A, bool Unique>
class buckets
{
return buckets_ + static_cast<std::ptrdiff_t>(num);
}
buckets(buckets const&);
buckets& operator=(buckets const&);
public:
// Types
template <class A, class G>
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
hash_buckets<A, G>::bucket_ptr_from_hash(std::size_t hashed) const
{
return get_bucket(hashed % bucket_count_);
}
typedef BOOST_DEDUCED_TYPENAME ::boost::detail::if_true<Unique>::
BOOST_NESTED_TEMPLATE then<
::boost::unordered::detail::ungrouped_node<A>,
::boost::unordered::detail::grouped_node<A>
>::type node;
typedef A value_allocator;
typedef ::boost::unordered::detail::bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_allocator
bucket_allocator;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<bucket_allocator>::pointer bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME rebind_wrap<value_allocator, node>::type
node_allocator;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<node_allocator>::pointer real_node_ptr;
// Members
bucket_ptr buckets_;
std::size_t bucket_count_;
std::size_t size_;
compressed_pair<bucket_allocator, node_allocator> allocators_;
// Data access
bucket_allocator const& bucket_alloc() const
{
return allocators_.first();
}
node_allocator const& node_alloc() const
{
return allocators_.second();
}
bucket_allocator& bucket_alloc()
{
return allocators_.first();
}
node_allocator& node_alloc()
{
return allocators_.second();
}
std::size_t max_bucket_count() const
{
// -1 to account for the start bucket.
return prev_prime(allocator_traits<bucket_allocator>::max_size(bucket_alloc()) - 1);
}
////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
buckets(node_allocator const& a, std::size_t bucket_count)
: buckets_(),
bucket_count_(bucket_count),
size_(),
allocators_(a,a)
{
}
buckets(buckets& b, move_tag m)
: buckets_(),
bucket_count_(b.bucket_count_),
size_(),
allocators_(b.allocators_, m)
{
swap(b);
}
template <typename T>
buckets(table<T>& x, move_tag m)
: buckets_(),
bucket_count_(x.bucket_count_),
allocators_(x.allocators_, m)
{
swap(x);
x.size_ = 0;
}
inline ~buckets()
{
if(this->buckets_) { this->delete_buckets(); }
}
void create_buckets()
{
// The array constructor will clean up in the event of an
// exception.
allocator_array_constructor<bucket_allocator>
constructor(bucket_alloc());
template <class A, class G>
std::size_t hash_buckets<A, G>::bucket_size(std::size_t index) const
{
if(!buckets_) return 0;
bucket_ptr ptr = get_bucket(index)->next_;
std::size_t count = 0;
while(ptr) {
++count;
// Creates an extra bucket to act as the start node.
constructor.construct(bucket(), this->bucket_count_ + 1);
// Only release the buckets once everything is successfully
// done.
this->buckets_ = constructor.release();
}
void swap(buckets& other, false_type = false_type())
{
BOOST_ASSERT(node_alloc() == other.node_alloc());
std::swap(buckets_, other.buckets_);
std::swap(bucket_count_, other.bucket_count_);
std::swap(size_, other.size_);
}
void swap(buckets& other, true_type)
{
allocators_.swap(other.allocators_);
std::swap(buckets_, other.buckets_);
std::swap(bucket_count_, other.bucket_count_);
std::swap(size_, other.size_);
}
void move_buckets_from(buckets& other)
{
BOOST_ASSERT(node_alloc() == other.node_alloc());
BOOST_ASSERT(!this->buckets_);
this->buckets_ = other.buckets_;
this->bucket_count_ = other.bucket_count_;
this->size_ = other.size_;
other.buckets_ = bucket_ptr();
other.bucket_count_ = 0;
other.size_ = 0;
}
std::size_t bucket_size(std::size_t index) const
{
if (!this->size_) return 0;
node_ptr ptr = this->buckets_[index].next_;
if (!ptr) return 0;
ptr = ptr->next_;
}
return count;
}
template <class A, class G>
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::node_ptr
hash_buckets<A, G>::bucket_begin(std::size_t num) const
{
return buckets_ ? get_bucket(num)->next_ : node_ptr();
}
////////////////////////////////////////////////////////////////////////////
// Delete
template <class A, class G>
inline void hash_buckets<A, G>::delete_node(node_ptr b)
{
node* raw_ptr = static_cast<node*>(&*b);
boost::unordered_detail::destroy(raw_ptr->value_ptr());
real_node_ptr n(node_alloc().address(*raw_ptr));
node_alloc().destroy(n);
node_alloc().deallocate(n, 1);
}
template <class A, class G>
inline void hash_buckets<A, G>::clear_bucket(bucket_ptr b)
{
node_ptr node_it = b->next_;
b->next_ = node_ptr();
while(node_it) {
node_ptr node_to_delete = node_it;
node_it = node_it->next_;
delete_node(node_to_delete);
}
}
template <class A, class G>
inline void hash_buckets<A, G>::delete_buckets()
{
bucket_ptr end = this->get_bucket(this->bucket_count_);
for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
clear_bucket(begin);
}
// Destroy the buckets (including the sentinel bucket).
++end;
for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
bucket_alloc().destroy(begin);
}
bucket_alloc().deallocate(this->buckets_, this->bucket_count_ + 1);
this->buckets_ = bucket_ptr();
}
template <class A, class G>
inline std::size_t hash_buckets<A, G>::delete_nodes(
node_ptr begin, node_ptr end)
{
std::size_t count = 0;
while(begin != end) {
node_ptr n = begin;
begin = begin->next_;
delete_node(n);
++count;
}
return count;
}
////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
template <class A, class G>
inline hash_buckets<A, G>::hash_buckets(
node_allocator const& a, std::size_t bucket_count)
: buckets_(),
bucket_count_(bucket_count),
allocators_(a,a)
{
}
template <class A, class G>
inline hash_buckets<A, G>::~hash_buckets()
{
if(this->buckets_) { this->delete_buckets(); }
}
template <class A, class G>
inline void hash_buckets<A, G>::create_buckets()
std::size_t count = 0;
while(BOOST_UNORDERED_BORLAND_BOOL(ptr) &&
node::get_hash(ptr) % this->bucket_count_ == index)
{
++count;
ptr = ptr->next_;
}
return count;
}
node_ptr bucket_begin(std::size_t bucket_index) const
{
if (!this->size_) return node_ptr();
bucket& b = this->buckets_[bucket_index];
if (!b.next_) return node_ptr();
return b.next_->next_;
}
// For the remaining functions, buckets_ must not be null.
bucket_ptr get_bucket(std::size_t bucket_index) const
{
return buckets_ + static_cast<std::ptrdiff_t>(bucket_index);
}
float load_factor() const
{
BOOST_ASSERT(this->bucket_count_ != 0);
return static_cast<float>(this->size_)
/ static_cast<float>(this->bucket_count_);
}
////////////////////////////////////////////////////////////////////////
// Delete
void delete_node(node_ptr n)
{
node* raw_ptr = static_cast<node*>(boost::addressof(*n));
real_node_ptr real_ptr(node_alloc().address(*raw_ptr));
::boost::unordered::detail::destroy(raw_ptr->value_ptr());
allocator_traits<node_allocator>::destroy(node_alloc(), raw_ptr);
allocator_traits<node_allocator>::deallocate(node_alloc(), real_ptr, 1);
--this->size_;
}
void delete_buckets()
{
bucket_ptr end = this->get_bucket(this->bucket_count_);
node_ptr n = (end)->next_;
while(BOOST_UNORDERED_BORLAND_BOOL(n))
{
node_ptr node_to_delete = n;
n = n->next_;
delete_node(node_to_delete);
}
++end;
for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
allocator_traits<bucket_allocator>::destroy(bucket_alloc(),
boost::addressof(*begin));
}
allocator_traits<bucket_allocator>::deallocate(bucket_alloc(), this->buckets_, this->bucket_count_ + 1);
this->buckets_ = bucket_ptr();
BOOST_ASSERT(this->size_ == 0);
}
std::size_t delete_nodes(node_ptr begin, node_ptr end)
{
std::size_t count = 0;
while(begin != end) {
node_ptr n = begin;
begin = begin->next_;
delete_node(n);
++count;
}
return count;
}
void clear()
{
if(!this->size_) return;
bucket_ptr end = this->get_bucket(this->bucket_count_);
node_ptr n = (end)->next_;
while(BOOST_UNORDERED_BORLAND_BOOL(n))
{
node_ptr node_to_delete = n;
n = n->next_;
this->delete_node(node_to_delete);
}
++end;
for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
begin->next_ = bucket_ptr();
}
this->size_ = 0;
}
node_ptr erase(node_ptr r)
{
BOOST_ASSERT(r);
node_ptr next = r->next_;
bucket_ptr bucket = this->get_bucket(
node::get_hash(r) % this->bucket_count_);
node_ptr prev = node::unlink_node(*bucket, r);
this->fix_buckets(bucket, prev, next);
this->delete_node(r);
return next;
}
node_ptr erase_range(node_ptr r1, node_ptr r2)
{
if (r1 == r2) return r2;
std::size_t bucket_index = node::get_hash(r1) % this->bucket_count_;
node_ptr prev = node::unlink_nodes(
this->buckets_[bucket_index], r1, r2);
this->fix_buckets_range(bucket_index, prev, r1, r2);
this->delete_nodes(r1, r2);
return r2;
}
// This is called after erasing a node or group of nodes to fix up
// the bucket pointers.
void fix_buckets(bucket_ptr bucket, node_ptr prev, node_ptr next)
{
if (!next)
{
if (bucket->next_ == prev) bucket->next_ = node_ptr();
}
else
{
bucket_ptr next_bucket = this->get_bucket(
node::get_hash(next) % this->bucket_count_);
if (next_bucket != bucket)
{
next_bucket->next_ = prev;
if (bucket->next_ == prev) bucket->next_ = node_ptr();
}
}
}
// This is called after erasing a range of nodes to fix any bucket
// pointers into that range.
void fix_buckets_range(
std::size_t bucket_index, node_ptr prev, node_ptr begin, node_ptr end)
{
node_ptr n = begin;
// If we're not at the start of the current bucket, then
// go to the start of the next bucket.
if (this->get_bucket(bucket_index)->next_ != prev)
{
for(;;) {
n = n->next_;
if (n == end) return;
std::size_t new_bucket_index =
node::get_hash(n) % this->bucket_count_;
if (bucket_index != new_bucket_index) {
bucket_index = new_bucket_index;
break;
}
}
}
// Iterate through the remaining nodes, clearing out the bucket
// pointers.
this->buckets_[bucket_index].next_ = bucket_ptr();
for(;;) {
n = n->next_;
if (n == end) break;
std::size_t new_bucket_index =
node::get_hash(n) % this->bucket_count_;
if (bucket_index != new_bucket_index) {
bucket_index = new_bucket_index;
this->buckets_[bucket_index].next_ = bucket_ptr();
}
};
// Finally fix the bucket containing the trailing node.
if (BOOST_UNORDERED_BORLAND_BOOL(n)) {
this->buckets_[node::get_hash(n) % this->bucket_count_].next_
= prev;
}
}
// Iterate through the nodes placing them in the correct buckets.
// pre: prev->next_ is not null.
node_ptr place_in_bucket(node_ptr prev, node_ptr end) {
bucket_ptr b = this->get_bucket(node::get_hash(prev->next_) % this->bucket_count_);
if (!b->next_) {
b->next_ = prev;
return end;
}
else {
node_ptr next = end->next_;
end->next_ = b->next_->next_;
b->next_->next_ = prev->next_;
prev->next_ = next;
return prev;
}
}
void copy_buckets_to(buckets&) const;
void move_buckets_to(buckets&) const;
void rehash_impl(std::size_t);
};
// Assigning and swapping the equality and hash function objects
// needs strong exception safety. To implement that normally we'd
// require one of them to be known to not throw and the other to
// guarantee strong exception safety. Unfortunately they both only
// have basic exception safety. So to acheive strong exception
// safety we have storage space for two copies, and assign the new
// copies to the unused space. Then switch to using that to use
// them. This is implemented in 'set_hash_functions' which
// atomically assigns the new function objects in a strongly
// exception safe manner.
template <class H, class P> class set_hash_functions;
template <class H, class P>
class functions
{
// The array constructor will clean up in the event of an
// exception.
allocator_array_constructor<bucket_allocator>
constructor(bucket_alloc());
friend class set_hash_functions<H, P>;
functions& operator=(functions const&);
// Creates an extra bucket to act as a sentinel.
constructor.construct(bucket(), this->bucket_count_ + 1);
typedef compressed_pair<H, P> function_pair;
typedef BOOST_DEDUCED_TYPENAME ::boost::aligned_storage<
sizeof(function_pair),
::boost::alignment_of<function_pair>::value>::type aligned_function;
// Set up the sentinel (node_ptr cast)
bucket_ptr sentinel = constructor.get() +
static_cast<std::ptrdiff_t>(this->bucket_count_);
sentinel->next_ = sentinel;
bool current_; // The currently active functions.
aligned_function funcs_[2];
// Only release the buckets once everything is successfully
// done.
this->buckets_ = constructor.release();
}
function_pair const& current() const {
return *static_cast<function_pair const*>(
static_cast<void const*>(&funcs_[current_]));
}
void construct(bool which, H const& hf, P const& eq)
{
new((void*) &funcs_[which]) function_pair(hf, eq);
}
void construct(bool which, function_pair const& f)
{
new((void*) &funcs_[which]) function_pair(f);
}
void destroy(bool which)
{
::boost::unordered::detail::destroy((function_pair*)(&funcs_[which]));
}
public:
functions(H const& hf, P const& eq)
: current_(false)
{
construct(current_, hf, eq);
}
functions(functions const& bf)
: current_(false)
{
construct(current_, bf.current());
}
~functions() {
destroy(current_);
}
H const& hash_function() const {
return current().first();
}
P const& key_eq() const {
return current().second();
}
};
template <class H, class P>
class set_hash_functions
{
set_hash_functions(set_hash_functions const&);
set_hash_functions& operator=(set_hash_functions const&);
functions<H,P>& functions_;
bool tmp_functions_;
public:
set_hash_functions(functions<H,P>& f, H const& h, P const& p)
: functions_(f),
tmp_functions_(!f.current_)
{
f.construct(tmp_functions_, h, p);
}
set_hash_functions(functions<H,P>& f, functions<H,P> const& other)
: functions_(f),
tmp_functions_(!f.current_)
{
f.construct(tmp_functions_, other.current());
}
~set_hash_functions()
{
functions_.destroy(tmp_functions_);
}
void commit()
{
functions_.current_ = tmp_functions_;
tmp_functions_ = !tmp_functions_;
}
};
////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
// Node Constructors
// no throw
template <class A, class G>
inline void hash_buckets<A, G>::move(hash_buckets& other)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class T, class... Args>
inline void construct_impl(T*, void* address, Args&&... args)
{
BOOST_ASSERT(node_alloc() == other.node_alloc());
if(this->buckets_) { this->delete_buckets(); }
this->buckets_ = other.buckets_;
this->bucket_count_ = other.bucket_count_;
other.buckets_ = bucket_ptr();
other.bucket_count_ = 0;
new(address) T(std::forward<Args>(args)...);
}
template <class A, class G>
inline void hash_buckets<A, G>::swap(hash_buckets<A, G>& other)
{
BOOST_ASSERT(node_alloc() == other.node_alloc());
std::swap(buckets_, other.buckets_);
std::swap(bucket_count_, other.bucket_count_);
#else
#define BOOST_UNORDERED_CONSTRUCT_IMPL(z, num_params, _) \
template < \
class T, \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
inline void construct_impl( \
T*, void* address, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
) \
{ \
new(address) T( \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
} \
\
template <class First, class Second, class Key, \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
inline void construct_impl( \
std::pair<First, Second>*, void* address, \
Key const& k, BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
new(address) std::pair<First, Second>(k, \
Second(BOOST_UNORDERED_CALL_PARAMS(z, num_params))); \
}
}}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_CONSTRUCT_IMPL, _)
#undef BOOST_UNORDERED_CONSTRUCT_IMPL
#endif
///////////////////////////////////////////////////////////////////
//
// Node construction
template <class Alloc, bool Unique>
class node_constructor
{
typedef ::boost::unordered::detail::buckets<Alloc, Unique> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef BOOST_DEDUCED_TYPENAME buckets::real_node_ptr real_node_ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME buckets::node_allocator node_allocator;
buckets& buckets_;
real_node_ptr node_;
bool node_constructed_;
bool value_constructed_;
public:
node_constructor(buckets& m) :
buckets_(m),
node_(),
node_constructed_(false),
value_constructed_(false)
{
}
~node_constructor();
void construct_preamble();
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class... Args>
void construct(Args&&... args)
{
construct_preamble();
construct_impl((value_type*) 0, node_->address(),
std::forward<Args>(args)...);
value_constructed_ = true;
}
#else
#define BOOST_UNORDERED_CONSTRUCT(z, num_params, _) \
template < \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
void construct( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
) \
{ \
construct_preamble(); \
construct_impl( \
(value_type*) 0, node_->address(), \
BOOST_UNORDERED_CALL_PARAMS(z, num_params) \
); \
value_constructed_ = true; \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_CONSTRUCT, _)
#undef BOOST_UNORDERED_CONSTRUCT
#endif
template <class K, class M>
void construct_pair(K const& k, M*)
{
construct_preamble();
new(node_->address()) value_type(k, M());
value_constructed_ = true;
}
value_type& value() const
{
BOOST_ASSERT(node_);
return node_->value();
}
// no throw
BOOST_DEDUCED_TYPENAME buckets::node_ptr release()
{
real_node_ptr p = node_;
node_ = real_node_ptr();
// node_ptr cast
return buckets_.bucket_alloc().address(*p);
}
private:
node_constructor(node_constructor const&);
node_constructor& operator=(node_constructor const&);
};
// node_constructor
template <class Alloc, bool Unique>
inline node_constructor<Alloc, Unique>::~node_constructor()
{
if (node_) {
if (value_constructed_) {
#if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613))
struct dummy { node<Alloc, Grouped> x; };
#endif
::boost::unordered::detail::destroy(node_->value_ptr());
}
if (node_constructed_)
allocator_traits<node_allocator>::destroy(buckets_.node_alloc(),
boost::addressof(*node_));
allocator_traits<node_allocator>::deallocate(buckets_.node_alloc(), node_, 1);
}
}
template <class Alloc, bool Unique>
inline void node_constructor<Alloc, Unique>::construct_preamble()
{
if(!node_) {
node_constructed_ = false;
value_constructed_ = false;
node_ = allocator_traits<node_allocator>::allocate(buckets_.node_alloc(), 1);
allocator_traits<node_allocator>::construct(buckets_.node_alloc(),
boost::addressof(*node_), node());
node_->init(buckets_.bucket_alloc().address(*node_));
node_constructed_ = true;
}
else {
BOOST_ASSERT(node_constructed_ && value_constructed_);
::boost::unordered::detail::destroy(node_->value_ptr());
value_constructed_ = false;
}
}
////////////////////////////////////////////////////////////////////////////
// copy_buckets_to
//
// basic exception safety. If an exception is thrown this will
// leave dst partially filled and the buckets unset.
template <class A, bool Unique>
void buckets<A, Unique>::copy_buckets_to(buckets& dst) const
{
BOOST_ASSERT(!dst.buckets_);
dst.create_buckets();
bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
{
node_constructor<A, Unique> a(dst);
node_ptr n = this->buckets_[this->bucket_count_].next_;
node_ptr prev = dst_start;
while(n) {
std::size_t hash = node::get_hash(n);
node_ptr group_end = node::next_group(n);
a.construct(node::get_value(n));
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(node::get_value(n));
end = a.release();
node::set_hash(end, hash);
node::add_after_node(end, first_node);
++dst.size_;
}
prev = dst.place_in_bucket(prev, end);
}
}
}
////////////////////////////////////////////////////////////////////////////
// move_buckets_to
//
// Basic exception safety. The source nodes are left in an unusable state
// if an exception throws.
template <class A, bool Unique>
void buckets<A, Unique>::move_buckets_to(buckets& dst) const
{
BOOST_ASSERT(!dst.buckets_);
dst.create_buckets();
bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
{
node_constructor<A, Unique> a(dst);
node_ptr n = this->buckets_[this->bucket_count_].next_;
node_ptr prev = dst_start;
while(n) {
std::size_t hash = node::get_hash(n);
node_ptr group_end = node::next_group(n);
a.construct(boost::move(node::get_value(n)));
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)));
end = a.release();
node::set_hash(end, hash);
node::add_after_node(end, first_node);
++dst.size_;
}
prev = dst.place_in_bucket(prev, end);
}
}
}
// strong otherwise exception safety
template <class A, bool Unique>
void buckets<A, Unique>::rehash_impl(std::size_t num_buckets)
{
BOOST_ASSERT(this->size_);
buckets dst(this->node_alloc(), num_buckets);
dst.create_buckets();
bucket_ptr src_start = this->get_bucket(this->bucket_count_);
bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
dst_start->next_ = src_start->next_;
src_start->next_ = bucket_ptr();
dst.size_ = this->size_;
this->size_ = 0;
node_ptr prev = dst_start;
while (BOOST_UNORDERED_BORLAND_BOOL(prev->next_))
prev = dst.place_in_bucket(prev, node::next_group2(prev));
// Swap the new nodes back into the container and setup the
// variables.
dst.swap(*this); // no throw
}
}}}
#endif

View File

@ -1,19 +1,18 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2005-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)
#ifndef BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
#include <boost/unordered/detail/table.hpp>
#include <boost/unordered/detail/extract_key.hpp>
namespace boost { namespace unordered_detail {
namespace boost { namespace unordered { namespace detail {
template <class T>
class hash_equivalent_table : public T::table
class equivalent_table : public T::table_base
{
public:
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
@ -21,54 +20,212 @@ namespace boost { namespace unordered_detail {
typedef BOOST_DEDUCED_TYPENAME T::value_allocator value_allocator;
typedef BOOST_DEDUCED_TYPENAME T::key_type key_type;
typedef BOOST_DEDUCED_TYPENAME T::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME T::table table;
typedef BOOST_DEDUCED_TYPENAME T::table_base table_base;
typedef BOOST_DEDUCED_TYPENAME T::node_constructor node_constructor;
typedef BOOST_DEDUCED_TYPENAME T::node_allocator node_allocator;
typedef BOOST_DEDUCED_TYPENAME T::node node;
typedef BOOST_DEDUCED_TYPENAME T::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME T::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME T::iterator_base iterator_base;
typedef BOOST_DEDUCED_TYPENAME T::extractor extractor;
// Constructors
hash_equivalent_table(std::size_t n,
equivalent_table(std::size_t n,
hasher const& hf, key_equal const& eq, value_allocator const& a)
: table(n, hf, eq, a) {}
hash_equivalent_table(hash_equivalent_table const& x)
: table(x, x.node_alloc()) {}
hash_equivalent_table(hash_equivalent_table const& x,
: table_base(n, hf, eq, a) {}
equivalent_table(equivalent_table const& x)
: table_base(x,
allocator_traits<node_allocator>::
select_on_container_copy_construction(x.node_alloc())) {}
equivalent_table(equivalent_table const& x,
value_allocator const& a)
: table(x, a) {}
hash_equivalent_table(hash_equivalent_table& x, move_tag m)
: table(x, m) {}
hash_equivalent_table(hash_equivalent_table& x,
: table_base(x, a) {}
equivalent_table(equivalent_table& x, move_tag m)
: table_base(x, m) {}
equivalent_table(equivalent_table& x,
value_allocator const& a, move_tag m)
: table(x, a, m) {}
~hash_equivalent_table() {}
: table_base(x, a, m) {}
~equivalent_table() {}
// Equality
bool equals(equivalent_table const& other) const
{
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
for(node_ptr n1 = this->buckets_[this->bucket_count_].next_; n1;)
{
node_ptr n2 = other.find_matching_node(n1);
if (!n2) return false;
node_ptr end1 = node::next_group(n1);
node_ptr end2 = node::next_group(n2);
if (!group_equals(n1, end1, n2, end2)) return false;
n1 = end1;
}
return true;
}
static bool group_equals(node_ptr n1, node_ptr end1,
node_ptr n2, node_ptr end2)
{
for(;;)
{
if (node::get_value(n1) != node::get_value(n2))
break;
n1 = n1->next_;
n2 = n2->next_;
if (n1 == end1) return n2 == end2;
if (n2 == end2) return false;
}
for(node_ptr n1a = n1, n2a = n2;;)
{
n1a = n1a->next_;
n2a = n2a->next_;
if (n1a == end1)
{
if (n2a == end2) break;
else return false;
}
if (n2a == end2) return false;
}
node_ptr start = n1;
for(;n1 != end2; n1 = n1->next_)
{
value_type const& v = node::get_value(n1);
if (find(start, n1, v)) continue;
std::size_t matches = count_equal(n2, end2, v);
if (!matches || matches != 1 + count_equal(n1->next_, end1, v))
return false;
}
return true;
}
static bool find(node_ptr n, node_ptr end, value_type const& v)
{
for(;n != end; n = n->next_)
if (node::get_value(n) == v)
return true;
return false;
}
static std::size_t count_equal(node_ptr n, node_ptr end, value_type const& v)
{
std::size_t count = 0;
for(;n != end; n = n->next_)
if (node::get_value(n) == v) ++count;
return count;
}
////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
inline node_ptr add_node(
node_constructor& a,
std::size_t bucket_index,
std::size_t hash,
node_ptr pos)
{
node_ptr n = a.release();
node::set_hash(n, hash);
if(BOOST_UNORDERED_BORLAND_BOOL(pos)) {
node::add_after_node(n, pos);
if (n->next_) {
std::size_t next_bucket =
node::get_hash(n->next_) % this->bucket_count_;
if (next_bucket != bucket_index) {
this->buckets_[next_bucket].next_ = n;
}
}
}
else {
bucket_ptr b = this->get_bucket(bucket_index);
if (!b->next_)
{
bucket_ptr start_node =
this->get_bucket(this->bucket_count_);
if (BOOST_UNORDERED_BORLAND_BOOL(start_node->next_)) {
this->buckets_[
node::get_hash(start_node->next_) %
this->bucket_count_].next_ = n;
}
b->next_ = start_node;
n->next_ = start_node->next_;
start_node->next_ = n;
}
else
{
n->next_ = b->next_->next_;
b->next_->next_ = n;
}
}
++this->size_;
return n;
}
////////////////////////////////////////////////////////////////////////
// Insert methods
iterator_base emplace_impl(node_constructor& a);
void emplace_impl_no_rehash(node_constructor& a);
node_ptr emplace_impl(node_constructor& a)
{
key_type const& k = this->get_key(a.value());
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr position = this->find_node(bucket_index, hash, k);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1)) {
bucket_index = hash % this->bucket_count_;
}
return add_node(a, bucket_index, hash, position);
}
// equals
void emplace_impl_no_rehash(node_constructor& a)
{
key_type const& k = this->get_key(a.value());
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
add_node(a, bucket_index, hash,
this->find_node(bucket_index, hash, k));
}
bool equals(hash_equivalent_table const&) const;
inline node_ptr add_node(node_constructor& a,
bucket_ptr bucket, node_ptr pos);
#if defined(BOOST_UNORDERED_STD_FORWARD)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class... Args>
iterator_base emplace(Args&&... args);
node_ptr emplace(Args&&... args)
{
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
return emplace_impl(a);
}
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
iterator_base emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, n));
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
node_ptr emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_impl(a); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
@ -76,229 +233,75 @@ namespace boost { namespace unordered_detail {
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
////////////////////////////////////////////////////////////////////////
// Insert range methods
// if hash function throws, or inserting > 1 element, basic exception
// safety. Strong otherwise
template <class I>
void insert_for_range(I i, I j, forward_traversal_tag);
void insert_for_range(I i, I j, forward_traversal_tag)
{
if(i == j) return;
std::size_t distance = ::boost::unordered::detail::distance(i, j);
if(distance == 1) {
emplace(*i);
}
else {
// Only require basic exception safety here
this->reserve_for_insert(this->size_ + distance);
node_constructor a(*this);
for (; i != j; ++i) {
a.construct(*i);
emplace_impl_no_rehash(a);
}
}
}
template <class I>
void insert_for_range(I i, I j, boost::incrementable_traversal_tag);
void insert_for_range(I i, I j, ::boost::incrementable_traversal_tag)
{
node_constructor a(*this);
for (; i != j; ++i) {
a.construct(*i);
emplace_impl(a);
}
}
// If hash function throws, or inserting > 1 element, basic exception
// safety. Strong otherwise
template <class I>
void insert_range(I i, I j);
void insert_range(I i, I j)
{
BOOST_DEDUCED_TYPENAME ::boost::iterator_traversal<I>::type
iterator_traversal_tag;
insert_for_range(i, j, iterator_traversal_tag);
}
};
template <class H, class P, class A>
struct multiset : public types<
BOOST_DEDUCED_TYPENAME A::value_type,
BOOST_DEDUCED_TYPENAME A::value_type,
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
set_extractor<BOOST_DEDUCED_TYPENAME A::value_type>,
grouped>
set_extractor<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
false>
{
typedef hash_equivalent_table<multiset<H, P, A> > impl;
typedef hash_table<multiset<H, P, A> > table;
typedef equivalent_table<multiset<H, P, A> > impl;
typedef table<multiset<H, P, A> > table_base;
};
template <class K, class H, class P, class A>
struct multimap : public types<
K, BOOST_DEDUCED_TYPENAME A::value_type,
K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
map_extractor<K, BOOST_DEDUCED_TYPENAME A::value_type>,
grouped>
map_extractor<K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
false>
{
typedef hash_equivalent_table<multimap<K, H, P, A> > impl;
typedef hash_table<multimap<K, H, P, A> > table;
typedef equivalent_table<multimap<K, H, P, A> > impl;
typedef table<multimap<K, H, P, A> > table_base;
};
////////////////////////////////////////////////////////////////////////////
// Equality
template <class T>
bool hash_equivalent_table<T>
::equals(hash_equivalent_table<T> const& other) const
{
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
bucket_ptr end = this->get_bucket(this->bucket_count_);
for(bucket_ptr i = this->cached_begin_bucket_; i != end; ++i)
{
node_ptr it1 = i->next_;
while(BOOST_UNORDERED_BORLAND_BOOL(it1))
{
node_ptr it2 = other.find_iterator(this->get_key_from_ptr(it1));
if(!BOOST_UNORDERED_BORLAND_BOOL(it2)) return false;
node_ptr end1 = node::next_group(it1);
node_ptr end2 = node::next_group(it2);
do {
if(!extractor::compare_mapped(
node::get_value(it1), node::get_value(it2)))
return false;
it1 = it1->next_;
it2 = it2->next_;
} while(it1 != end1 && it2 != end2);
if(it1 != end1 || it2 != end2) return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
template <class T>
inline BOOST_DEDUCED_TYPENAME hash_equivalent_table<T>::node_ptr
hash_equivalent_table<T>
::add_node(node_constructor& a, bucket_ptr bucket, node_ptr pos)
{
node_ptr n = a.release();
if(BOOST_UNORDERED_BORLAND_BOOL(pos)) {
node::add_after_node(n, pos);
}
else {
node::add_to_bucket(n, *bucket);
if(bucket < this->cached_begin_bucket_)
this->cached_begin_bucket_ = bucket;
}
++this->size_;
return n;
}
////////////////////////////////////////////////////////////////////////////
// Insert methods
template <class T>
inline BOOST_DEDUCED_TYPENAME
hash_equivalent_table<T>::iterator_base
hash_equivalent_table<T>::emplace_impl(node_constructor& a)
{
key_type const& k = this->get_key(a.value());
std::size_t hash_value = this->hash_function()(k);
if(!this->size_) {
return this->emplace_empty_impl_with_node(a, 1);
}
else {
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr position = this->find_iterator(bucket, k);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket = this->bucket_ptr_from_hash(hash_value);
return iterator_base(bucket, add_node(a, bucket, position));
}
}
template <class T>
inline void hash_equivalent_table<T>
::emplace_impl_no_rehash(node_constructor& a)
{
key_type const& k = this->get_key(a.value());
bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
add_node(a, bucket, this->find_iterator(bucket, k));
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
// Emplace (equivalent key containers)
// (I'm using an overloaded emplace for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template <class T>
template <class... Args>
BOOST_DEDUCED_TYPENAME hash_equivalent_table<T>::iterator_base
hash_equivalent_table<T>
::emplace(Args&&... args)
{
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
return emplace_impl(a);
}
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
BOOST_DEDUCED_TYPENAME hash_equivalent_table<T>::iterator_base \
hash_equivalent_table<T> \
::emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_impl(a); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
////////////////////////////////////////////////////////////////////////////
// Insert range methods
// if hash function throws, or inserting > 1 element, basic exception safety
// strong otherwise
template <class T>
template <class I>
inline void hash_equivalent_table<T>
::insert_for_range(I i, I j, forward_traversal_tag)
{
if(i == j) return;
std::size_t distance = unordered_detail::distance(i, j);
if(distance == 1) {
emplace(*i);
}
else {
node_constructor a(*this);
// Only require basic exception safety here
if(this->size_) {
this->reserve_for_insert(this->size_ + distance);
}
else {
a.construct(*i++);
this->emplace_empty_impl_with_node(a, distance);
}
for (; i != j; ++i) {
a.construct(*i);
emplace_impl_no_rehash(a);
}
}
}
// if hash function throws, or inserting > 1 element, basic exception safety
// strong otherwise
template <class T>
template <class I>
inline void hash_equivalent_table<T>
::insert_for_range(I i, I j, boost::incrementable_traversal_tag)
{
node_constructor a(*this);
for (; i != j; ++i) {
a.construct(*i);
emplace_impl(a);
}
}
// if hash function throws, or inserting > 1 element, basic exception safety
// strong otherwise
template <class T>
template <class I>
void hash_equivalent_table<T>::insert_range(I i, I j)
{
BOOST_DEDUCED_TYPENAME boost::iterator_traversal<I>::type
iterator_traversal_tag;
insert_for_range(i, j, iterator_traversal_tag);
}
}}
}}}
#endif

View File

@ -1,17 +1,16 @@
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2005-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)
#ifndef BOOST_UNORDERED_DETAIL_EXTRACT_KEY_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_EXTRACT_KEY_HPP_INCLUDED
#include <boost/config.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <boost/unordered/detail/table.hpp>
namespace boost {
namespace unordered_detail {
namespace unordered {
namespace detail {
// key extractors
//
@ -39,12 +38,19 @@ namespace unordered_detail {
return v;
}
#if BOOST_UNORDERED_USE_RV_REF
static key_type const& extract(BOOST_RV_REF(key_type) v)
{
return v;
}
#endif
static no_key extract()
{
return no_key();
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class... Args>
static no_key extract(Args const&...)
{
@ -75,7 +81,7 @@ namespace unordered_detail {
struct map_extractor
{
typedef ValueType value_type;
typedef BOOST_DEDUCED_TYPENAME boost::remove_const<Key>::type key_type;
typedef BOOST_DEDUCED_TYPENAME ::boost::remove_const<Key>::type key_type;
static key_type const& extract(value_type const& v)
{
@ -87,6 +93,13 @@ namespace unordered_detail {
return v;
}
// TODO: Why does this cause errors?
//
//static key_type const& extract(BOOST_RV_REF(key_type) v)
//{
// return v;
//}
template <class Second>
static key_type const& extract(std::pair<key_type, Second> const& v)
{
@ -100,7 +113,7 @@ namespace unordered_detail {
return v.first;
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class Arg1, class... Args>
static key_type const& extract(key_type const& k,
Arg1 const&, Args const&...)
@ -143,6 +156,6 @@ namespace unordered_detail {
return x.second == y.second;
}
};
}}
}}}
#endif

View File

@ -1,932 +1,50 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2008-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)
// This contains the basic data structure, apart from the actual values. There's
// no construction or deconstruction here. So this only depends on the pointer
// type.
#ifndef BOOST_UNORDERED_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_FWD_HPP_INCLUDED
#ifndef BOOST_UNORDERED_DETAIL_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_FWD_HPP_INCLUDED
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <boost/config.hpp>
#include <boost/iterator.hpp>
#include <boost/compressed_pair.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/unordered/detail/allocator_helpers.hpp>
#include <algorithm>
#include <memory>
#include <functional>
#include <boost/functional/hash_fwd.hpp>
// This header defines most of the classes used to implement the unordered
// containers. It doesn't include the insert methods as they require a lot
// of preprocessor metaprogramming - they are in unique.hpp and equivalent.hpp.
namespace boost
{
namespace unordered
{
template <class K,
class T,
class H = hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_map;
// Template parameters:
//
// H = Hash Function
// P = Predicate
// A = Value Allocator
// G = Bucket group policy, 'grouped' or 'ungrouped'
// E = Key Extractor
template <class K,
class T,
class H = hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_multimap;
#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES)
# if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
// STLport doesn't have std::forward.
# else
# define BOOST_UNORDERED_STD_FORWARD
# endif
#endif
template <class T,
class H = hash<T>,
class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_set;
#if !defined(BOOST_UNORDERED_EMPLACE_LIMIT)
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
#endif
#if !defined(BOOST_UNORDERED_STD_FORWARD)
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#define BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
BOOST_PP_ENUM_PARAMS_Z(z, num_params, class Arg)
#define BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
BOOST_PP_ENUM_BINARY_PARAMS_Z(z, num_params, Arg, const& arg)
#define BOOST_UNORDERED_CALL_PARAMS(z, num_params) \
BOOST_PP_ENUM_PARAMS_Z(z, num_params, arg)
#endif
namespace boost { namespace unordered_detail {
static const float minimum_max_load_factor = 1e-3f;
static const std::size_t default_bucket_count = 11;
struct move_tag {};
template <class T> class hash_unique_table;
template <class T> class hash_equivalent_table;
template <class Alloc, class Grouped>
class hash_node_constructor;
template <class ValueType>
struct set_extractor;
template <class Key, class ValueType>
struct map_extractor;
struct no_key;
// Explicitly call a destructor
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4100) // unreferenced formal parameter
#endif
template <class T>
inline void destroy(T* x) {
x->~T();
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
////////////////////////////////////////////////////////////////////////////
//
// This section implements buckets and nodes. Here's a rough
// inheritance diagram, to show how they pull together.
//
// For unordered_set/unordered_map:
//
// hash_bucket<A>
// |
// ungrouped_node_base<A> value_base<A::value_type>
// | |
// +--------------+-------------+
// |
// hash_node<A, ungrouped>
//
// For unordered_multiset/unordered_multimap:
//
// hash_bucket<A>
// |
// grouped_node_base<A> value_base<A::value_type>
// | |
// +--------------+-------------+
// |
// hash_node<A, grouped>
// hash_bucket
//
// hash_bucket is used for both the buckets and as a base class for
// nodes. By using 'bucket_ptr' for 'node_ptr', 'next_' can point
// to either a bucket or a node. This is used later to implement a
// sentinel at the end of the bucket array.
template <class A>
class hash_bucket
{
hash_bucket& operator=(hash_bucket const&);
public:
typedef hash_bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME
boost::unordered_detail::rebind_wrap<A, bucket>::type
bucket_allocator;
typedef BOOST_DEDUCED_TYPENAME bucket_allocator::pointer bucket_ptr;
typedef bucket_ptr node_ptr;
node_ptr next_;
hash_bucket() : next_() {}
};
// In containers with equivalent keys (unordered_multimap and
// unordered_multiset) equivalent nodes are grouped together, in
// containers with unique keys (unordered_map and unordered_set)
// individual nodes are treated as groups of one. The following two
// classes implement the data structure.
// This is used for containers with unique keys. There are no groups
// so it doesn't add any extra members, and just treats individual
// nodes as groups of one.
template <class A>
struct ungrouped_node_base : hash_bucket<A> {
typedef hash_bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
ungrouped_node_base() : bucket() {}
static inline node_ptr& next_group(node_ptr ptr);
static inline std::size_t group_count(node_ptr ptr);
static inline void add_to_bucket(node_ptr n, bucket& b);
static inline void add_after_node(node_ptr n, node_ptr position);
static void unlink_node(bucket& b, node_ptr n);
static void unlink_nodes(bucket& b, node_ptr begin, node_ptr end);
static void unlink_nodes(bucket& b, node_ptr end);
};
// This is used for containers with equivalent keys. It implements a
// circular list running in the opposite direction to the linked
// list through the nodes.
template <class A>
struct grouped_node_base : hash_bucket<A>
{
typedef hash_bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
node_ptr group_prev_;
grouped_node_base() : bucket(), group_prev_() {}
static inline node_ptr& next_group(node_ptr ptr);
static inline node_ptr first_in_group(node_ptr n);
static inline std::size_t group_count(node_ptr ptr);
static inline void add_to_bucket(node_ptr n, bucket& b);
static inline void add_after_node(node_ptr n, node_ptr position);
static void unlink_node(bucket& b, node_ptr n);
static void unlink_nodes(bucket& b, node_ptr begin, node_ptr end);
static void unlink_nodes(bucket& b, node_ptr end);
private:
static inline node_ptr split_group(node_ptr split);
static inline grouped_node_base& get(node_ptr ptr) {
return static_cast<grouped_node_base&>(*ptr);
}
};
// These two classes implement an easy way to pass around the node
// group policy classes without the messy template parameters.
// Whenever you see the template parameter 'G' it's one of these.
struct ungrouped
{
template <class A>
struct base {
typedef ungrouped_node_base<A> type;
};
};
struct grouped
{
template <class A>
struct base {
typedef grouped_node_base<A> type;
};
};
// The space used to store values in a node.
template <class ValueType>
struct value_base
{
typedef ValueType value_type;
BOOST_DEDUCED_TYPENAME boost::aligned_storage<
sizeof(value_type),
::boost::alignment_of<value_type>::value>::type data_;
void* address() {
return this;
}
value_type& value() {
return *(ValueType*) this;
}
value_type* value_ptr() {
return (ValueType*) this;
}
private:
value_base& operator=(value_base const&);
};
// Node
template <class A, class G>
class hash_node :
public G::BOOST_NESTED_TEMPLATE base<A>::type,
public value_base<BOOST_DEDUCED_TYPENAME A::value_type>
{
public:
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME hash_bucket<A>::node_ptr node_ptr;
static value_type& get_value(node_ptr p) {
return static_cast<hash_node&>(*p).value();
}
static value_type* get_value_ptr(node_ptr p) {
return static_cast<hash_node&>(*p).value_ptr();
}
private:
hash_node& operator=(hash_node const&);
};
////////////////////////////////////////////////////////////////////////////
//
// Iterator Base
//
// This is the iterator used internally, the external iterators are
// provided by lightweight wrappers (hash_iterator and
// hast_const_iterator) which provide the full iterator interface.
template <class A, class G>
class hash_iterator_base
{
public:
typedef A value_allocator;
typedef hash_bucket<A> bucket;
typedef hash_node<A, G> node;
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
bucket_ptr bucket_;
node_ptr node_;
hash_iterator_base() : bucket_(), node_() {}
explicit hash_iterator_base(bucket_ptr b)
: bucket_(b),
node_(b ? b->next_ : node_ptr()) {}
hash_iterator_base(bucket_ptr b, node_ptr n)
: bucket_(b),
node_(n) {}
bool operator==(hash_iterator_base const& x) const {
return node_ == x.node_; }
bool operator!=(hash_iterator_base const& x) const {
return node_ != x.node_; }
value_type& operator*() const {
return node::get_value(node_);
}
void increment_bucket(node_ptr n) {
while(!n) {
++bucket_;
n = bucket_->next_;
}
node_ = bucket_ == n ? node_ptr() : n;
}
void increment() {
increment_bucket(node_->next_);
}
};
////////////////////////////////////////////////////////////////////////////
//
// Now the main data structure:
//
// hash_buckets<A, G> hash_buffered_functions<H, P>
// | |
// +-------------+--------------+
// |
// hash_table<T>
//
// T is a class which contains typedefs for all the types we need.
// hash_buckets
//
// This is responsible for allocating and deallocating buckets and nodes.
//
// Notes:
// 1. For the sake exception safety the consturctors don't allocate
// anything.
// 2. It's the callers responsibility to allocate the buckets before calling
// any of the methods (other than getters and setters).
template <class A, class G>
class hash_buckets
{
hash_buckets(hash_buckets const&);
hash_buckets& operator=(hash_buckets const&);
public:
// Types
typedef A value_allocator;
typedef hash_bucket<A> bucket;
typedef hash_iterator_base<A, G> iterator_base;
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME iterator_base::node node;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_allocator
bucket_allocator;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME rebind_wrap<value_allocator, node>::type
node_allocator;
typedef BOOST_DEDUCED_TYPENAME node_allocator::pointer real_node_ptr;
// Members
bucket_ptr buckets_;
std::size_t bucket_count_;
boost::compressed_pair<bucket_allocator, node_allocator> allocators_;
// Data access
bucket_allocator const& bucket_alloc() const {
return allocators_.first(); }
node_allocator const& node_alloc() const {
return allocators_.second(); }
bucket_allocator& bucket_alloc() {
return allocators_.first(); }
node_allocator& node_alloc() {
return allocators_.second(); }
std::size_t max_bucket_count() const;
// Constructors
hash_buckets(node_allocator const& a, std::size_t n);
void create_buckets();
~hash_buckets();
// no throw
void swap(hash_buckets& other);
void move(hash_buckets& other);
// For the remaining functions, buckets_ must not be null.
bucket_ptr get_bucket(std::size_t n) const;
bucket_ptr bucket_ptr_from_hash(std::size_t hashed) const;
std::size_t bucket_size(std::size_t index) const;
node_ptr bucket_begin(std::size_t n) const;
// Alloc/Dealloc
void delete_node(node_ptr);
//
void delete_buckets();
void clear_bucket(bucket_ptr);
std::size_t delete_nodes(node_ptr begin, node_ptr end);
std::size_t delete_to_bucket_end(node_ptr begin);
};
// Assigning and swapping the equality and hash function objects
// needs strong exception safety. To implement that normally we'd
// require one of them to be known to not throw and the other to
// guarantee strong exception safety. Unfortunately they both only
// have basic exception safety. So to acheive strong exception
// safety we have storage space for two copies, and assign the new
// copies to the unused space. Then switch to using that to use
// them. This is implemented in 'set_hash_functions' which
// atomically assigns the new function objects in a strongly
// exception safe manner.
template <class H, class P> class set_hash_functions;
template <class H, class P>
class hash_buffered_functions
{
friend class set_hash_functions<H, P>;
hash_buffered_functions& operator=(hash_buffered_functions const&);
typedef boost::compressed_pair<H, P> function_pair;
typedef BOOST_DEDUCED_TYPENAME boost::aligned_storage<
sizeof(function_pair),
::boost::alignment_of<function_pair>::value>::type aligned_function;
bool current_; // The currently active functions.
aligned_function funcs_[2];
function_pair const& current() const {
return *static_cast<function_pair const*>(
static_cast<void const*>(&funcs_[current_]));
}
void construct(bool which, H const& hf, P const& eq)
{
new((void*) &funcs_[which]) function_pair(hf, eq);
}
void construct(bool which, function_pair const& f)
{
new((void*) &funcs_[which]) function_pair(f);
}
void destroy(bool which)
{
boost::unordered_detail::destroy((function_pair*)(&funcs_[which]));
}
public:
hash_buffered_functions(H const& hf, P const& eq)
: current_(false)
{
construct(current_, hf, eq);
}
hash_buffered_functions(hash_buffered_functions const& bf)
: current_(false)
{
construct(current_, bf.current());
}
~hash_buffered_functions() {
destroy(current_);
}
H const& hash_function() const {
return current().first();
}
P const& key_eq() const {
return current().second();
}
};
template <class H, class P>
class set_hash_functions
{
set_hash_functions(set_hash_functions const&);
set_hash_functions& operator=(set_hash_functions const&);
typedef hash_buffered_functions<H, P> buffered_functions;
buffered_functions& buffered_functions_;
bool tmp_functions_;
public:
set_hash_functions(buffered_functions& f, H const& h, P const& p)
: buffered_functions_(f),
tmp_functions_(!f.current_)
{
f.construct(tmp_functions_, h, p);
}
set_hash_functions(buffered_functions& f,
buffered_functions const& other)
: buffered_functions_(f),
tmp_functions_(!f.current_)
{
f.construct(tmp_functions_, other.current());
}
~set_hash_functions()
{
buffered_functions_.destroy(tmp_functions_);
}
void commit()
{
buffered_functions_.current_ = tmp_functions_;
tmp_functions_ = !tmp_functions_;
}
};
// This implements almost all of the required functionality, apart
// from some things that are specific to containers with unique and
// equivalent keys which is implemented in hash_unique_table and
// hash_equivalent_table. See unique.hpp and equivalent.hpp for
// their declaration and implementation.
template <class T>
class hash_table : public T::buckets, public T::buffered_functions
{
hash_table(hash_table const&);
public:
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
typedef BOOST_DEDUCED_TYPENAME T::key_equal key_equal;
typedef BOOST_DEDUCED_TYPENAME T::value_allocator value_allocator;
typedef BOOST_DEDUCED_TYPENAME T::key_type key_type;
typedef BOOST_DEDUCED_TYPENAME T::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME T::buffered_functions base;
typedef BOOST_DEDUCED_TYPENAME T::buckets buckets;
typedef BOOST_DEDUCED_TYPENAME T::extractor extractor;
typedef BOOST_DEDUCED_TYPENAME T::node_constructor node_constructor;
typedef BOOST_DEDUCED_TYPENAME T::node node;
typedef BOOST_DEDUCED_TYPENAME T::bucket bucket;
typedef BOOST_DEDUCED_TYPENAME T::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME T::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME T::iterator_base iterator_base;
typedef BOOST_DEDUCED_TYPENAME T::node_allocator node_allocator;
typedef BOOST_DEDUCED_TYPENAME T::iterator_pair iterator_pair;
// Members
std::size_t size_;
float mlf_;
// Cached data - invalid if !this->buckets_
bucket_ptr cached_begin_bucket_;
std::size_t max_load_;
// Helper methods
key_type const& get_key(value_type const& v) const {
return extractor::extract(v);
}
key_type const& get_key_from_ptr(node_ptr n) const {
return extractor::extract(node::get_value(n));
}
bool equal(key_type const& k, value_type const& v) const;
template <class Key, class Pred>
node_ptr find_iterator(bucket_ptr bucket, Key const& k,
Pred const&) const;
node_ptr find_iterator(bucket_ptr bucket, key_type const& k) const;
node_ptr find_iterator(key_type const& k) const;
node_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const;
// Load methods
std::size_t max_size() const;
std::size_t bucket_index(key_type const& k) const;
void max_load_factor(float z);
std::size_t min_buckets_for_size(std::size_t n) const;
std::size_t calculate_max_load();
// Constructors
hash_table(std::size_t n, hasher const& hf, key_equal const& eq,
node_allocator const& a);
hash_table(hash_table const& x, node_allocator const& a);
hash_table(hash_table& x, move_tag m);
hash_table(hash_table& x, node_allocator const& a, move_tag m);
~hash_table() {}
hash_table& operator=(hash_table const&);
// Iterators
iterator_base begin() const {
return this->size_ ?
iterator_base(this->cached_begin_bucket_) :
iterator_base();
}
iterator_base end() const {
return iterator_base();
}
// Swap & Move
void swap(hash_table& x);
void fast_swap(hash_table& other);
void slow_swap(hash_table& other);
void partial_swap(hash_table& other);
void move(hash_table& x);
// Reserve and rehash
void create_for_insert(std::size_t n);
bool reserve_for_insert(std::size_t n);
void rehash(std::size_t n);
void rehash_impl(std::size_t n);
// Move/copy buckets
void move_buckets_to(buckets& dst);
void copy_buckets_to(buckets& dst) const;
// Misc. key methods
std::size_t count(key_type const& k) const;
iterator_base find(key_type const& k) const;
template <class Key, class Hash, class Pred>
iterator_base find(Key const& k, Hash const& h, Pred const& eq) const;
value_type& at(key_type const& k) const;
iterator_pair equal_range(key_type const& k) const;
// Erase
//
// no throw
void clear();
std::size_t erase_key(key_type const& k);
iterator_base erase_return_iterator(iterator_base r);
void erase(iterator_base r);
std::size_t erase_group(node_ptr* it, bucket_ptr bucket);
iterator_base erase_range(iterator_base r1, iterator_base r2);
// recompute_begin_bucket
void init_buckets();
// After an erase cached_begin_bucket_ might be left pointing to
// an empty bucket, so this is called to update it
//
// no throw
void recompute_begin_bucket(bucket_ptr b);
// This is called when a range has been erased
//
// no throw
void recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2);
// no throw
float load_factor() const;
iterator_base emplace_empty_impl_with_node(
node_constructor&, std::size_t);
};
///////////////////////////////////////////////////////////////////
//
// Iterators
// iterator_access is used to access the internal iterator without
// making it publicly available.
class iterator_access
{
public:
template <class Iterator>
static BOOST_DEDUCED_TYPENAME Iterator::base const&
get(Iterator const& it)
{
return it.base_;
}
};
template <class A, class G> class hash_iterator;
template <class A, class G> class hash_const_iterator;
template <class A, class G> class hash_local_iterator;
template <class A, class G> class hash_const_local_iterator;
// Local Iterators
//
// all no throw
template <class A, class G>
class hash_local_iterator
: public boost::iterator <
std::forward_iterator_tag,
BOOST_DEDUCED_TYPENAME A::value_type,
std::ptrdiff_t,
BOOST_DEDUCED_TYPENAME A::pointer,
BOOST_DEDUCED_TYPENAME A::reference>
{
public:
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
private:
typedef hash_buckets<A, G> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef hash_const_local_iterator<A, G> const_local_iterator;
friend class hash_const_local_iterator<A, G>;
node_ptr ptr_;
public:
hash_local_iterator() : ptr_() {}
explicit hash_local_iterator(node_ptr x) : ptr_(x) {}
BOOST_DEDUCED_TYPENAME A::reference operator*() const {
return node::get_value(ptr_);
}
value_type* operator->() const {
return node::get_value_ptr(ptr_);
}
hash_local_iterator& operator++() {
ptr_ = ptr_->next_; return *this;
}
hash_local_iterator operator++(int) {
hash_local_iterator tmp(ptr_); ptr_ = ptr_->next_; return tmp; }
bool operator==(hash_local_iterator x) const {
return ptr_ == x.ptr_;
}
bool operator==(const_local_iterator x) const {
return ptr_ == x.ptr_;
}
bool operator!=(hash_local_iterator x) const {
return ptr_ != x.ptr_;
}
bool operator!=(const_local_iterator x) const {
return ptr_ != x.ptr_;
}
};
template <class A, class G>
class hash_const_local_iterator
: public boost::iterator <
std::forward_iterator_tag,
BOOST_DEDUCED_TYPENAME A::value_type,
std::ptrdiff_t,
BOOST_DEDUCED_TYPENAME A::const_pointer,
BOOST_DEDUCED_TYPENAME A::const_reference >
{
public:
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
private:
typedef hash_buckets<A, G> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef hash_local_iterator<A, G> local_iterator;
friend class hash_local_iterator<A, G>;
ptr ptr_;
public:
hash_const_local_iterator() : ptr_() {}
explicit hash_const_local_iterator(ptr x) : ptr_(x) {}
hash_const_local_iterator(local_iterator x) : ptr_(x.ptr_) {}
BOOST_DEDUCED_TYPENAME A::const_reference
operator*() const {
return node::get_value(ptr_);
}
value_type const* operator->() const {
return node::get_value_ptr(ptr_);
}
hash_const_local_iterator& operator++() {
ptr_ = ptr_->next_; return *this;
}
hash_const_local_iterator operator++(int) {
hash_const_local_iterator tmp(ptr_); ptr_ = ptr_->next_; return tmp;
}
bool operator==(local_iterator x) const {
return ptr_ == x.ptr_;
}
bool operator==(hash_const_local_iterator x) const {
return ptr_ == x.ptr_;
}
bool operator!=(local_iterator x) const {
return ptr_ != x.ptr_;
}
bool operator!=(hash_const_local_iterator x) const {
return ptr_ != x.ptr_;
}
};
// Iterators
//
// all no throw
template <class A, class G>
class hash_iterator
: public boost::iterator <
std::forward_iterator_tag,
BOOST_DEDUCED_TYPENAME A::value_type,
std::ptrdiff_t,
BOOST_DEDUCED_TYPENAME A::pointer,
BOOST_DEDUCED_TYPENAME A::reference >
{
public:
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
private:
typedef hash_buckets<A, G> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base;
typedef hash_const_iterator<A, G> const_iterator;
friend class hash_const_iterator<A, G>;
base base_;
public:
hash_iterator() : base_() {}
explicit hash_iterator(base const& x) : base_(x) {}
BOOST_DEDUCED_TYPENAME A::reference operator*() const {
return *base_;
}
value_type* operator->() const {
return &*base_;
}
hash_iterator& operator++() {
base_.increment(); return *this;
}
hash_iterator operator++(int) {
hash_iterator tmp(base_); base_.increment(); return tmp;
}
bool operator==(hash_iterator const& x) const {
return base_ == x.base_;
}
bool operator==(const_iterator const& x) const {
return base_ == x.base_;
}
bool operator!=(hash_iterator const& x) const {
return base_ != x.base_;
}
bool operator!=(const_iterator const& x) const {
return base_ != x.base_;
}
};
template <class A, class G>
class hash_const_iterator
: public boost::iterator <
std::forward_iterator_tag,
BOOST_DEDUCED_TYPENAME A::value_type,
std::ptrdiff_t,
BOOST_DEDUCED_TYPENAME A::const_pointer,
BOOST_DEDUCED_TYPENAME A::const_reference >
{
public:
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
private:
typedef hash_buckets<A, G> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base;
typedef hash_iterator<A, G> iterator;
friend class hash_iterator<A, G>;
friend class iterator_access;
base base_;
public:
hash_const_iterator() : base_() {}
explicit hash_const_iterator(base const& x) : base_(x) {}
hash_const_iterator(iterator const& x) : base_(x.base_) {}
BOOST_DEDUCED_TYPENAME A::const_reference operator*() const {
return *base_;
}
value_type const* operator->() const {
return &*base_;
}
hash_const_iterator& operator++() {
base_.increment(); return *this;
}
hash_const_iterator operator++(int) {
hash_const_iterator tmp(base_); base_.increment(); return tmp;
}
bool operator==(iterator const& x) const {
return base_ == x.base_;
}
bool operator==(hash_const_iterator const& x) const {
return base_ == x.base_;
}
bool operator!=(iterator const& x) const {
return base_ != x.base_;
}
bool operator!=(hash_const_iterator const& x) const {
return base_ != x.base_;
}
};
////////////////////////////////////////////////////////////////////////////
//
// types
//
// This is used to convieniently pass around a container's typedefs
// without having 7 template parameters.
template <class K, class V, class H, class P, class A, class E, class G>
struct types
{
public:
typedef K key_type;
typedef V value_type;
typedef H hasher;
typedef P key_equal;
typedef A value_allocator;
typedef E extractor;
typedef G group_type;
typedef hash_node_constructor<value_allocator, group_type>
node_constructor;
typedef hash_buckets<value_allocator, group_type> buckets;
typedef hash_buffered_functions<hasher, key_equal> buffered_functions;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef BOOST_DEDUCED_TYPENAME buckets::bucket bucket;
typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base iterator_base;
typedef BOOST_DEDUCED_TYPENAME buckets::node_allocator node_allocator;
typedef std::pair<iterator_base, iterator_base> iterator_pair;
};
}}
template <class T,
class H = hash<T>,
class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_multiset;
}
}
#endif

View File

@ -1,243 +0,0 @@
/*
Copyright 2005-2007 Adobe Systems Incorporated
Use, modification and distribution are subject to 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).
*/
/*************************************************************************************************/
#ifndef BOOST_UNORDERED_DETAIL_MOVE_HEADER
#define BOOST_UNORDERED_DETAIL_MOVE_HEADER
#include <boost/config.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/detail/workaround.hpp>
/*************************************************************************************************/
#if defined(BOOST_NO_SFINAE)
# define BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN
#elif defined(__GNUC__) && \
(__GNUC__ < 3 || __GNUC__ == 3 && __GNUC_MINOR__ <= 3)
# define BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN
#elif BOOST_WORKAROUND(BOOST_INTEL, < 900) || \
BOOST_WORKAROUND(__EDG_VERSION__, < 304) || \
BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0593))
# define BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN
#endif
/*************************************************************************************************/
namespace boost {
namespace unordered_detail {
/*************************************************************************************************/
namespace move_detail {
/*************************************************************************************************/
#if !defined(BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN)
/*************************************************************************************************/
template <typename T>
struct class_has_move_assign {
class type {
typedef T& (T::*E)(T t);
typedef char (&no_type)[1];
typedef char (&yes_type)[2];
template <E e> struct sfinae { typedef yes_type type; };
template <class U>
static typename sfinae<&U::operator=>::type test(int);
template <class U>
static no_type test(...);
public:
enum {value = sizeof(test<T>(1)) == sizeof(yes_type)};
};
};
/*************************************************************************************************/
template<typename T>
struct has_move_assign : boost::mpl::and_<boost::is_class<T>, class_has_move_assign<T> > {};
/*************************************************************************************************/
class test_can_convert_anything { };
/*************************************************************************************************/
#endif // BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN
/*************************************************************************************************/
/*
REVISIT (sparent@adobe.com): This is a work around for Boost 1.34.1 and VC++ 2008 where
boost::is_convertible<T, T> fails to compile.
*/
template <typename T, typename U>
struct is_convertible : boost::mpl::or_<
boost::is_same<T, U>,
boost::is_convertible<T, U>
> { };
/*************************************************************************************************/
} //namespace move_detail
/*************************************************************************************************/
/*!
\ingroup move_related
\brief move_from is used for move_ctors.
*/
template <typename T>
struct move_from
{
explicit move_from(T& x) : source(x) { }
T& source;
private:
move_from& operator=(move_from const&);
};
/*************************************************************************************************/
#if !defined(BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN)
/*************************************************************************************************/
/*!
\ingroup move_related
\brief The is_movable trait can be used to identify movable types.
*/
template <typename T>
struct is_movable : boost::mpl::and_<
boost::is_convertible<move_from<T>, T>,
move_detail::has_move_assign<T>,
boost::mpl::not_<boost::is_convertible<move_detail::test_can_convert_anything, T> >
> { };
/*************************************************************************************************/
#else // BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN
// On compilers which don't have adequate SFINAE support, treat most types as unmovable,
// unless the trait is specialized.
template <typename T>
struct is_movable : boost::mpl::false_ { };
#endif
/*************************************************************************************************/
#if !defined(BOOST_NO_SFINAE)
/*************************************************************************************************/
/*!
\ingroup move_related
\brief copy_sink and move_sink are used to select between overloaded operations according to
whether type T is movable and convertible to type U.
\sa move
*/
template <typename T,
typename U = T,
typename R = void*>
struct copy_sink : boost::enable_if<
boost::mpl::and_<
boost::unordered_detail::move_detail::is_convertible<T, U>,
boost::mpl::not_<is_movable<T> >
>,
R
>
{ };
/*************************************************************************************************/
/*!
\ingroup move_related
\brief move_sink and copy_sink are used to select between overloaded operations according to
whether type T is movable and convertible to type U.
\sa move
*/
template <typename T,
typename U = T,
typename R = void*>
struct move_sink : boost::enable_if<
boost::mpl::and_<
boost::unordered_detail::move_detail::is_convertible<T, U>,
is_movable<T>
>,
R
>
{ };
/*************************************************************************************************/
/*!
\ingroup move_related
\brief This version of move is selected when T is_movable . It in turn calls the move
constructor. This call, with the help of the return value optimization, will cause x to be moved
instead of copied to its destination. See adobe/test/move/main.cpp for examples.
*/
template <typename T>
T move(T& x, typename move_sink<T>::type = 0) { return T(move_from<T>(x)); }
/*************************************************************************************************/
/*!
\ingroup move_related
\brief This version of move is selected when T is not movable . The net result will be that
x gets copied.
*/
template <typename T>
T& move(T& x, typename copy_sink<T>::type = 0) { return x; }
/*************************************************************************************************/
#else // BOOST_NO_SFINAE
// On compilers without SFINAE, define copy_sink to always use the copy function.
template <typename T,
typename U = T,
typename R = void*>
struct copy_sink
{
typedef R type;
};
// Always copy the element unless this is overloaded.
template <typename T>
T& move(T& x) {
return x;
}
#endif // BOOST_NO_SFINAE
} // namespace unordered_detail
} // namespace boost
/*************************************************************************************************/
#endif
/*************************************************************************************************/

View File

@ -1,6 +1,6 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2005-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)
@ -11,10 +11,7 @@
#ifndef BOOST_UNORDERED_DETAIL_NODE_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_NODE_HPP_INCLUDED
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <boost/unordered/detail/util.hpp>
#if BOOST_WORKAROUND(__BORLANDC__, <= 0X0582)
#define BOOST_UNORDERED_BORLAND_BOOL(x) (bool)(x)
@ -22,205 +19,348 @@
#define BOOST_UNORDERED_BORLAND_BOOL(x) x
#endif
namespace boost { namespace unordered_detail {
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;
////////////////////////////////////////////////////////////////////////////
// ungrouped node implementation
//
// This section implements buckets and nodes. Here's a rough
// inheritance diagram, to show how they pull together.
//
// For unordered_set/unordered_map:
//
// bucket<A> value_base<allocator_traits<A>::value_type>
// | |
// +--------------+-------------+
// |
// ungrouped_node<A>
//
// For unordered_multiset/unordered_multimap:
//
// bucket<A> value_base<allocator_traits<A>::value_type>
// | |
// +--------------+-------------+
// |
// grouped_node<A>
// bucket
//
// bucket is used for both the buckets and as a base class for
// nodes. By using 'bucket_ptr' for 'node_ptr', 'next_' can point
// to either a bucket or a node. This is used later to implement a
// sentinel at the end of the bucket array.
template <class A>
inline BOOST_DEDUCED_TYPENAME ungrouped_node_base<A>::node_ptr&
ungrouped_node_base<A>::next_group(node_ptr ptr)
class bucket
{
return ptr->next_;
}
template <class A>
inline std::size_t ungrouped_node_base<A>::group_count(node_ptr)
{
return 1;
}
template <class A>
inline void ungrouped_node_base<A>::add_to_bucket(node_ptr n, bucket& b)
{
n->next_ = b.next_;
b.next_ = n;
}
template <class A>
inline void ungrouped_node_base<A>::add_after_node(node_ptr n,
node_ptr position)
{
n->next_ = position->next_;
position->next_ = position;
}
bucket& operator=(bucket const&);
public:
typedef BOOST_DEDUCED_TYPENAME
::boost::unordered::detail::rebind_wrap<A, bucket>::type
bucket_allocator;
typedef BOOST_DEDUCED_TYPENAME
allocator_traits<bucket_allocator>::pointer bucket_ptr;
typedef bucket_ptr node_ptr;
template <class A>
inline void ungrouped_node_base<A>::unlink_nodes(bucket& b,
node_ptr begin, node_ptr end)
node_ptr next_;
bucket() : next_() {}
};
// The space used to store values in a node.
template <class ValueType>
struct value_base
{
node_ptr* pos = &b.next_;
while(*pos != begin) pos = &(*pos)->next_;
*pos = end;
}
typedef ValueType value_type;
BOOST_DEDUCED_TYPENAME ::boost::aligned_storage<
sizeof(value_type),
::boost::alignment_of<value_type>::value>::type data_;
template <class A>
inline void ungrouped_node_base<A>::unlink_nodes(bucket& b, node_ptr end)
{
b.next_ = end;
}
template <class A>
inline void ungrouped_node_base<A>::unlink_node(bucket& b, node_ptr n)
{
unlink_nodes(b, n, n->next_);
}
////////////////////////////////////////////////////////////////////////////
// grouped node implementation
// If ptr is the first element in a group, return pointer to next group.
// Otherwise returns a pointer to ptr.
template <class A>
inline BOOST_DEDUCED_TYPENAME grouped_node_base<A>::node_ptr&
grouped_node_base<A>::next_group(node_ptr ptr)
{
return get(ptr).group_prev_->next_;
}
template <class A>
inline BOOST_DEDUCED_TYPENAME grouped_node_base<A>::node_ptr
grouped_node_base<A>::first_in_group(node_ptr ptr)
{
while(next_group(ptr) == ptr)
ptr = get(ptr).group_prev_;
return ptr;
}
template <class A>
inline std::size_t grouped_node_base<A>::group_count(node_ptr ptr)
{
node_ptr start = ptr;
std::size_t size = 0;
do {
++size;
ptr = get(ptr).group_prev_;
} while(ptr != start);
return size;
}
template <class A>
inline void grouped_node_base<A>::add_to_bucket(node_ptr n, bucket& b)
{
n->next_ = b.next_;
get(n).group_prev_ = n;
b.next_ = n;
}
template <class A>
inline void grouped_node_base<A>::add_after_node(node_ptr n, node_ptr pos)
{
n->next_ = next_group(pos);
get(n).group_prev_ = get(pos).group_prev_;
next_group(pos) = n;
get(pos).group_prev_ = n;
}
// Break a ciruclar list into two, with split as the beginning
// of the second group (if split is at the beginning then don't
// split).
template <class A>
inline BOOST_DEDUCED_TYPENAME grouped_node_base<A>::node_ptr
grouped_node_base<A>::split_group(node_ptr split)
{
node_ptr first = first_in_group(split);
if(first == split) return split;
node_ptr last = get(first).group_prev_;
get(first).group_prev_ = get(split).group_prev_;
get(split).group_prev_ = last;
return first;
}
template <class A>
void grouped_node_base<A>::unlink_node(bucket& b, node_ptr n)
{
node_ptr next = n->next_;
node_ptr* pos = &next_group(n);
if(*pos != n) {
// The node is at the beginning of a group.
// Find the previous node pointer:
pos = &b.next_;
while(*pos != n) pos = &next_group(*pos);
// Remove from group
if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
get(next).group_prev_ == n)
{
get(next).group_prev_ = get(n).group_prev_;
}
void* address() {
return this;
}
else if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
get(next).group_prev_ == n)
value_type& value() {
return *(ValueType*) this;
}
value_type* value_ptr() {
return (ValueType*) this;
}
private:
value_base& operator=(value_base const&);
};
// In containers with equivalent keys (unordered_multimap and
// unordered_multiset) equivalent nodes are grouped together, in
// containers with unique keys (unordered_map and unordered_set)
// individual nodes are treated as groups of one. The following two
// classes implement the data structure.
// This is used for containers with unique keys. There are no groups
// so it doesn't add any extra members, and just treats individual
// nodes as groups of one.
template <class A>
struct ungrouped_node
: ::boost::unordered::detail::bucket<A>,
value_base<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>
{
typedef ::boost::unordered::detail::bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type value_type;
std::size_t hash_;
ungrouped_node() : bucket() {}
void init(node_ptr) {}
static node_ptr next_group(node_ptr ptr)
{
// The deleted node is not at the end of the group, so
// change the link from the next node.
get(next).group_prev_ = get(n).group_prev_;
return ptr->next_;
}
else {
// The deleted node is at the end of the group, so the
// first node in the group is pointing to it.
// Find that to change its pointer.
node_ptr x = get(n).group_prev_;
while(get(x).group_prev_ != n) {
x = get(x).group_prev_;
}
get(x).group_prev_ = get(n).group_prev_;
static node_ptr next_group2(node_ptr ptr)
{
return ptr->next_;
}
*pos = next;
}
static std::size_t group_count(node_ptr n)
{
return !n ? 0 : 1;
}
static void add_after_node(node_ptr n, node_ptr position)
{
n->next_ = position->next_;
position->next_ = position;
}
static node_ptr unlink_node(bucket& b, node_ptr n)
{
return unlink_nodes(b, n, n->next_);
}
static node_ptr unlink_nodes(bucket& b, node_ptr begin, node_ptr end)
{
node_ptr prev = b.next_;
while(prev->next_ != begin) prev = prev->next_;
prev->next_ = end;
return prev;
}
static std::size_t get_hash(node_ptr p)
{
return static_cast<ungrouped_node&>(*p).hash_;
}
static void set_hash(node_ptr p, std::size_t hash)
{
static_cast<ungrouped_node&>(*p).hash_ = hash;
}
static value_type& get_value(node_ptr p)
{
return static_cast<ungrouped_node&>(*p).value();
}
static value_type* get_value_ptr(node_ptr p)
{
return static_cast<ungrouped_node&>(*p).value_ptr();
}
};
// This is used for containers with equivalent keys. It implements a
// circular list running in the opposite direction to the linked
// list through the nodes.
template <class A>
void grouped_node_base<A>::unlink_nodes(bucket& b,
node_ptr begin, node_ptr end)
struct grouped_node
: ::boost::unordered::detail::bucket<A>,
value_base<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>
{
node_ptr* pos = &next_group(begin);
typedef ::boost::unordered::detail::bucket<A> bucket;
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type value_type;
if(*pos != begin) {
// The node is at the beginning of a group.
std::size_t hash_;
node_ptr group_prev_;
// Find the previous node pointer:
pos = &b.next_;
while(*pos != begin) pos = &next_group(*pos);
// Remove from group
if(BOOST_UNORDERED_BORLAND_BOOL(end)) split_group(end);
grouped_node() : bucket(), group_prev_() {}
void init(node_ptr n)
{
group_prev_ = n;
}
else {
node_ptr group1 = split_group(begin);
if(BOOST_UNORDERED_BORLAND_BOOL(end)) {
node_ptr group2 = split_group(end);
if(begin == group2) {
node_ptr end1 = get(group1).group_prev_;
node_ptr end2 = get(group2).group_prev_;
get(group1).group_prev_ = end2;
get(group2).group_prev_ = end1;
static node_ptr next_group(node_ptr ptr)
{
return get(ptr).group_prev_->next_;
}
static node_ptr next_group2(node_ptr ptr)
{
return get(ptr->next_).group_prev_;
}
static std::size_t group_count(node_ptr ptr)
{
if (!ptr) return 0;
node_ptr start = ptr;
std::size_t size = 0;
do {
++size;
ptr = get(ptr).group_prev_;
} while(ptr != start);
return size;
}
static void add_after_node(node_ptr n, node_ptr pos)
{
n->next_ = get(pos).group_prev_->next_;
get(n).group_prev_ = get(pos).group_prev_;
get(pos).group_prev_->next_ = n;
get(pos).group_prev_ = n;
}
static node_ptr unlink_node(bucket& b, node_ptr n)
{
node_ptr next = n->next_;
node_ptr prev = get(n).group_prev_;
if(prev->next_ != n) {
// The node is at the beginning of a group.
// Find the previous node pointer:
prev = b.next_;
while(prev->next_ != n) {
prev = next_group2(prev);
}
// Remove from group
if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
get(next).group_prev_ == n)
{
get(next).group_prev_ = get(n).group_prev_;
}
}
else if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
get(next).group_prev_ == n)
{
// The deleted node is not at the end of the group, so
// change the link from the next node.
get(next).group_prev_ = get(n).group_prev_;
}
else {
// The deleted node is at the end of the group, so the
// first node in the group is pointing to it.
// Find that to change its pointer.
node_ptr x = get(n).group_prev_;
while(get(x).group_prev_ != n) {
x = get(x).group_prev_;
}
get(x).group_prev_ = get(n).group_prev_;
}
prev->next_ = next;
return prev;
}
*pos = end;
}
template <class A>
void grouped_node_base<A>::unlink_nodes(bucket& b, node_ptr end)
static node_ptr unlink_nodes(bucket& b, node_ptr begin, node_ptr end)
{
node_ptr prev = get(begin).group_prev_;
if(prev->next_ != begin) {
// The node is at the beginning of a group.
// Find the previous node pointer:
prev = b.next_;
while(prev->next_ != begin) prev = next_group2(prev);
if(BOOST_UNORDERED_BORLAND_BOOL(end)) split_group(end);
}
else {
node_ptr group1 = split_group(begin);
if(BOOST_UNORDERED_BORLAND_BOOL(end)) {
node_ptr group2 = split_group(end);
if(begin == group2) {
node_ptr end1 = get(group1).group_prev_;
node_ptr end2 = get(group2).group_prev_;
get(group1).group_prev_ = end2;
get(group2).group_prev_ = end1;
}
}
}
prev->next_ = end;
return prev;
}
// Break a ciruclar list into two, with split as the beginning
// of the second group (if split is at the beginning then don't
// split).
static node_ptr split_group(node_ptr split)
{
// Find first node in group.
node_ptr first = split;
while(next_group(first) == first)
first = get(first).group_prev_;
if(first == split) return split;
node_ptr last = get(first).group_prev_;
get(first).group_prev_ = get(split).group_prev_;
get(split).group_prev_ = last;
return first;
}
static std::size_t get_hash(node_ptr p) {
return static_cast<grouped_node&>(*p).hash_;
}
static void set_hash(node_ptr p, std::size_t hash) {
static_cast<grouped_node&>(*p).hash_ = hash;
}
static value_type& get_value(node_ptr p) {
return static_cast<grouped_node&>(*p).value();
}
static value_type* get_value_ptr(node_ptr p) {
return static_cast<grouped_node&>(*p).value_ptr();
}
static grouped_node& get(node_ptr ptr) {
return static_cast<grouped_node&>(*ptr);
}
};
// These two classes implement an easy way to pass around the node
// group policy classes without the messy template parameters.
// Whenever you see the template parameter 'G' it's one of these.
struct ungrouped
{
split_group(end);
b.next_ = end;
}
}}
template <class A>
struct node {
typedef ungrouped_node<A> type;
};
};
struct grouped
{
template <class A>
struct node {
typedef grouped_node<A> type;
};
};
}}}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,18 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2010 Daniel James
// Copyright (C) 2005-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)
#ifndef BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED
#include <boost/unordered/detail/table.hpp>
#include <boost/unordered/detail/extract_key.hpp>
namespace boost { namespace unordered_detail {
namespace boost { namespace unordered { namespace detail {
template <class T>
class hash_unique_table : public T::table
class unique_table : public T::table_base
{
public:
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
@ -21,493 +20,398 @@ namespace boost { namespace unordered_detail {
typedef BOOST_DEDUCED_TYPENAME T::value_allocator value_allocator;
typedef BOOST_DEDUCED_TYPENAME T::key_type key_type;
typedef BOOST_DEDUCED_TYPENAME T::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME T::table table;
typedef BOOST_DEDUCED_TYPENAME T::table_base table_base;
typedef BOOST_DEDUCED_TYPENAME T::node_constructor node_constructor;
typedef BOOST_DEDUCED_TYPENAME T::node_allocator node_allocator;
typedef BOOST_DEDUCED_TYPENAME T::node node;
typedef BOOST_DEDUCED_TYPENAME T::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME T::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME T::iterator_base iterator_base;
typedef BOOST_DEDUCED_TYPENAME T::extractor extractor;
typedef std::pair<iterator_base, bool> emplace_return;
typedef std::pair<node_ptr, bool> emplace_return;
// Constructors
hash_unique_table(std::size_t n, hasher const& hf, key_equal const& eq,
unique_table(std::size_t n, hasher const& hf, key_equal const& eq,
value_allocator const& a)
: table(n, hf, eq, a) {}
hash_unique_table(hash_unique_table const& x)
: table(x, x.node_alloc()) {}
hash_unique_table(hash_unique_table const& x, value_allocator const& a)
: table(x, a) {}
hash_unique_table(hash_unique_table& x, move_tag m)
: table(x, m) {}
hash_unique_table(hash_unique_table& x, value_allocator const& a,
: table_base(n, hf, eq, a) {}
unique_table(unique_table const& x)
: table_base(x,
allocator_traits<node_allocator>::
select_on_container_copy_construction(x.node_alloc())) {}
unique_table(unique_table const& x, value_allocator const& a)
: table_base(x, a) {}
unique_table(unique_table& x, move_tag m)
: table_base(x, m) {}
unique_table(unique_table& x, value_allocator const& a,
move_tag m)
: table(x, a, m) {}
~hash_unique_table() {}
// Insert methods
emplace_return emplace_impl_with_node(node_constructor& a);
value_type& operator[](key_type const& k);
: table_base(x, a, m) {}
~unique_table() {}
// equals
bool equals(hash_unique_table const&) const;
node_ptr add_node(node_constructor& a, bucket_ptr bucket);
#if defined(BOOST_UNORDERED_STD_FORWARD)
template<class... Args>
emplace_return emplace(Args&&... args);
template<class... Args>
emplace_return emplace_impl(key_type const& k, Args&&... args);
template<class... Args>
emplace_return emplace_impl(no_key, Args&&... args);
template<class... Args>
emplace_return emplace_empty_impl(Args&&... args);
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_empty_impl( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n));
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
// if hash function throws, or inserting > 1 element, basic exception
// safety strong otherwise
template <class InputIt>
void insert_range(InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl(key_type const&, InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl2(node_constructor&, key_type const&, InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl(no_key, InputIt i, InputIt j);
};
template <class H, class P, class A>
struct set : public types<
BOOST_DEDUCED_TYPENAME A::value_type,
BOOST_DEDUCED_TYPENAME A::value_type,
H, P, A,
set_extractor<BOOST_DEDUCED_TYPENAME A::value_type>,
ungrouped>
{
typedef hash_unique_table<set<H, P, A> > impl;
typedef hash_table<set<H, P, A> > table;
};
template <class K, class H, class P, class A>
struct map : public types<
K, BOOST_DEDUCED_TYPENAME A::value_type,
H, P, A,
map_extractor<K, BOOST_DEDUCED_TYPENAME A::value_type>,
ungrouped>
{
typedef hash_unique_table<map<K, H, P, A> > impl;
typedef hash_table<map<K, H, P, A> > table;
};
////////////////////////////////////////////////////////////////////////////
// Equality
template <class T>
bool hash_unique_table<T>
::equals(hash_unique_table<T> const& other) const
{
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
bucket_ptr end = this->get_bucket(this->bucket_count_);
for(bucket_ptr i = this->cached_begin_bucket_; i != end; ++i)
bool equals(unique_table const& other) const
{
node_ptr it1 = i->next_;
while(BOOST_UNORDERED_BORLAND_BOOL(it1))
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
for(node_ptr n1 = this->get_bucket(this->bucket_count_)->next_;
n1; n1 = n1->next_)
{
node_ptr it2 = other.find_iterator(this->get_key_from_ptr(it1));
if(!BOOST_UNORDERED_BORLAND_BOOL(it2)) return false;
if(!extractor::compare_mapped(
node::get_value(it1), node::get_value(it2)))
node_ptr n2 = other.find_matching_node(n1);
if(!n2 || node::get_value(n1) != node::get_value(n2))
return false;
it1 = it1->next_;
}
return true;
}
return true;
}
////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
////////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
template <class T>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::node_ptr
hash_unique_table<T>::add_node(node_constructor& a,
bucket_ptr bucket)
{
node_ptr n = a.release();
node::add_to_bucket(n, *bucket);
++this->size_;
if(bucket < this->cached_begin_bucket_)
this->cached_begin_bucket_ = bucket;
return n;
}
////////////////////////////////////////////////////////////////////////////
// Insert methods
// if hash function throws, basic exception safety
// strong otherwise
template <class T>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::value_type&
hash_unique_table<T>::operator[](key_type const& k)
{
typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
if(!this->buckets_) {
node_constructor a(*this);
a.construct_pair(k, (mapped_type*) 0);
return *this->emplace_empty_impl_with_node(a, 1);
node_ptr add_node(
node_constructor& a,
std::size_t bucket_index,
std::size_t hash)
{
bucket_ptr b = this->get_bucket(bucket_index);
node_ptr n = a.release();
node::set_hash(n, hash);
if (!b->next_)
{
bucket_ptr start_node = this->get_bucket(this->bucket_count_);
if (start_node->next_) {
this->buckets_[
node::get_hash(start_node->next_) % this->bucket_count_
].next_ = n;
}
b->next_ = start_node;
n->next_ = start_node->next_;
start_node->next_ = n;
}
else
{
n->next_ = b->next_->next_;
b->next_->next_ = n;
}
++this->size_;
return n;
}
node_ptr pos = this->find_iterator(bucket, k);
////////////////////////////////////////////////////////////////////////////
// Insert methods
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
return node::get_value(pos);
}
else {
// Side effects only in this block.
// if hash function throws, basic exception safety
// strong otherwise
value_type& operator[](key_type const& k)
{
typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
return node::get_value(pos);
}
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct_pair(k, (mapped_type*) 0);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket = this->bucket_ptr_from_hash(hash_value);
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return node::get_value(add_node(a, bucket));
return node::get_value(add_node(a, bucket_index, hash));
}
}
template <class T>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_impl_with_node(node_constructor& a)
{
// No side effects in this initial code
key_type const& k = this->get_key(a.value());
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(iterator_base(bucket, pos), false);
} else {
emplace_return emplace_impl_with_node(node_constructor& a)
{
// No side effects in this initial code
key_type const& k = this->get_key(a.value());
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket = this->bucket_ptr_from_hash(hash_value);
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return emplace_return(
iterator_base(bucket, add_node(a, bucket)),
true);
return emplace_return(add_node(a, bucket_index, hash), true);
}
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
emplace_return insert(value_type const& v)
{
key_type const& k = extractor::extract(v);
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// Isn't in table, add to bucket.
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct(v);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return emplace_return(add_node(a, bucket_index, hash), true);
}
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_impl(key_type const& k,
Args&&... args)
{
// No side effects in this initial code
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(iterator_base(bucket, pos), false);
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
} else {
template<class... Args>
emplace_return emplace(Args&&... args)
{
return emplace_impl(
extractor::extract(std::forward<Args>(args)...),
std::forward<Args>(args)...);
}
template<class... Args>
emplace_return emplace_impl(key_type const& k, Args&&... args)
{
// No side effects in this initial code
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// 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(*this);
a.construct(std::forward<Args>(args)...);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket = this->bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
return emplace_return(
iterator_base(bucket, add_node(a, bucket)),
true);
}
}
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_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(*this);
a.construct(std::forward<Args>(args)...);
return emplace_impl_with_node(a);
}
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_empty_impl(Args&&... args)
{
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
return emplace_return(this->emplace_empty_impl_with_node(a, 1), true);
}
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>::emplace_impl( \
key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
std::size_t hash_value = this->hash_function()(k); \
bucket_ptr bucket \
= this->bucket_ptr_from_hash(hash_value); \
node_ptr pos = this->find_iterator(bucket, k); \
\
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { \
return emplace_return(iterator_base(bucket, pos), false); \
} else { \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
\
if(this->reserve_for_insert(this->size_ + 1)) \
bucket = this->bucket_ptr_from_hash(hash_value); \
\
return emplace_return(iterator_base(bucket, \
add_node(a, bucket)), true); \
} \
} \
\
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>:: \
emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_impl_with_node(a); \
} \
\
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>:: \
emplace_empty_impl( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_return(this->emplace_empty_impl_with_node(a, 1), true); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
#if defined(BOOST_UNORDERED_STD_FORWARD)
// Emplace (unique keys)
// (I'm using an overloaded emplace for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template <class T>
template<class... Args>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace(Args&&... args)
{
return this->size_ ?
emplace_impl(
extractor::extract(std::forward<Args>(args)...),
std::forward<Args>(args)...) :
emplace_empty_impl(std::forward<Args>(args)...);
}
#else
template <class T>
template <class Arg0>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace(Arg0 const& arg0)
{
return this->size_ ?
emplace_impl(extractor::extract(arg0), arg0) :
emplace_empty_impl(arg0);
}
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return \
hash_unique_table<T>::emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
return this->size_ ? \
emplace_impl(extractor::extract(arg0, arg1), \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)) : \
emplace_empty_impl( \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
}
BOOST_PP_REPEAT_FROM_TO(2, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
bucket_index = hash % this->bucket_count_;
////////////////////////////////////////////////////////////////////////////
// Insert range methods
// Nothing after this point can throw.
return emplace_return(add_node(a, bucket_index, hash), true);
}
template <class T>
template <class InputIt>
inline void hash_unique_table<T>::insert_range_impl2(
node_constructor& a, key_type const& k, InputIt i, InputIt j)
{
// No side effects in this initial code
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, k);
template<class... Args>
emplace_return emplace_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(*this);
a.construct(std::forward<Args>(args)...);
return emplace_impl_with_node(a);
}
#else
if (!BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Doesn't already exist, add to bucket.
// Side effects only in this block.
template <class Arg0>
emplace_return emplace(BOOST_FWD_REF(Arg0) arg0)
{
return emplace_impl(
extractor::extract(boost::forward<Arg0>(arg0)),
boost::forward<Arg0>(arg0));
}
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
a.construct(*i);
#define BOOST_UNORDERED_INSERT1_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
return emplace_impl(extractor::extract(arg0, arg1), \
BOOST_UNORDERED_CALL_PARAMS(z, n)); \
}
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->size_ + 1 >= this->max_load_) {
this->reserve_for_insert(this->size_ + insert_size(i, j));
bucket = this->bucket_ptr_from_hash(hash_value);
#define BOOST_UNORDERED_INSERT2_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
std::size_t hash = this->hash_function()(k); \
std::size_t bucket_index = hash % this->bucket_count_; \
node_ptr pos = this->find_node(bucket_index, hash, k); \
\
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { \
return emplace_return(pos, false); \
} else { \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, n)); \
\
if(this->reserve_for_insert(this->size_ + 1)) \
bucket_index = hash % this->bucket_count_; \
\
return emplace_return( \
add_node(a, bucket_index, hash), \
true); \
} \
} \
\
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, n)); \
return emplace_impl_with_node(a); \
}
BOOST_PP_REPEAT_FROM_TO(2, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT1_IMPL, _)
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT2_IMPL, _)
#undef BOOST_UNORDERED_INSERT1_IMPL
#undef BOOST_UNORDERED_INSERT2_IMPL
#endif
////////////////////////////////////////////////////////////////////////
// Insert range methods
//
// if hash function throws, or inserting > 1 element, basic exception
// safety strong otherwise
template <class InputIt>
void insert_range(InputIt i, InputIt j)
{
if(i != j)
return insert_range_impl(extractor::extract(*i), i, j);
}
template <class InputIt>
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;
}
// Nothing after this point can throw.
add_node(a, bucket);
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
// different second_type.
//
// TODO: Might be worth storing the value_type instead of the key
// here. Could be more efficient if '*i' is expensive. Could be
// less efficient if copying the full value_type is expensive.
insert_range_impl2(a, extractor::extract(*i), i, j);
} while(++i != j);
}
}
template <class T>
template <class InputIt>
inline void hash_unique_table<T>::insert_range_impl(
key_type const&, InputIt i, InputIt j)
{
node_constructor a(*this);
if(!this->size_) {
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->emplace_empty_impl_with_node(a, 1);
++i;
if(i == j) return;
this->reserve_for_insert(this->size_ + insert_size(i, j));
add_node(a, hash % this->bucket_count_, hash);
}
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
// different second_type.
//
// TODO: Might be worth storing the value_type instead of the key
// here. Could be more efficient if '*i' is expensive. Could be
// less efficient if copying the full value_type is expensive.
insert_range_impl2(a, extractor::extract(*i), i, j);
} while(++i != j);
}
template <class T>
template <class InputIt>
inline void hash_unique_table<T>::insert_range_impl(
no_key, InputIt i, InputIt j)
{
node_constructor a(*this);
if(!this->size_) {
a.construct(*i);
this->emplace_empty_impl_with_node(a, 1);
++i;
if(i == j) return;
}
do {
template <class InputIt>
void insert_range_impl2(node_constructor& a, key_type const& k,
InputIt i, InputIt j)
{
// No side effects in this initial code
a.construct(*i);
emplace_impl_with_node(a);
} while(++i != j);
}
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (!BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// 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).
a.construct(*i);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->size_ + 1 >= this->max_load_) {
this->reserve_for_insert(this->size_ + insert_size(i, j));
bucket_index = hash % this->bucket_count_;
}
// Nothing after this point can throw.
add_node(a, bucket_index, hash);
}
}
// if hash function throws, or inserting > 1 element, basic exception safety
// strong otherwise
template <class T>
template <class InputIt>
void hash_unique_table<T>::insert_range(InputIt i, InputIt j)
template <class InputIt>
void insert_range_impl(no_key, InputIt i, InputIt j)
{
node_constructor a(*this);
do {
// No side effects in this initial code
a.construct(*i);
emplace_impl_with_node(a);
} while(++i != j);
}
};
template <class H, class P, class A>
struct set : public types<
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
set_extractor<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
true>
{
typedef ::boost::unordered::detail::unique_table<set<H, P, A> > impl;
typedef ::boost::unordered::detail::table<set<H, P, A> > table_base;
};
template <class K, class H, class P, class A>
struct map : public types<
K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
map_extractor<K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
true>
{
if(i != j)
return insert_range_impl(extractor::extract(*i), i, j);
}
}}
typedef ::boost::unordered::detail::unique_table<map<K, H, P, A> > impl;
typedef ::boost::unordered::detail::table<map<K, H, P, A> > table_base;
};
}}}
#endif

View File

@ -1,22 +1,127 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2009 Daniel James
// Copyright (C) 2005-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)
#ifndef BOOST_UNORDERED_DETAIL_UTIL_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_UTIL_HPP_INCLUDED
#include <cstddef>
#include <utility>
#include <algorithm>
#include <cstddef>
#include <stdexcept>
#include <utility>
#include <boost/limits.hpp>
#include <boost/config.hpp>
#include <boost/config/no_tr1/cmath.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/detail/select_type.hpp>
#include <boost/assert.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/is_empty.hpp>
#include <boost/throw_exception.hpp>
#include <boost/unordered/detail/allocator_helpers.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/move/move.hpp>
#include <boost/swap.hpp>
namespace boost { namespace unordered_detail {
// Template parameters:
//
// H = Hash Function
// P = Predicate
// A = Value Allocator
// G = Bucket group policy, 'grouped' or 'ungrouped'
// E = Key Extractor
#if !defined(BOOST_NO_RVALUE_REFERENCES) && \
!defined(BOOST_NO_VARIADIC_TEMPLATES)
# if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
# elif defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER)
# elif defined(_LIBCPP_VERSION)
# define BOOST_UNORDERED_STD_FORWARD_MOVE
# elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
# if defined(__GLIBCXX__) && __GLIBCXX__ >= 20090804
# define BOOST_UNORDERED_STD_FORWARD_MOVE
# endif
# elif defined(__STL_CONFIG_H)
# elif defined(__MSL_CPP__)
# elif defined(__IBMCPP__)
# elif defined(MSIPL_COMPILE_H)
# elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER)
// Visual C++. A version check would be a good idea.
# define BOOST_UNORDERED_STD_FORWARD_MOVE
# endif
#endif
#if !defined(BOOST_UNORDERED_EMPLACE_LIMIT)
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
#endif
#if defined(__SUNPRO_CC)
#define BOOST_UNORDERED_USE_RV_REF 0
#else
#define BOOST_UNORDERED_USE_RV_REF 1
#endif
#if !defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#define BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
BOOST_PP_ENUM_PARAMS_Z(z, num_params, class Arg)
#define BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
BOOST_PP_ENUM_##z(num_params, BOOST_UNORDERED_FUNCTION_PARAMS2, _)
#define BOOST_UNORDERED_FUNCTION_PARAMS2(z, i, _) \
BOOST_FWD_REF(Arg##i) arg##i
#define BOOST_UNORDERED_CALL_PARAMS(z, num_params) \
BOOST_PP_ENUM_##z(num_params, BOOST_UNORDERED_CALL_PARAMS2, _)
#define BOOST_UNORDERED_CALL_PARAMS2(z, i, _) \
boost::forward<Arg##i>(arg##i)
#endif
namespace boost { namespace unordered { namespace detail {
static const float minimum_max_load_factor = 1e-3f;
static const std::size_t default_bucket_count = 11;
struct move_tag {};
struct empty_emplace {};
template <class T> class unique_table;
template <class T> class equivalent_table;
template <class Alloc, bool Unique> class node_constructor;
template <class ValueType>
struct set_extractor;
template <class Key, class ValueType>
struct map_extractor;
struct no_key;
// Explicitly call a destructor
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4100) // unreferenced formal parameter
#endif
template <class T>
inline void destroy(T* x) {
x->~T();
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
////////////////////////////////////////////////////////////////////////////
// convert double to std::size_t
@ -95,12 +200,19 @@ namespace boost { namespace unordered_detail {
////////////////////////////////////////////////////////////////////////////
// pair_cast - because some libraries don't have the full pair constructors.
#if 0
template <class Dst1, class Dst2, class Src1, class Src2>
inline std::pair<Dst1, Dst2> pair_cast(std::pair<Src1, Src2> const& x)
{
return std::pair<Dst1, Dst2>(Dst1(x.first), Dst2(x.second));
}
#define BOOST_UNORDERED_PAIR_CAST(First, Last, Argument) \
::boost::unordered::detail::pair_cast<First, Last>(Argument)
#else
#define BOOST_UNORDERED_PAIR_CAST(First, Last, Argument) \
Argument
#endif
////////////////////////////////////////////////////////////////////////////
// insert_size/initial_size
@ -116,13 +228,13 @@ namespace boost { namespace unordered_detail {
#endif
template <class I>
inline std::size_t insert_size(I i, I j, boost::forward_traversal_tag)
inline std::size_t insert_size(I i, I j, ::boost::forward_traversal_tag)
{
return std::distance(i, j);
}
template <class I>
inline std::size_t insert_size(I, I, boost::incrementable_traversal_tag)
inline std::size_t insert_size(I, I, ::boost::incrementable_traversal_tag)
{
return 1;
}
@ -130,202 +242,114 @@ namespace boost { namespace unordered_detail {
template <class I>
inline std::size_t insert_size(I i, I j)
{
BOOST_DEDUCED_TYPENAME boost::iterator_traversal<I>::type
BOOST_DEDUCED_TYPENAME ::boost::iterator_traversal<I>::type
iterator_traversal_tag;
return insert_size(i, j, iterator_traversal_tag);
}
template <class I>
inline std::size_t initial_size(I i, I j,
std::size_t num_buckets = boost::unordered_detail::default_bucket_count)
std::size_t num_buckets = ::boost::unordered::detail::default_bucket_count)
{
return (std::max)(static_cast<std::size_t>(insert_size(i, j)) + 1,
num_buckets);
}
////////////////////////////////////////////////////////////////////////////
// Node Constructors
// compressed_pair
#if defined(BOOST_UNORDERED_STD_FORWARD)
template <class T, class... Args>
inline void construct_impl(T*, void* address, Args&&... args)
template <typename T, int Index>
struct compressed_base : private T
{
new(address) T(std::forward<Args>(args)...);
}
compressed_base(T const& x) : T(x) {}
compressed_base(T& x, move_tag) : T(boost::move(x)) {}
#if defined(BOOST_UNORDERED_CPP0X_PAIR)
template <class First, class Second, class Key, class Arg0, class... Args>
inline void construct_impl(std::pair<First, Second>*, void* address,
Key&& k, Arg0&& arg0, Args&&... args)
)
T& get() { return *this; }
T const& get() const { return *this; }
};
template <typename T, int Index>
struct uncompressed_base
{
new(address) std::pair<First, Second>(k,
Second(arg0, std::forward<Args>(args)...);
}
#endif
uncompressed_base(T const& x) : value_(x) {}
uncompressed_base(T& x, move_tag) : value_(boost::move(x)) {}
#else
#define BOOST_UNORDERED_CONSTRUCT_IMPL(z, num_params, _) \
template < \
class T, \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
inline void construct_impl( \
T*, void* address, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
) \
{ \
new(address) T( \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
} \
\
template <class First, class Second, class Key, \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
inline void construct_impl( \
std::pair<First, Second>*, void* address, \
Key const& k, BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
new(address) std::pair<First, Second>(k, \
Second(BOOST_UNORDERED_CALL_PARAMS(z, num_params))); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_CONSTRUCT_IMPL, _)
#undef BOOST_UNORDERED_CONSTRUCT_IMPL
#endif
// hash_node_constructor
//
// Used to construct nodes in an exception safe manner.
template <class Alloc, class Grouped>
class hash_node_constructor
T& get() { return value_; }
T const& get() const { return value_; }
private:
T value_;
};
template <typename T, int Index>
struct generate_base
: boost::detail::if_true<
boost::is_empty<T>::value
>:: BOOST_NESTED_TEMPLATE then<
compressed_base<T, Index>,
uncompressed_base<T, Index>
>
{};
template <typename T1, typename T2>
struct compressed_pair
: private generate_base<T1, 1>::type,
private generate_base<T2, 2>::type
{
typedef hash_buckets<Alloc, Grouped> buckets;
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
typedef BOOST_DEDUCED_TYPENAME buckets::real_node_ptr real_node_ptr;
typedef BOOST_DEDUCED_TYPENAME buckets::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME generate_base<T1, 1>::type base1;
typedef BOOST_DEDUCED_TYPENAME generate_base<T2, 2>::type base2;
buckets& buckets_;
real_node_ptr node_;
bool node_constructed_;
bool value_constructed_;
public:
hash_node_constructor(buckets& m) :
buckets_(m),
node_(),
node_constructed_(false),
value_constructed_(false)
{
typedef T1 first_type;
typedef T2 second_type;
first_type& first() {
return static_cast<base1*>(this)->get();
}
~hash_node_constructor();
void construct_preamble();
#if defined(BOOST_UNORDERED_STD_FORWARD)
template <class... Args>
void construct(Args&&... args)
{
construct_preamble();
construct_impl((value_type*) 0, node_->address(),
std::forward<Args>(args)...);
value_constructed_ = true;
}
#else
#define BOOST_UNORDERED_CONSTRUCT(z, num_params, _) \
template < \
BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
> \
void construct( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
) \
{ \
construct_preamble(); \
construct_impl( \
(value_type*) 0, node_->address(), \
BOOST_UNORDERED_CALL_PARAMS(z, num_params) \
); \
value_constructed_ = true; \
first_type const& first() const {
return static_cast<base1 const*>(this)->get();
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_CONSTRUCT, _)
#undef BOOST_UNORDERED_CONSTRUCT
#endif
template <class K, class M>
void construct_pair(K const& k, M*)
{
construct_preamble();
new(node_->address()) value_type(k, M());
value_constructed_ = true;
second_type& second() {
return static_cast<base2*>(this)->get();
}
value_type& value() const
{
BOOST_ASSERT(node_);
return node_->value();
second_type const& second() const {
return static_cast<base2 const*>(this)->get();
}
// no throw
BOOST_DEDUCED_TYPENAME buckets::node_ptr release()
template <typename First, typename Second>
compressed_pair(First const& x1, Second const& x2)
: base1(x1), base2(x2) {}
compressed_pair(compressed_pair const& x)
: base1(x.first()), base2(x.second()) {}
compressed_pair(compressed_pair& x, move_tag m)
: base1(x.first(), m), base2(x.second(), m) {}
void assign(compressed_pair const& x)
{
real_node_ptr p = node_;
node_ = real_node_ptr();
// node_ptr cast
return buckets_.bucket_alloc().address(*p);
first() = x.first();
second() = x.second();
}
void move_assign(compressed_pair& x)
{
first() = boost::move(x.first());
second() = boost::move(x.second());
}
void swap(compressed_pair& x)
{
boost::swap(first(), x.first());
boost::swap(second(), x.second());
}
private:
hash_node_constructor(hash_node_constructor const&);
hash_node_constructor& operator=(hash_node_constructor const&);
// Prevent assignment just to make use of assign or
// move_assign explicit.
compressed_pair& operator=(compressed_pair const&);
};
// hash_node_constructor
template <class Alloc, class Grouped>
inline hash_node_constructor<Alloc, Grouped>::~hash_node_constructor()
{
if (node_) {
if (value_constructed_) {
#if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613))
struct dummy { hash_node<Alloc, Grouped> x; };
#endif
boost::unordered_detail::destroy(node_->value_ptr());
}
if (node_constructed_)
buckets_.node_alloc().destroy(node_);
buckets_.node_alloc().deallocate(node_, 1);
}
}
template <class Alloc, class Grouped>
inline void hash_node_constructor<Alloc, Grouped>::construct_preamble()
{
if(!node_) {
node_constructed_ = false;
value_constructed_ = false;
node_ = buckets_.node_alloc().allocate(1);
buckets_.node_alloc().construct(node_, node());
node_constructed_ = true;
}
else {
BOOST_ASSERT(node_constructed_ && value_constructed_);
boost::unordered_detail::destroy(node_->value_ptr());
value_constructed_ = false;
}
}
}}
}}}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// Copyright (C) 2008-2009 Daniel James.
// Copyright (C) 2008-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)
@ -10,44 +10,38 @@
# pragma once
#endif
#include <boost/config.hpp>
#include <memory>
#include <functional>
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
namespace boost
{
template <class K,
class T,
class H = hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_map;
template <class K, class T, class H, class P, class A>
inline bool operator==(unordered_map<K, T, H, P, A> const&,
unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(unordered_map<K, T, H, P, A> const&,
unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_map<K, T, H, P, A>&,
unordered_map<K, T, H, P, A>&);
namespace unordered
{
template <class K, class T, class H, class P, class A>
inline bool operator==(unordered_map<K, T, H, P, A> const&,
unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(unordered_map<K, T, H, P, A> const&,
unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_map<K, T, H, P, A>&,
unordered_map<K, T, H, P, A>&);
template <class K,
class T,
class H = hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_multimap;
template <class K, class T, class H, class P, class A>
inline bool operator==(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_multimap<K, T, H, P, A>&,
unordered_multimap<K, T, H, P, A>&);
template <class K, class T, class H, class P, class A>
inline bool operator==(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_multimap<K, T, H, P, A>&,
unordered_multimap<K, T, H, P, A>&);
}
using ::boost::unordered::unordered_map;
using ::boost::unordered::unordered_multimap;
using ::boost::unordered::swap;
using ::boost::unordered::operator==;
using ::boost::unordered::operator!=;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// Copyright (C) 2008-2009 Daniel James.
// Copyright (C) 2008-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)
@ -10,42 +10,38 @@
# pragma once
#endif
#include <boost/config.hpp>
#include <memory>
#include <functional>
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
namespace boost
{
template <class T,
class H = hash<T>,
class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_set;
template <class T, class H, class P, class A>
inline bool operator==(unordered_set<T, H, P, A> const&,
unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(unordered_set<T, H, P, A> const&,
unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_set<T, H, P, A> &m1,
unordered_set<T, H, P, A> &m2);
namespace unordered
{
template <class T, class H, class P, class A>
inline bool operator==(unordered_set<T, H, P, A> const&,
unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(unordered_set<T, H, P, A> const&,
unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_set<T, H, P, A> &m1,
unordered_set<T, H, P, A> &m2);
template <class T,
class H = hash<T>,
class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_multiset;
template <class T, class H, class P, class A>
inline bool operator==(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_multiset<T, H, P, A> &m1,
unordered_multiset<T, H, P, A> &m2);
template <class T, class H, class P, class A>
inline bool operator==(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_multiset<T, H, P, A> &m1,
unordered_multiset<T, H, P, A> &m2);
}
using ::boost::unordered::unordered_set;
using ::boost::unordered::unordered_multiset;
using ::boost::unordered::swap;
using ::boost::unordered::operator==;
using ::boost::unordered::operator!=;
}
#endif

View File

@ -27,13 +27,12 @@ struct self_swap_base : public test::exception_base
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const {
std::string scope(test::scope);
#if BOOST_UNORDERED_SWAP_METHOD != 2
// TODO: In C++11 exceptions are only allowed in the swap function.
BOOST_TEST(
scope == "hash::operator(hash)" ||
scope == "hash::hash(hash)" ||
scope == "hash::operator=(hash)" ||
scope == "equal_to::operator(equal_to)" ||
scope == "equal_to::equal_to(equal_to)" ||
scope == "equal_to::operator=(equal_to)");
#endif
test::check_equivalent_keys(x);
}
@ -82,13 +81,12 @@ struct swap_base : public test::exception_base
void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const {
std::string scope(test::scope);
#if BOOST_UNORDERED_SWAP_METHOD != 2
// TODO: In C++11 exceptions are only allowed in the swap function.
BOOST_TEST(
scope == "hash::operator(hash)" ||
scope == "hash::hash(hash)" ||
scope == "hash::operator=(hash)" ||
scope == "equal_to::operator(equal_to)" ||
scope == "equal_to::equal_to(equal_to)" ||
scope == "equal_to::operator=(equal_to)");
#endif
test::check_equivalent_keys(d.x);
test::check_equivalent_keys(d.y);

View File

@ -75,6 +75,13 @@ namespace test {
namespace {
object_count& global_object_count = globally_counted_object::count_;
}
struct check_instances {
int instances;
check_instances() : instances(global_object_count.instances) {}
~check_instances() { BOOST_TEST(global_object_count.instances == instances); }
};
}
#endif

View File

@ -65,37 +65,32 @@ namespace test
std::cerr<<x1.count(key)<<","<<count<<"\n";
}
// I'm not bothering with the following test for now, as the
// previous test is probably more enough to catch the kind of
// errors that this would catch (if an element was in the wrong
// bucket it not be found by the call to count, if elements are not
// adjacent then they would be caught when checking against
// found_.
// // Check that the keys are in the correct bucket and are
// // adjacent in the bucket.
// BOOST_DEDUCED_TYPENAME X::size_type bucket = x1.bucket(key);
// BOOST_DEDUCED_TYPENAME X::const_local_iterator
// lit = x1.begin(bucket), lend = x1.end(bucket);
// for(; lit != lend && !eq(get_key<X>(*lit), key); ++lit) continue;
// if(lit == lend)
// BOOST_ERROR("Unable to find element with a local_iterator");
// unsigned int count2 = 0;
// for(; lit != lend && eq(get_key<X>(*lit), key); ++lit) ++count2;
// if(count != count2)
// BOOST_ERROR("Element count doesn't match local_iterator.");
// for(; lit != lend; ++lit) {
// if(eq(get_key<X>(*lit), key)) {
// BOOST_ERROR("Non-adjacent element with equivalent key "
// "in bucket.");
// break;
// }
// }
// Check that the keys are in the correct bucket and are
// adjacent in the bucket.
BOOST_DEDUCED_TYPENAME X::size_type bucket = x1.bucket(key);
BOOST_DEDUCED_TYPENAME X::const_local_iterator
lit = x1.begin(bucket), lend = x1.end(bucket);
for(; lit != lend && !eq(get_key<X>(*lit), key); ++lit) continue;
if(lit == lend)
BOOST_ERROR("Unable to find element with a local_iterator");
unsigned int count2 = 0;
for(; lit != lend && eq(get_key<X>(*lit), key); ++lit) ++count2;
if(count != count2)
BOOST_ERROR("Element count doesn't match local_iterator.");
for(; lit != lend; ++lit) {
if(eq(get_key<X>(*lit), key)) {
BOOST_ERROR("Non-adjacent element with equivalent key "
"in bucket.");
break;
}
}
};
// Finally, check that size matches up.
if(x1.size() != size)
if(x1.size() != size) {
BOOST_ERROR("x1.size() doesn't match actual size.");
std::cout<<x1.size()<<"/"<<size<<std::endl;
}
float load_factor =
static_cast<float>(size) / static_cast<float>(x1.bucket_count());
using namespace std;

View File

@ -70,7 +70,7 @@ namespace test
template <class Alloc = std::allocator<int> >
struct memory_tracker {
typedef BOOST_DEDUCED_TYPENAME
boost::unordered_detail::rebind_wrap<Alloc,
::boost::unordered::detail::rebind_wrap<Alloc,
std::pair<memory_area const, memory_track> >::type
allocator_type;
@ -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,37 @@ 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 <int Value>
struct bool_type {
enum { value = (Value ? true : false) };
};
struct true_type {
enum { value = true };
};
struct false_type {
enum { value = false };
};
template <typename Alloc>
int selected_count(Alloc const&)
{
return 0;
}
}
#endif

View File

@ -0,0 +1,294 @@
// 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/fwd.hpp"
#include "../helpers/memory.hpp"
namespace test
{
struct allocator_false
{
enum {
is_select_on_copy = 0,
is_propagate_on_swap = 0,
is_propagate_on_assign = 0,
is_propagate_on_move = 0
};
};
struct allocator_flags_all
{
enum {
is_select_on_copy = 1,
is_propagate_on_swap = 1,
is_propagate_on_assign = 1,
is_propagate_on_move = 1
};
};
struct select_copy : allocator_false
{ enum { is_select_on_copy = 1 }; };
struct propagate_swap : allocator_false
{ enum { is_propagate_on_swap = 1 }; };
struct propagate_assign : allocator_false
{ enum { is_propagate_on_assign = 1 }; };
struct propagate_move : allocator_false
{ enum { is_propagate_on_move = 1 }; };
struct no_select_copy : allocator_flags_all
{ enum { is_select_on_copy = 0 }; };
struct no_propagate_swap : allocator_flags_all
{ enum { is_propagate_on_swap = 0 }; };
struct no_propagate_assign : allocator_flags_all
{ enum { is_propagate_on_assign = 0 }; };
struct no_propagate_move : allocator_flags_all
{ enum { is_propagate_on_move = 0 }; };
template <typename Flag>
struct swap_allocator_base
{
struct propagate_on_container_swap {
enum { value = Flag::is_propagate_on_swap }; };
};
template <typename Flag>
struct assign_allocator_base
{
struct propagate_on_container_copy_assignment {
enum { value = Flag::is_propagate_on_assign }; };
};
template <typename Flag>
struct move_allocator_base
{
struct propagate_on_container_move_assignment {
enum { value = Flag::is_propagate_on_move }; };
};
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>
struct cxx11_allocator_base
{
int tag_;
int selected_;
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;
explicit cxx11_allocator_base(int t)
: tag_(t), selected_(0)
{
detail::tracker.allocator_ref();
}
template <typename Y> cxx11_allocator_base(
cxx11_allocator_base<Y> const& x)
: tag_(x.tag_), selected_(x.selected_)
{
detail::tracker.allocator_ref();
}
cxx11_allocator_base(cxx11_allocator_base const& x)
: tag_(x.tag_), selected_(x.selected_)
{
detail::tracker.allocator_ref();
}
~cxx11_allocator_base()
{
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_,
!force_equal_allocator_value);
::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)();
}
};
template <typename T, typename Flags = propagate_swap,
bool SelectCopy = Flags::is_select_on_copy ? true : false>
struct cxx11_allocator :
public cxx11_allocator_base<T>,
public swap_allocator_base<Flags>,
public assign_allocator_base<Flags>,
public move_allocator_base<Flags>,
Flags
{
template <typename U> struct rebind {
typedef cxx11_allocator<U, Flags> other;
};
explicit cxx11_allocator(int t = 0)
: cxx11_allocator_base<T>(t)
{
}
template <typename Y> cxx11_allocator(
cxx11_allocator<Y, Flags> const& x)
: cxx11_allocator_base<T>(x)
{
}
cxx11_allocator(cxx11_allocator const& x)
: cxx11_allocator_base<T>(x)
{
}
// When not propagating swap, allocators are always equal
// to avoid undefined behaviour.
bool operator==(cxx11_allocator const& x) const
{
return force_equal_allocator_value || (this->tag_ == x.tag_);
}
bool operator!=(cxx11_allocator const& x) const
{
return !(*this == x);
}
};
template <typename T, typename Flags>
struct cxx11_allocator<T, Flags, true> :
public cxx11_allocator_base<T>,
public swap_allocator_base<Flags>,
public assign_allocator_base<Flags>,
public move_allocator_base<Flags>,
Flags
{
cxx11_allocator select_on_container_copy_construction() const
{
cxx11_allocator tmp(*this);
++tmp.selected_;
return tmp;
}
template <typename U> struct rebind {
typedef cxx11_allocator<U, Flags> other;
};
explicit cxx11_allocator(int t = 0)
: cxx11_allocator_base<T>(t)
{
}
template <typename Y> cxx11_allocator(
cxx11_allocator<Y, Flags> const& x)
: cxx11_allocator_base<T>(x)
{
}
cxx11_allocator(cxx11_allocator const& x)
: cxx11_allocator_base<T>(x)
{
}
// When not propagating swap, allocators are always equal
// to avoid undefined behaviour.
bool operator==(cxx11_allocator const& x) const
{
return force_equal_allocator_value || (this->tag_ == x.tag_);
}
bool operator!=(cxx11_allocator const& x) const
{
return !(*this == x);
}
};
template <typename T, typename 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, typename Flags>
int selected_count(cxx11_allocator<T, Flags> const& x)
{
return x.selected_;
}
}
#endif

View File

@ -34,6 +34,16 @@ namespace exception
template <class T> class allocator;
object generate(object const*);
struct true_type
{
enum { value = true };
};
struct false_type
{
enum { value = false };
};
class object
{
public:
@ -340,15 +350,15 @@ namespace exception
}
void construct(pointer p, T const& t) {
UNORDERED_SCOPE(allocator::construct(pointer, T)) {
UNORDERED_SCOPE(allocator::construct(T*, T)) {
UNORDERED_EPOINT("Mock allocator construct function.");
new(p) T(t);
}
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
template<class... Args> void construct(pointer p, Args&&... args) {
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template<class... Args> void construct(T* p, Args&&... args) {
UNORDERED_SCOPE(allocator::construct(pointer, Args&&...)) {
UNORDERED_EPOINT("Mock allocator construct function.");
new(p) T(std::forward<Args>(args)...);
@ -357,7 +367,7 @@ namespace exception
}
#endif
void destroy(pointer p) {
void destroy(T* p) {
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
p->~T();
}
@ -368,6 +378,10 @@ namespace exception
}
return (std::numeric_limits<std::size_t>::max)();
}
typedef true_type propagate_on_container_copy_assignment;
typedef true_type propagate_on_container_move_assignment;
typedef true_type propagate_on_container_swap;
};
template <class T>

View File

@ -11,6 +11,8 @@
#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER
#include <cstddef>
#include <boost/move/move.hpp>
#include <utility>
#if defined(BOOST_MSVC)
#pragma warning(push)
@ -21,6 +23,7 @@ namespace test
{
namespace minimal
{
class destructible;
class copy_constructible;
class copy_constructible_equality_comparable;
class default_copy_constructible;
@ -33,11 +36,27 @@ namespace minimal
template <class T> class ptr;
template <class T> class const_ptr;
template <class T> class allocator;
template <class T> class cxx11_allocator;
struct constructor_param
{
operator int() const { return 0; }
};
class destructible
{
public:
destructible(constructor_param const&) {}
~destructible() {}
private:
destructible(destructible const&);
destructible& operator=(destructible const&);
};
class copy_constructible
{
public:
static copy_constructible create() { return copy_constructible(); }
copy_constructible(constructor_param const&) {}
copy_constructible(copy_constructible const&) {}
~copy_constructible() {}
private:
@ -48,9 +67,7 @@ namespace minimal
class copy_constructible_equality_comparable
{
public:
static copy_constructible_equality_comparable create() {
return copy_constructible_equality_comparable();
}
copy_constructible_equality_comparable(constructor_param const&) {}
copy_constructible_equality_comparable(
copy_constructible_equality_comparable const&)
@ -85,10 +102,7 @@ namespace minimal
class default_copy_constructible
{
public:
static default_copy_constructible create()
{
return default_copy_constructible();
}
default_copy_constructible(constructor_param const&) {}
default_copy_constructible()
{
@ -105,13 +119,14 @@ namespace minimal
private:
default_copy_constructible& operator=(
default_copy_constructible const&);
ampersand_operator_used operator&() const { return ampersand_operator_used(); }
ampersand_operator_used operator&() const {
return ampersand_operator_used(); }
};
class assignable
{
public:
static assignable create() { return assignable(); }
assignable(constructor_param const&) {}
assignable(assignable const&) {}
assignable& operator=(assignable const&) { return *this; }
~assignable() {}
@ -122,11 +137,43 @@ namespace minimal
//ampersand_operator_used operator&() const { return ampersand_operator_used(); }
};
struct movable_init {};
class movable1
{
BOOST_MOVABLE_BUT_NOT_COPYABLE(movable1)
public:
movable1(constructor_param const&) {}
movable1() {}
explicit movable1(movable_init) {}
movable1(BOOST_RV_REF(movable1)) {}
movable1& operator=(BOOST_RV_REF(movable1));
~movable1() {}
};
#if !defined(BOOST_NO_RVALUE_REFERENCES)
class movable2
{
public:
movable2(constructor_param const&) {}
explicit movable2(movable_init) {}
movable2(movable2&&) {}
~movable2() {}
private:
movable2() {}
movable2(movable2 const&);
movable2& operator=(movable2 const&);
};
#else
typedef movable1 movable2;
#endif
template <class T>
class hash
{
public:
static hash create() { return hash<T>(); }
hash(constructor_param const&) {}
hash() {}
hash(hash const&) {}
hash& operator=(hash const&) { return *this; }
@ -141,7 +188,7 @@ namespace minimal
class equal_to
{
public:
static equal_to create() { return equal_to<T>(); }
equal_to(constructor_param const&) {}
equal_to() {}
equal_to(equal_to const&) {}
equal_to& operator=(equal_to const&) { return *this; }
@ -278,15 +325,15 @@ namespace minimal
::operator delete((void*) p.ptr_);
}
void construct(pointer p, T const& t) { new((void*)p.ptr_) T(t); }
void construct(T* p, T const& t) { new((void*)p) T(t); }
#if defined(BOOST_UNORDERED_STD_FORWARD)
template<class... Args> void construct(pointer p, Args&&... args) {
new((void*)p.ptr_) T(std::forward<Args>(args)...);
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template<class... Args> void construct(T* p, Args&&... args) {
new((void*)p) T(std::forward<Args>(args)...);
}
#endif
void destroy(pointer p) { ((T*)p.ptr_)->~T(); }
void destroy(T* p) { p->~T(); }
size_type max_size() const { return 1000; }
@ -316,6 +363,69 @@ namespace minimal
void swap(allocator<T>&, allocator<T>&)
{
}
// C++11 allocator
//
// Not a fully minimal C++11 allocator, just what I support. Hopefully will
// cut down further in the future.
template <class T>
class cxx11_allocator
{
public:
typedef T value_type;
template <class U> struct rebind { typedef cxx11_allocator<U> other; };
cxx11_allocator() {}
template <class Y> cxx11_allocator(cxx11_allocator<Y> const&) {}
cxx11_allocator(cxx11_allocator const&) {}
~cxx11_allocator() {}
T* address(T& r) { return &r; }
T const* address(T const& r) { return &r; }
T* allocate(std::size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
template <class Y>
T* allocate(std::size_t n, const_ptr<Y> u) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) {
::operator delete((void*) p);
}
void construct(T* p, T const& t) { new((void*)p) T(t); }
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template<class... Args> void construct(T* p, Args&&... args) {
new((void*)p) T(std::forward<Args>(args)...);
}
#endif
void destroy(T* p) { p->~T(); }
std::size_t max_size() const { return 1000u; }
};
template <class T>
inline bool operator==(cxx11_allocator<T> const&, cxx11_allocator<T> const&)
{
return true;
}
template <class T>
inline bool operator!=(cxx11_allocator<T> const&, cxx11_allocator<T> const&)
{
return false;
}
template <class T>
void swap(cxx11_allocator<T>&, cxx11_allocator<T>&)
{
}
}
}

View File

@ -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
{
@ -27,7 +25,7 @@ namespace test
template <class T> class allocator;
object generate(object const*);
implicitly_convertible generate(implicitly_convertible const*);
class object : globally_counted_object
{
friend class hash;
@ -185,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
{
@ -268,19 +254,19 @@ namespace test
::operator delete((void*) p);
}
void construct(pointer p, T const& t) {
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)
template<class... Args> void construct(pointer p, Args&&... args) {
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template<class... 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(pointer p) {
void destroy(T* p) {
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
p->~T();
}
@ -298,6 +284,13 @@ namespace test
{
return tag_ != x.tag_;
}
enum {
is_select_on_copy = false,
is_propagate_on_swap = false,
is_propagate_on_assign = false,
is_propagate_on_move = false
};
};
template <class T>

View File

@ -43,5 +43,5 @@ test-suite unordered
[ run load_factor_tests.cpp ]
[ run rehash_tests.cpp ]
[ run equality_tests.cpp ]
[ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ]
[ run swap_tests.cpp ]
;

View File

@ -9,12 +9,17 @@
#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"
#include <iostream>
#if defined(BOOST_MSVC)
#pragma warning(disable:4127) // conditional expression is constant
#endif
namespace assign_tests {
test::seed_t seed(96785);
@ -28,6 +33,8 @@ void assign_tests1(T*,
std::cerr<<"assign_tests1.1\n";
{
test::check_instances check_;
T x;
x = x;
BOOST_TEST(x.empty());
@ -37,6 +44,8 @@ void assign_tests1(T*,
std::cerr<<"assign_tests1.2\n";
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end());
@ -64,9 +73,13 @@ 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_;
test::random_values<T> v(1000, generator);
T x1(v.begin(), v.end(), 0, hf1, eq1);
T x2(0, hf2, eq2);
@ -78,13 +91,22 @@ void assign_tests2(T*,
std::cerr<<"assign_tests2.2\n";
{
test::check_instances check_;
test::random_values<T> v1(100, generator), v2(100, generator);
T x1(v1.begin(), v1.end(), 0, hf1, eq1, al1);
T x2(v2.begin(), v2.end(), 0, hf2, eq2, al2);
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 (allocator_type::is_propagate_on_assign) {
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);
}
}
@ -102,16 +124,69 @@ 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))
template <typename T>
bool is_propagate(T*)
{
return T::allocator_type::is_propagate_on_assign;
}
UNORDERED_AUTO_TEST(check_traits)
{
BOOST_TEST(!is_propagate(test_set));
BOOST_TEST(is_propagate(test_set_prop_assign));
BOOST_TEST(!is_propagate(test_set_no_prop_assign));
}
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))
)

View File

@ -25,6 +25,8 @@ test::seed_t seed(54635);
template <class X>
void tests(X* = 0, test::random_generator generator = test::default_generator)
{
test::check_instances check_;
typedef BOOST_DEDUCED_TYPENAME X::size_type size_type;
typedef BOOST_DEDUCED_TYPENAME X::const_local_iterator const_local_iterator;
test::random_values<X> v(1000, generator);

View File

@ -17,6 +17,19 @@
// Explicit instantiation to catch compile-time errors
template class boost::unordered_map<
int,
int,
boost::hash<int>,
std::equal_to<int>,
test::minimal::allocator<std::pair<int const, int> > >;
template class boost::unordered_multimap<
int,
int,
boost::hash<int>,
std::equal_to<int>,
test::minimal::allocator<std::pair<int const, int> > >;
template class boost::unordered_map<
test::minimal::assignable,
test::minimal::default_copy_constructible,
@ -32,16 +45,21 @@ template class boost::unordered_multimap<
UNORDERED_AUTO_TEST(test0)
{
test::minimal::constructor_param x;
typedef std::pair<test::minimal::assignable const,
test::minimal::copy_constructible> value_type;
value_type value(
test::minimal::assignable::create(),
test::minimal::copy_constructible::create());
value_type value(x, x);
std::cout<<"Test unordered_map.\n";
boost::unordered_map<int, int> int_map;
boost::unordered_map<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> int_map2;
boost::unordered_map<
test::minimal::assignable,
test::minimal::copy_constructible,
@ -50,12 +68,18 @@ UNORDERED_AUTO_TEST(test0)
test::minimal::allocator<value_type> > map;
container_test(int_map, std::pair<int const, int>(0, 0));
container_test(int_map2, std::pair<int const, int>(0, 0));
container_test(map, value);
std::cout<<"Test unordered_multimap.\n";
boost::unordered_multimap<int, int> int_multimap;
boost::unordered_multimap<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> int_multimap2;
boost::unordered_multimap<
test::minimal::assignable,
test::minimal::copy_constructible,
@ -64,35 +88,49 @@ UNORDERED_AUTO_TEST(test0)
test::minimal::allocator<value_type> > multimap;
container_test(int_multimap, std::pair<int const, int>(0, 0));
container_test(int_multimap2, std::pair<int const, int>(0, 0));
container_test(multimap, value);
}
UNORDERED_AUTO_TEST(equality_tests) {
typedef std::pair<test::minimal::assignable const,
typedef std::pair<
test::minimal::copy_constructible_equality_comparable const,
test::minimal::copy_constructible> value_type;
boost::unordered_map<int, int> int_map;
boost::unordered_map<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> int_map2;
boost::unordered_map<
test::minimal::assignable,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> > map;
equality_test(int_map);
equality_test(int_map2);
equality_test(map);
boost::unordered_multimap<int, int> int_multimap;
boost::unordered_multimap<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> int_multimap2;
boost::unordered_multimap<
test::minimal::assignable,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> > multimap;
equality_test(int_multimap);
equality_test(int_multimap2);
equality_test(multimap);
}
@ -106,30 +144,47 @@ UNORDERED_AUTO_TEST(test1) {
boost::unordered_map<int, int> map;
boost::unordered_map<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> map2;
unordered_unique_test(map, map_value);
unordered_map_test(map, value, value);
unordered_test(map, value, map_value, hash, equal_to);
unordered_copyable_test(map, value, map_value, hash, equal_to);
unordered_map_functions(map, value, value);
unordered_unique_test(map2, map_value);
unordered_map_test(map2, value, value);
unordered_copyable_test(map2, value, map_value, hash, equal_to);
unordered_map_functions(map2, value, value);
std::cout<<"Test unordered_multimap.\n";
boost::unordered_multimap<int, int> multimap;
boost::unordered_multimap<int, int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> >
> multimap2;
unordered_equivalent_test(multimap, map_value);
unordered_map_test(multimap, value, value);
unordered_test(multimap, value, map_value, hash, equal_to);
unordered_copyable_test(multimap, value, map_value, hash, equal_to);
unordered_equivalent_test(multimap2, map_value);
unordered_map_test(multimap2, value, value);
unordered_copyable_test(multimap2, value, map_value, hash, equal_to);
}
UNORDERED_AUTO_TEST(test2)
{
test::minimal::assignable assignable
= test::minimal::assignable::create();
test::minimal::copy_constructible copy_constructible
= test::minimal::copy_constructible::create();
test::minimal::hash<test::minimal::assignable> hash
= test::minimal::hash<test::minimal::assignable>::create();
test::minimal::equal_to<test::minimal::assignable> equal_to
= test::minimal::equal_to<test::minimal::assignable>::create();
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
test::minimal::copy_constructible copy_constructible(x);
test::minimal::hash<test::minimal::assignable> hash(x);
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
typedef std::pair<test::minimal::assignable const,
test::minimal::copy_constructible> map_value_type;
@ -146,8 +201,7 @@ UNORDERED_AUTO_TEST(test2)
unordered_unique_test(map, map_value);
unordered_map_test(map, assignable, copy_constructible);
unordered_test(map, assignable, map_value, hash, equal_to);
unordered_copyable_test(map, assignable, map_value, hash, equal_to);
boost::unordered_map<
test::minimal::assignable,
@ -171,7 +225,7 @@ UNORDERED_AUTO_TEST(test2)
unordered_equivalent_test(multimap, map_value);
unordered_map_test(multimap, assignable, copy_constructible);
unordered_test(multimap, assignable, map_value, hash, equal_to);
unordered_copyable_test(multimap, assignable, map_value, hash, equal_to);
}
RUN_TESTS()

View File

@ -16,7 +16,18 @@
#include "./compile_tests.hpp"
// Explicit instantiation to catch compile-time errors
/*
template class boost::unordered_set<
int,
boost::hash<int>,
std::equal_to<int>,
test::minimal::allocator<int> >;
template class boost::unordered_multiset<
int,
boost::hash<int>,
std::equal_to<int>,
test::minimal::allocator<int> >;
*/
template class boost::unordered_set<
test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
@ -30,10 +41,19 @@ template class boost::unordered_multiset<
UNORDERED_AUTO_TEST(test0)
{
test::minimal::assignable assignable = test::minimal::assignable::create();
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
std::cout<<"Test unordered_set.\n";
boost::unordered_set<int> int_set;
boost::unordered_set<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> int_set2;
boost::unordered_set<
test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
@ -41,10 +61,18 @@ UNORDERED_AUTO_TEST(test0)
test::minimal::allocator<test::minimal::assignable> > set;
container_test(int_set, 0);
container_test(int_set2, 0);
container_test(set, assignable);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<int> int_multiset;
boost::unordered_multiset<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> int_multiset2;
boost::unordered_multiset<
test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
@ -52,32 +80,45 @@ UNORDERED_AUTO_TEST(test0)
test::minimal::allocator<test::minimal::assignable> > multiset;
container_test(int_multiset, 0);
container_test(int_multiset2, 0);
container_test(multiset, assignable);
}
UNORDERED_AUTO_TEST(equality_tests) {
typedef test::minimal::assignable value_type;
typedef test::minimal::copy_constructible_equality_comparable value_type;
boost::unordered_set<int> int_set;
boost::unordered_set<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> int_set2;
boost::unordered_set<
test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> > set;
equality_test(int_set);
equality_test(int_set2);
equality_test(set);
boost::unordered_multiset<int> int_multiset;
boost::unordered_multiset<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> int_multiset2;
boost::unordered_multiset<
test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> > multiset;
equality_test(int_multiset);
equality_test(int_multiset2);
equality_test(multiset);
}
@ -91,29 +132,45 @@ UNORDERED_AUTO_TEST(test1)
boost::unordered_set<int> set;
boost::unordered_set<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> set2;
unordered_unique_test(set, value);
unordered_set_test(set, value);
unordered_test(set, value, value, hash, equal_to);
unordered_copyable_test(set, value, value, hash, equal_to);
unordered_unique_test(set2, value);
unordered_set_test(set2, value);
unordered_copyable_test(set2, value, value, hash, equal_to);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<int> multiset;
boost::unordered_multiset<int,
boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int>
> multiset2;
unordered_equivalent_test(multiset, value);
unordered_set_test(multiset, value);
unordered_test(multiset, value, value, hash, equal_to);
unordered_copyable_test(multiset, value, value, hash, equal_to);
unordered_equivalent_test(multiset2, value);
unordered_set_test(multiset2, value);
unordered_copyable_test(multiset2, value, value, hash, equal_to);
}
UNORDERED_AUTO_TEST(test2)
{
test::minimal::assignable assignable
= test::minimal::assignable::create();
test::minimal::copy_constructible copy_constructible
= test::minimal::copy_constructible::create();
test::minimal::hash<test::minimal::assignable> hash
= test::minimal::hash<test::minimal::assignable>::create();
test::minimal::equal_to<test::minimal::assignable> equal_to
= test::minimal::equal_to<test::minimal::assignable>::create();
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
test::minimal::copy_constructible copy_constructible(x);
test::minimal::hash<test::minimal::assignable> hash(x);
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
std::cout<<"Test unordered_set.\n";
@ -125,7 +182,7 @@ UNORDERED_AUTO_TEST(test2)
unordered_unique_test(set, assignable);
unordered_set_test(set, assignable);
unordered_test(set, assignable, assignable, hash, equal_to);
unordered_copyable_test(set, assignable, assignable, hash, equal_to);
std::cout<<"Test unordered_multiset.\n";
@ -137,7 +194,100 @@ UNORDERED_AUTO_TEST(test2)
unordered_equivalent_test(multiset, assignable);
unordered_set_test(multiset, assignable);
unordered_test(multiset, assignable, assignable, hash, equal_to);
unordered_copyable_test(multiset, assignable, assignable, hash, equal_to);
}
UNORDERED_AUTO_TEST(movable1_tests)
{
test::minimal::constructor_param x;
test::minimal::movable1 movable1(x);
test::minimal::hash<test::minimal::movable1> hash(x);
test::minimal::equal_to<test::minimal::movable1> equal_to(x);
std::cout<<"Test unordered_set.\n";
boost::unordered_set<
test::minimal::movable1,
test::minimal::hash<test::minimal::movable1>,
test::minimal::equal_to<test::minimal::movable1>,
test::minimal::allocator<test::minimal::movable1> > set;
//unordered_unique_test(set, movable1);
unordered_set_test(set, movable1);
unordered_movable_test(set, movable1, movable1, hash, equal_to);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<
test::minimal::movable1,
test::minimal::hash<test::minimal::movable1>,
test::minimal::equal_to<test::minimal::movable1>,
test::minimal::allocator<test::minimal::movable1> > multiset;
//unordered_equivalent_test(multiset, movable1);
unordered_set_test(multiset, movable1);
unordered_movable_test(multiset, movable1, movable1, hash, equal_to);
}
UNORDERED_AUTO_TEST(movable2_tests)
{
test::minimal::constructor_param x;
test::minimal::movable2 movable2(x);
test::minimal::hash<test::minimal::movable2> hash(x);
test::minimal::equal_to<test::minimal::movable2> equal_to(x);
std::cout<<"Test unordered_set.\n";
boost::unordered_set<
test::minimal::movable2,
test::minimal::hash<test::minimal::movable2>,
test::minimal::equal_to<test::minimal::movable2>,
test::minimal::allocator<test::minimal::movable2> > set;
//unordered_unique_test(set, movable2);
unordered_set_test(set, movable2);
unordered_movable_test(set, movable2, movable2, hash, equal_to);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<
test::minimal::movable2,
test::minimal::hash<test::minimal::movable2>,
test::minimal::equal_to<test::minimal::movable2>,
test::minimal::allocator<test::minimal::movable2> > multiset;
//unordered_equivalent_test(multiset, movable2);
unordered_set_test(multiset, movable2);
unordered_movable_test(multiset, movable2, movable2, hash, equal_to);
}
UNORDERED_AUTO_TEST(destructible_tests)
{
test::minimal::constructor_param x;
test::minimal::destructible destructible(x);
test::minimal::hash<test::minimal::destructible> hash(x);
test::minimal::equal_to<test::minimal::destructible> equal_to(x);
std::cout<<"Test unordered_set.\n";
boost::unordered_set<
test::minimal::destructible,
test::minimal::hash<test::minimal::destructible>,
test::minimal::equal_to<test::minimal::destructible> > set;
unordered_destructible_test(set);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<
test::minimal::destructible,
test::minimal::hash<test::minimal::destructible>,
test::minimal::equal_to<test::minimal::destructible> > multiset;
unordered_destructible_test(multiset);
}
RUN_TESTS()

View File

@ -28,6 +28,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 T> T rvalue_default() { return T(); }
template <class X, class T>
void container_test(X& r, T const&)
@ -109,30 +110,15 @@ void container_test(X& r, T const&)
BOOST_TEST(X().size() == 0);
X a,b;
X a_const;
sink(X(a));
X u2(a);
X u3 = a;
X* ptr = new X();
X& a1 = *ptr;
(&a1)->~X();
X const a_const;
test::check_return_type<iterator>::equals(a.begin());
test::check_return_type<const_iterator>::equals(a_const.begin());
test::check_return_type<const_iterator>::equals(a.cbegin());
test::check_return_type<const_iterator>::equals(a_const.cbegin());
test::check_return_type<iterator>::equals(a.end());
test::check_return_type<const_iterator>::equals(a_const.end());
test::check_return_type<const_iterator>::equals(a.cend());
test::check_return_type<const_iterator>::equals(a_const.cend());
a.swap(b);
boost::swap(a, b);
test::check_return_type<X>::equals_ref(r = a);
test::check_return_type<size_type>::equals(a.size());
test::check_return_type<size_type>::equals(a.max_size());
test::check_return_type<bool>::convertible(a.empty());
// Allocator
@ -146,6 +132,51 @@ void container_test(X& r, T const&)
sink(u3);
}
template <class X>
void unordered_destructible_test(X&)
{
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
typedef BOOST_DEDUCED_TYPENAME X::const_iterator const_iterator;
typedef BOOST_DEDUCED_TYPENAME X::size_type size_type;
X x1;
#if !defined(BOOST_NO_RVALUE_REFERENCES)
X x2(rvalue_default<X>());
X x3 = rvalue_default<X>();
// This can only be done if propagate_on_container_move_assignment::value
// is true.
// x2 = rvalue_default<X>();
#endif
X* ptr = new X();
X& a1 = *ptr;
(&a1)->~X();
X a,b;
X const a_const;
test::check_return_type<iterator>::equals(a.begin());
test::check_return_type<const_iterator>::equals(a_const.begin());
test::check_return_type<const_iterator>::equals(a.cbegin());
test::check_return_type<const_iterator>::equals(a_const.cbegin());
test::check_return_type<iterator>::equals(a.end());
test::check_return_type<const_iterator>::equals(a_const.end());
test::check_return_type<const_iterator>::equals(a.cend());
test::check_return_type<const_iterator>::equals(a_const.cend());
a.swap(b);
boost::swap(a, b);
test::check_return_type<size_type>::equals(a.size());
test::check_return_type<size_type>::equals(a.max_size());
test::check_return_type<bool>::convertible(a.empty());
// Allocator
typedef BOOST_DEDUCED_TYPENAME X::allocator_type allocator_type;
test::check_return_type<allocator_type>::equals(a_const.get_allocator());
}
template <class X, class Key>
void unordered_set_test(X&, Key const&)
{
@ -160,6 +191,7 @@ 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> >));
@ -180,6 +212,8 @@ void equality_test(X& r)
test::check_return_type<bool>::equals(a == b);
test::check_return_type<bool>::equals(a != b);
test::check_return_type<bool>::equals(boost::operator==(a, b));
test::check_return_type<bool>::equals(boost::operator!=(a, b));
}
template <class X, class T>
@ -211,9 +245,11 @@ void unordered_map_functions(X&, Key const& k, T const&)
test::check_return_type<mapped_type const>::equals_ref(b.at(k));
}
template <class X, class Key, class T, class Hash, class Pred>
void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq)
template <class X, class Key, class Hash, class Pred>
void unordered_test(X& x, Key& k, Hash& hf, Pred& eq)
{
unordered_destructible_test(x);
typedef BOOST_DEDUCED_TYPENAME X::key_type key_type;
typedef BOOST_DEDUCED_TYPENAME X::hasher hasher;
typedef BOOST_DEDUCED_TYPENAME X::key_equal key_equal;
@ -277,8 +313,8 @@ void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq)
const_local_iterator_reference;
BOOST_MPL_ASSERT((boost::is_same<Key, key_type>));
boost::function_requires<boost::CopyConstructibleConcept<key_type> >();
boost::function_requires<boost::AssignableConcept<key_type> >();
//boost::function_requires<boost::CopyConstructibleConcept<key_type> >();
//boost::function_requires<boost::AssignableConcept<key_type> >();
BOOST_MPL_ASSERT((boost::is_same<Hash, hasher>));
test::check_return_type<std::size_t>::equals(hf(k));
@ -316,45 +352,18 @@ void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq)
X();
X a4;
BOOST_DEDUCED_TYPENAME X::value_type* i = 0;
BOOST_DEDUCED_TYPENAME X::value_type* j = 0;
X(i, j, 10, hf, eq);
X a5(i, j, 10, hf, eq);
X(i, j, 10, hf);
X a6(i, j, 10, hf);
X(i, j, 10);
X a7(i, j, 10);
X(i, j);
X a8(i, j);
X const b;
sink(X(b));
X a9(b);
a = b;
test::check_return_type<hasher>::equals(b.hash_function());
test::check_return_type<key_equal>::equals(b.key_eq());
const_iterator q = a.cbegin();
test::check_return_type<iterator>::equals(a.insert(q, t));
test::check_return_type<iterator>::equals(a.emplace_hint(q, t));
a.insert(i, j);
test::check_return_type<size_type>::equals(a.erase(k));
BOOST_TEST(a.empty());
if(a.empty()) {
a.insert(t);
q = a.cbegin();
test::check_return_type<iterator>::equals(a.erase(q));
}
const_iterator q1 = a.cbegin(), q2 = a.cend();
test::check_return_type<iterator>::equals(a.erase(q1, q2));
a.clear();
X const b;
test::check_return_type<hasher>::equals(b.hash_function());
test::check_return_type<key_equal>::equals(b.key_eq());
test::check_return_type<iterator>::equals(a.find(k));
test::check_return_type<const_iterator>::equals(b.find(k));
test::check_return_type<size_type>::equals(b.count(k));
@ -388,9 +397,117 @@ void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq)
sink(a2);
sink(a3);
sink(a4);
}
template <class X, class Key, class T, class Hash, class Pred>
void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq)
{
unordered_test(x, k, hf, eq);
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
typedef BOOST_DEDUCED_TYPENAME X::const_iterator const_iterator;
X a;
BOOST_DEDUCED_TYPENAME X::value_type* i = 0;
BOOST_DEDUCED_TYPENAME X::value_type* j = 0;
X(i, j, 10, hf, eq);
X a5(i, j, 10, hf, eq);
X(i, j, 10, hf);
X a6(i, j, 10, hf);
X(i, j, 10);
X a7(i, j, 10);
X(i, j);
X a8(i, j);
X const b;
sink(X(b));
X a9(b);
a = b;
const_iterator q = a.cbegin();
test::check_return_type<iterator>::equals(a.insert(q, t));
test::check_return_type<iterator>::equals(a.emplace_hint(q, t));
a.insert(i, j);
X a10;
a10.insert(t);
q = a10.cbegin();
test::check_return_type<iterator>::equals(a10.erase(q));
// Avoid unused variable warnings:
sink(a);
sink(a5);
sink(a6);
sink(a7);
sink(a8);
sink(a9);
}
template <class X, class Key, class T, class Hash, class Pred>
void unordered_movable_test(X& x, Key& k, T& /* t */, Hash& hf, Pred& eq)
{
unordered_test(x, k, hf, eq);
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
typedef BOOST_DEDUCED_TYPENAME X::const_iterator const_iterator;
#if !defined(BOOST_NO_RVALUE_REFERENCES)
X x1(rvalue_default<X>());
X x2(boost::move(x1));
x1 = rvalue_default<X>();
x2 = boost::move(x1);
#endif
test::minimal::constructor_param* i = 0;
test::minimal::constructor_param* j = 0;
X(i, j, 10, hf, eq);
X a5(i, j, 10, hf, eq);
X(i, j, 10, hf);
X a6(i, j, 10, hf);
X(i, j, 10);
X a7(i, j, 10);
X(i, j);
X a8(i, j);
X a;
const_iterator q = a.cbegin();
test::minimal::constructor_param v;
a.emplace(v);
test::check_return_type<iterator>::equals(a.emplace_hint(q, v));
T v1(v);
a.emplace(boost::move(v1));
T v2(v);
a.insert(boost::move(v2));
T v3(v);
test::check_return_type<iterator>::equals(
a.emplace_hint(q, boost::move(v3)));
T v4(v);
test::check_return_type<iterator>::equals(
a.insert(q, boost::move(v4)));
a.insert(i, j);
X a10;
T v5(v);
a10.insert(boost::move(v5));
q = a10.cbegin();
test::check_return_type<iterator>::equals(a10.erase(q));
// Avoid unused variable warnings:
sink(a);
sink(a5);
sink(a6);
sink(a7);
sink(a8);
sink(a10);
}

View File

@ -31,6 +31,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 1\n";
{
test::check_instances check_;
T x(0, hf, eq);
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -41,6 +43,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 2\n";
{
test::check_instances check_;
T x(100, hf);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 100);
@ -52,6 +56,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 3\n";
{
test::check_instances check_;
T x(2000);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 2000);
@ -63,6 +69,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 4\n";
{
test::check_instances check_;
T x;
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -73,6 +81,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 5\n";
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end(), 10000, hf, eq);
BOOST_TEST(x.bucket_count() >= 10000);
@ -85,6 +95,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 6\n";
{
test::check_instances check_;
test::random_values<T> v(10, generator);
T x(v.begin(), v.end(), 10000, hf);
BOOST_TEST(x.bucket_count() >= 10000);
@ -97,6 +109,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 7\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(v.begin(), v.end(), 100);
BOOST_TEST(x.bucket_count() >= 100);
@ -109,6 +123,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 8\n";
{
test::check_instances check_;
test::random_values<T> v(1, generator);
T x(v.begin(), v.end());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -120,6 +136,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 9\n";
{
test::check_instances check_;
T x(0, hf, eq, al);
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -130,6 +148,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 10\n";
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end(), 10000, hf, eq, al);
BOOST_TEST(x.bucket_count() >= 10000);
@ -142,6 +162,8 @@ void constructor_tests1(T*,
std::cerr<<"Construct 11\n";
{
test::check_instances check_;
T x(al);
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -167,6 +189,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 1\n";
{
test::check_instances check_;
T x(10000, hf1, eq1);
BOOST_TEST(x.bucket_count() >= 10000);
BOOST_TEST(test::equivalent(x.hash_function(), hf1));
@ -177,6 +200,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 2\n";
{
test::check_instances check_;
T x(100, hf1);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 100);
@ -188,6 +212,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 3\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(v.begin(), v.end(), 0, hf1, eq1);
BOOST_TEST(test::equivalent(x.hash_function(), hf1));
@ -199,6 +224,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 4\n";
{
test::check_instances check_;
test::random_values<T> v(5, generator);
T x(v.begin(), v.end(), 1000, hf1);
BOOST_TEST(x.bucket_count() >= 1000);
@ -212,6 +238,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 5\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(v.begin(), v.end(), 0, hf, eq, al1);
T y(x.begin(), x.end(), 0, hf1, eq1, al2);
@ -223,6 +250,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 6\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(v.begin(), v.end(), 0, hf1, eq1);
T y(x.begin(), x.end(), 0, hf, eq);
@ -234,6 +262,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 7\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(v.begin(), v.end(), 0, hf1, eq1);
T y(x.begin(), x.end(), 0, hf2, eq2);
@ -245,6 +274,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 8 - from input iterator\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
v_begin = v.begin(), v_end = v.end();
@ -262,6 +292,7 @@ void constructor_tests2(T*,
std::cerr<<"Construct 8.5 - from copy iterator\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(test::copy_iterator(v.begin()),
test::copy_iterator(v.end()), 0, hf1, eq1);
@ -275,6 +306,8 @@ void constructor_tests2(T*,
std::cerr<<"Construct 9\n";
{
test::check_instances check_;
test::random_values<T> v(100, generator);
T x(50);
BOOST_TEST(x.bucket_count() >= 50);
@ -291,6 +324,8 @@ void constructor_tests2(T*,
std::cerr<<"Initializer list construct 1\n";
{
test::check_instances check_;
T x(list);
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
@ -300,6 +335,8 @@ void constructor_tests2(T*,
std::cerr<<"Initializer list construct 2\n";
{
test::check_instances check_;
T x(list, 1000);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 1000);
@ -310,6 +347,8 @@ void constructor_tests2(T*,
std::cerr<<"Initializer list construct 3\n";
{
test::check_instances check_;
T x(list, 10, hf1);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 10);
@ -320,6 +359,8 @@ void constructor_tests2(T*,
std::cerr<<"Initializer list construct 4\n";
{
test::check_instances check_;
T x(list, 10, hf1, eq1);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 10);
@ -330,6 +371,8 @@ void constructor_tests2(T*,
std::cerr<<"Initializer list construct 5\n";
{
test::check_instances check_;
T x(list, 10, hf1, eq1, al1);
BOOST_TEST(x.empty());
BOOST_TEST(x.bucket_count() >= 10);

View File

@ -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"
@ -23,11 +24,15 @@ template <class T>
void copy_construct_tests1(T*,
test::random_generator const& generator = test::default_generator)
{
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
BOOST_DEDUCED_TYPENAME T::hasher hf;
BOOST_DEDUCED_TYPENAME T::key_equal eq;
BOOST_DEDUCED_TYPENAME T::allocator_type al;
BOOST_DEDUCED_TYPENAME T::allocator_type al;
{
test::check_instances check_;
T x;
T y(x);
BOOST_TEST(y.empty());
@ -35,20 +40,28 @@ void copy_construct_tests1(T*,
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end());
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
// In this test I drop the original containers max load factor, so it
// is much lower than the load factor. The hash table is not allowed
// to rehash, but the destination container should probably allocate
@ -61,6 +74,8 @@ void copy_construct_tests1(T*,
BOOST_TEST(equivalent(y));
// This isn't guaranteed:
BOOST_TEST(y.load_factor() < y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
test::check_equivalent_keys(y);
}
}
@ -75,8 +90,12 @@ void copy_construct_tests2(T* ptr,
BOOST_DEDUCED_TYPENAME T::key_equal eq(1);
BOOST_DEDUCED_TYPENAME T::allocator_type al(1);
BOOST_DEDUCED_TYPENAME T::allocator_type al2(2);
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
{
test::check_instances check_;
T x(10000, hf, eq, al);
T y(x);
BOOST_TEST(y.empty());
@ -84,10 +103,14 @@ void copy_construct_tests2(T* ptr,
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(1000, hf, eq, al);
T y(x, al2);
BOOST_TEST(y.empty());
@ -95,10 +118,13 @@ void copy_construct_tests2(T* ptr,
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end(), 0, hf, eq, al);
@ -106,10 +132,14 @@ void copy_construct_tests2(T* ptr,
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
}
{
test::check_instances check_;
test::random_values<T> v(500, generator);
T x(v.begin(), v.end(), 0, hf, eq, al);
@ -117,6 +147,7 @@ void copy_construct_tests2(T* ptr,
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
}
}
@ -134,15 +165,55 @@ 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::select_copy> >*
test_set_select_copy;
boost::unordered_multiset<test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::select_copy> >*
test_multiset_select_copy;
boost::unordered_map<test::object, test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::select_copy> >*
test_map_select_copy;
boost::unordered_multimap<test::object, test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::select_copy> >*
test_multimap_select_copy;
boost::unordered_set<test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_select_copy> >*
test_set_no_select_copy;
boost::unordered_multiset<test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_select_copy> >*
test_multiset_no_select_copy;
boost::unordered_map<test::object, test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_select_copy> >*
test_map_no_select_copy;
boost::unordered_multimap<test::object, test::object,
test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_select_copy> >*
test_multimap_no_select_copy;
using test::default_generator;
using test::generate_collisions;
UNORDERED_TEST(copy_construct_tests1,
((test_set)(test_multiset)(test_map)(test_multimap))
UNORDERED_TEST(copy_construct_tests1, (
(test_set)(test_multiset)(test_map)(test_multimap)
(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)
(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy)
)
)
UNORDERED_TEST(copy_construct_tests2,
((test_set)(test_multiset)(test_map)(test_multimap))
UNORDERED_TEST(copy_construct_tests2, (
(test_set)(test_multiset)(test_map)(test_multimap)
(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)
(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy)
)
((default_generator)(generate_collisions))
)

View File

@ -136,15 +136,17 @@ namespace equality_tests
UNORDERED_EQUALITY_MULTIMAP_TEST(
((1)(1))((1)(1)), !=, ((1)(1))((1)(2)))
UNORDERED_EQUALITY_MULTIMAP_TEST(
((1)(2))((1)(1)), !=, ((1)(1))((1)(2)))
((1)(2))((1)(1)), ==, ((1)(1))((1)(2)))
UNORDERED_EQUALITY_MULTIMAP_TEST(
((1)(2))((1)(1)), !=, ((1)(1))((1)(3)))
}
UNORDERED_AUTO_TEST(equality_predicate_test)
{
UNORDERED_EQUALITY_SET_TEST(
(1), ==, (1001))
(1), !=, (1001))
UNORDERED_EQUALITY_MAP_TEST(
((1)(2))((1001)(1)), ==, ((1001)(2))((1)(1)))
((1)(2))((1001)(1)), !=, ((1001)(2))((1)(1)))
}
// Test that equality still works when the two containers have

View File

@ -28,6 +28,8 @@ void erase_tests1(Container*,
{
std::cerr<<"Erase by key.\n";
{
test::check_instances check_;
test::random_values<Container> v(1000, generator);
Container x(v.begin(), v.end());
for(BOOST_DEDUCED_TYPENAME test::random_values<Container>::iterator
@ -44,6 +46,8 @@ void erase_tests1(Container*,
std::cerr<<"erase(begin()).\n";
{
test::check_instances check_;
test::random_values<Container> v(1000, generator);
Container x(v.begin(), v.end());
std::size_t size = x.size();
@ -64,6 +68,8 @@ void erase_tests1(Container*,
std::cerr<<"erase(random position).\n";
{
test::check_instances check_;
test::random_values<Container> v(1000, generator);
Container x(v.begin(), v.end());
std::size_t size = x.size();
@ -96,6 +102,8 @@ void erase_tests1(Container*,
std::cerr<<"erase(ranges).\n";
{
test::check_instances check_;
test::random_values<Container> v(500, generator);
Container x(v.begin(), v.end());
@ -118,6 +126,8 @@ void erase_tests1(Container*,
std::cerr<<"quick_erase(begin()).\n";
{
test::check_instances check_;
test::random_values<Container> v(1000, generator);
Container x(v.begin(), v.end());
std::size_t size = x.size();
@ -136,6 +146,8 @@ void erase_tests1(Container*,
std::cerr<<"quick_erase(random position).\n";
{
test::check_instances check_;
test::random_values<Container> v(1000, generator);
Container x(v.begin(), v.end());
std::size_t size = x.size();
@ -169,6 +181,8 @@ void erase_tests1(Container*,
std::cerr<<"clear().\n";
{
test::check_instances check_;
test::random_values<Container> v(500, generator);
Container x(v.begin(), v.end());
x.clear();

View File

@ -24,6 +24,8 @@ void find_tests1(X*, test::random_generator generator = test::default_generator)
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
{
test::check_instances check_;
test::random_values<X> v(500, generator);
X x(v.begin(), v.end());
X const& x_const = x;
@ -69,6 +71,8 @@ void find_tests1(X*, test::random_generator generator = test::default_generator)
}
{
test::check_instances check_;
X x;
test::random_values<X> v2(5, generator);

View File

@ -26,6 +26,8 @@ template <class X>
void unique_insert_tests1(X*,
test::random_generator generator = test::default_generator)
{
test::check_instances check_;
typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
typedef test::ordered<X> ordered;
@ -65,6 +67,9 @@ void equivalent_insert_tests1(X*,
{
std::cerr<<"insert(value) tests for containers with equivalent keys.\n";
test::check_instances check_;
X x;
test::ordered<X> tracker = test::create_ordered(x);
@ -102,6 +107,8 @@ void insert_tests2(X*,
std::cerr<<"insert(begin(), value) tests.\n";
{
test::check_instances check_;
X x;
tracker_type tracker = test::create_ordered(x);
@ -128,6 +135,8 @@ void insert_tests2(X*,
std::cerr<<"insert(end(), value) tests.\n";
{
test::check_instances check_;
X x;
X const& x_const = x;
tracker_type tracker = test::create_ordered(x);
@ -155,6 +164,8 @@ void insert_tests2(X*,
std::cerr<<"insert(pos, value) tests.\n";
{
test::check_instances check_;
X x;
const_iterator pos = x.begin();
tracker_type tracker = test::create_ordered(x);
@ -182,6 +193,8 @@ void insert_tests2(X*,
std::cerr<<"insert single item range tests.\n";
{
test::check_instances check_;
X x;
tracker_type tracker = test::create_ordered(x);
@ -207,6 +220,8 @@ void insert_tests2(X*,
std::cerr<<"insert range tests.\n";
{
test::check_instances check_;
X x;
test::random_values<X> v(1000, generator);
@ -219,6 +234,8 @@ void insert_tests2(X*,
std::cerr<<"insert input iterator range tests.\n";
{
test::check_instances check_;
X x;
test::random_values<X> v(1000, generator);
@ -233,6 +250,8 @@ void insert_tests2(X*,
std::cerr<<"insert copy iterator range tests.\n";
{
test::check_instances check_;
X x;
test::random_values<X> v(1000, generator);
@ -351,6 +370,8 @@ void map_insert_range_test1(X*,
{
std::cerr<<"map_insert_range_test1\n";
test::check_instances check_;
typedef test::list<
std::pair<
BOOST_DEDUCED_TYPENAME X::key_type,
@ -371,6 +392,8 @@ void map_insert_range_test2(X*,
{
std::cerr<<"map_insert_range_test2\n";
test::check_instances check_;
typedef test::list<
std::pair<BOOST_DEDUCED_TYPENAME X::key_type const, test::implicitly_convertible>
> list;

View File

@ -9,11 +9,16 @@
#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"
#include "../helpers/invariants.hpp"
#if defined(BOOST_MSVC)
#pragma warning(disable:4127) // conditional expression is constant
#endif
namespace move_tests
{
test::seed_t seed(98624);
@ -54,6 +59,8 @@ namespace move_tests
BOOST_DEDUCED_TYPENAME T::allocator_type al;
{
test::check_instances check_;
T y(empty(ptr));
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
@ -64,6 +71,8 @@ namespace move_tests
}
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
test::object_count count;
T y(create(v, count));
@ -80,6 +89,8 @@ namespace move_tests
test::random_generator const& generator = test::default_generator)
{
{
test::check_instances check_;
test::random_values<T> v(500, generator);
test::object_count count;
T y;
@ -104,6 +115,8 @@ namespace move_tests
test::object_count count;
{
test::check_instances check_;
test::random_values<T> v(500, generator);
T y(create(v, count, hf, eq, al, 0.5));
#if defined(BOOST_HAS_NRVO)
@ -118,6 +131,8 @@ namespace move_tests
}
{
test::check_instances check_;
// TODO: To do this correctly requires the fancy new allocator
// stuff.
test::random_values<T> v(500, generator);
@ -130,17 +145,24 @@ namespace move_tests
BOOST_TEST(y.max_load_factor() == 2.0); // Not necessarily required.
test::check_equivalent_keys(y);
}
/*
{
test::check_instances check_;
test::random_values<T> v(25, generator);
T y(create(v, count, hf, eq, al, 1.0), al);
#if !defined(BOOST_NO_RVALUE_REFERENCES)
BOOST_TEST(count == test::global_object_count);
#else
#elif defined(BOOST_HAS_NRVO)
BOOST_TEST(
test::global_object_count.constructions - count.constructions <=
(test::is_map<T>::value ? 50 : 25));
BOOST_TEST(count.instances == test::global_object_count.instances);
#else
BOOST_TEST(
test::global_object_count.constructions - count.constructions <=
(test::is_map<T>::value ? 100 : 50));
BOOST_TEST(count.instances == test::global_object_count.instances);
#endif
test::check_container(y, v);
BOOST_TEST(test::equivalent(y.hash_function(), hf));
@ -149,7 +171,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 (allocator_type::is_propagate_on_move) {
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 (allocator_type::is_propagate_on_move) {
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 (allocator_type::is_propagate_on_move) {
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,
@ -164,19 +236,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()

View File

@ -12,10 +12,15 @@
#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"
#if defined(BOOST_MSVC)
#pragma warning(disable:4127) // conditional expression is constant
#endif
namespace swap_tests
{
@ -37,16 +42,22 @@ template <class X>
void swap_tests1(X*, test::random_generator generator = test::default_generator)
{
{
test::check_instances check_;
X x;
swap_test_impl(x, x);
}
{
test::check_instances check_;
X x,y;
swap_test_impl(x, y);
}
{
test::check_instances check_;
test::random_values<X> v(1000, generator);
X x, y(v.begin(), v.end());
swap_test_impl(x, y);
@ -54,6 +65,8 @@ void swap_tests1(X*, test::random_generator generator = test::default_generator)
}
{
test::check_instances check_;
test::random_values<X> vx(1000, generator), vy(1000, generator);
X x(vx.begin(), vx.end()), y(vy.begin(), vy.end());
swap_test_impl(x, y);
@ -72,12 +85,16 @@ void swap_tests2(X* ptr = 0,
typedef BOOST_DEDUCED_TYPENAME X::allocator_type allocator_type;
{
test::check_instances check_;
X x(0, hasher(1), key_equal(1));
X y(0, hasher(2), key_equal(2));
swap_test_impl(x, y);
}
{
test::check_instances check_;
test::random_values<X> v(1000, generator);
X x(v.begin(), v.end(), 0, hasher(1), key_equal(1));
X y(0, hasher(2), key_equal(2));
@ -85,6 +102,8 @@ void swap_tests2(X* ptr = 0,
}
{
test::check_instances check_;
test::random_values<X> vx(100, generator), vy(50, generator);
X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1));
X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2));
@ -92,57 +111,113 @@ void swap_tests2(X* ptr = 0,
swap_test_impl(x, y);
}
#if BOOST_UNORDERED_SWAP_METHOD == 1
{
test::random_values<X> vx(100, generator), vy(50, 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));
try {
swap_test_impl(x, y);
BOOST_ERROR("Using swap method 1, "
"swapping with unequal allocators didn't throw.");
} catch (std::runtime_error) {}
}
#else
{
test::force_equal_allocator force_(
!allocator_type::is_propagate_on_swap);
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 (allocator_type::is_propagate_on_swap ||
x.get_allocator() == y.get_allocator())
{
swap_test_impl(x, y);
}
}
{
test::force_equal_allocator force_(
!allocator_type::is_propagate_on_swap);
test::check_instances check_;
test::random_values<X> vx(100, generator), vy(100, generator);
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);
swap_test_impl(x, y);
if (allocator_type::is_propagate_on_swap ||
x.get_allocator() == y.get_allocator())
{
swap_test_impl(x, y);
swap_test_impl(x, y);
}
}
#endif
}
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;
template <typename T>
bool is_propagate(T*)
{
return T::allocator_type::is_propagate_on_swap;
}
UNORDERED_AUTO_TEST(check_traits)
{
BOOST_TEST(!is_propagate(test_set));
BOOST_TEST(is_propagate(test_set_prop_swap));
BOOST_TEST(!is_propagate(test_set_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()

View File

@ -13,6 +13,9 @@ namespace unnecessary_copy_tests
{
struct count_copies
{
private:
BOOST_COPYABLE_AND_MOVABLE(count_copies)
public:
static int copies;
static int moves;
count_copies() : tag_(0) { ++copies; }
@ -31,14 +34,25 @@ namespace unnecessary_copy_tests
: tag_(x.tag_) { ++copies; }
count_copies(count_copies const& x) : tag_(x.tag_) { ++copies; }
#if !defined(BOOST_NO_RVALUE_REFERENCES)
count_copies(count_copies&& x) : tag_(x.tag_) {
count_copies(BOOST_RV_REF(count_copies) x) : tag_(x.tag_) {
x.tag_ = -1; ++moves;
}
#endif
int tag_;
private:
count_copies& operator=(count_copies const&);
count_copies& operator=(BOOST_COPY_ASSIGN_REF(count_copies) p) // Copy assignment
{
tag_ = p.tag_;
++copies;
return *this;
}
count_copies& operator=(BOOST_RV_REF(count_copies) p) //Move assignment
{
tag_ = p.tag_;
++moves;
return *this;
}
int tag_;
};
bool operator==(count_copies const& x, count_copies const& y) {
@ -68,31 +82,37 @@ namespace unnecessary_copy_tests
}
#define COPY_COUNT(n) \
if(count_copies::copies != n) { \
if(::unnecessary_copy_tests::count_copies::copies != n) { \
BOOST_ERROR("Wrong number of copies."); \
std::cerr \
<< "Number of copies: " << count_copies::copies \
<< "Number of copies: " \
<< ::unnecessary_copy_tests::count_copies::copies \
<< " expecting: " << n << std::endl; \
}
#define MOVE_COUNT(n) \
if(count_copies::moves != n) { \
if(::unnecessary_copy_tests::count_copies::moves != n) { \
BOOST_ERROR("Wrong number of moves."); \
std::cerr \
<< "Number of moves: " << count_copies::moves \
<< "Number of moves: " \
<< ::unnecessary_copy_tests::count_copies::moves \
<< " expecting: " <<n << std::endl; \
}
#define COPY_COUNT_RANGE(a, b) \
if(count_copies::copies < a || count_copies::copies > b) { \
if(::unnecessary_copy_tests::count_copies::copies < a || \
::unnecessary_copy_tests::count_copies::copies > b) { \
BOOST_ERROR("Wrong number of copies."); \
std::cerr \
<< "Number of copies: " << count_copies::copies \
<< "Number of copies: " \
<< ::unnecessary_copy_tests::count_copies::copies \
<< " expecting: [" << a << ", " << b << "]" << std::endl; \
}
#define MOVE_COUNT_RANGE(a, b) \
if(count_copies::moves < a || count_copies::moves > b) { \
if(::unnecessary_copy_tests::count_copies::moves < a || \
::unnecessary_copy_tests::count_copies::moves > b) { \
BOOST_ERROR("Wrong number of moves."); \
std::cerr \
<< "Number of moves: " << count_copies::copies \
<< "Number of moves: " \
<< ::unnecessary_copy_tests::count_copies::copies \
<< " expecting: [" << a << ", " << b << "]" << std::endl; \
}
@ -136,7 +156,7 @@ namespace unnecessary_copy_tests
reset();
T x;
x.emplace(source<BOOST_DEDUCED_TYPENAME T::value_type>());
#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES)
#if !defined(BOOST_NO_RVALUE_REFERENCES)
COPY_COUNT(1);
#else
COPY_COUNT(2);
@ -148,7 +168,7 @@ namespace unnecessary_copy_tests
UNORDERED_TEST(unnecessary_copy_emplace_rvalue_test,
((set)(multiset)(map)(multimap)))
#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template <class T>
void unnecessary_copy_emplace_move_test(T*)
{
@ -162,11 +182,51 @@ namespace unnecessary_copy_tests
UNORDERED_TEST(unnecessary_copy_emplace_move_test,
((set)(multiset)(map)(multimap)))
#endif
template <class T>
void unnecessary_copy_emplace_boost_move_set_test(T*)
{
reset();
T x;
BOOST_DEDUCED_TYPENAME T::value_type a;
COPY_COUNT(1); MOVE_COUNT(0);
x.emplace(boost::move(a));
COPY_COUNT(1); MOVE_COUNT(1);
}
UNORDERED_TEST(unnecessary_copy_emplace_boost_move_set_test,
((set)(multiset)))
template <class T>
void unnecessary_copy_emplace_boost_move_map_test(T*)
{
reset();
T x;
BOOST_DEDUCED_TYPENAME T::value_type a;
COPY_COUNT(1); MOVE_COUNT(0);
x.emplace(boost::move(a));
#if defined(BOOST_NO_RVALUE_REFERENCES)
COPY_COUNT(2); MOVE_COUNT(0);
#else
COPY_COUNT(1); MOVE_COUNT(1);
#endif
}
UNORDERED_TEST(unnecessary_copy_emplace_boost_move_map_test,
((map)(multimap)))
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test)
{
// When calling 'source' the object is moved on some compilers, but not
// others. So count that here to adjust later.
reset();
source<count_copies>();
int source_cost = ::unnecessary_copy_tests::count_copies::moves;
//
reset();
boost::unordered_set<count_copies> x;
count_copies a;
@ -181,7 +241,12 @@ namespace unnecessary_copy_tests
// the existing element.
reset();
x.emplace();
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
COPY_COUNT(1); MOVE_COUNT(0);
#else
// source_cost doesn't make much sense here, but it seems to fit.
COPY_COUNT(1); MOVE_COUNT(source_cost);
#endif
//
// 1 argument
@ -197,9 +262,9 @@ namespace unnecessary_copy_tests
// copied.
reset();
x.emplace(source<count_copies>());
COPY_COUNT(1); MOVE_COUNT(0);
COPY_COUNT(1); MOVE_COUNT(source_cost);
#if !defined(BOOST_NO_RVALUE_REFERENCES)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
// No move should take place.
reset();
x.emplace(std::move(a));
@ -233,6 +298,19 @@ namespace unnecessary_copy_tests
UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test)
{
// When calling 'source' the object is moved on some compilers, but not
// others. So count that here to adjust later.
reset();
source<count_copies>();
int source_cost = ::unnecessary_copy_tests::count_copies::moves;
reset();
source<std::pair<count_copies, count_copies> >();
int source_pair_cost = ::unnecessary_copy_tests::count_copies::moves;
//
reset();
boost::unordered_map<count_copies, count_copies> x;
// TODO: Run tests for pairs without const etc.
@ -261,17 +339,17 @@ namespace unnecessary_copy_tests
// copied.
reset();
x.emplace(source<std::pair<count_copies, count_copies> >());
COPY_COUNT(2); MOVE_COUNT_RANGE(0,2);
COPY_COUNT(2); MOVE_COUNT(source_pair_cost);
// TODO: This doesn't work on older versions of gcc.
//count_copies part;
std::pair<count_copies const, count_copies> b;
//reset();
//std::pair<count_copies const&, count_copies const&> a_ref(part, part);
//x.emplace(a_ref);
//COPY_COUNT(0); MOVE_COUNT(0);
#if !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)
count_copies part;
reset();
std::pair<count_copies const&, count_copies const&> a_ref(part, part);
x.emplace(a_ref);
COPY_COUNT(2); MOVE_COUNT(0);
#endif
#if !defined(BOOST_NO_RVALUE_REFERENCES)
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
// No move should take place.
// (since a is already in the container)
reset();
@ -279,23 +357,24 @@ namespace unnecessary_copy_tests
COPY_COUNT(0); MOVE_COUNT(0);
#endif
//
// 2 arguments
//
std::pair<count_copies const, count_copies> b;
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);
COPY_COUNT(2); MOVE_COUNT(source_cost * 2);
// source<count_copies> creates a single copy.
reset();
x.emplace(b.first, source<count_copies>());
COPY_COUNT(1); MOVE_COUNT(0);
COPY_COUNT(1); MOVE_COUNT(source_cost);
reset();
x.emplace(count_copies(b.first.tag_), count_copies(b.second.tag_));