forked from boostorg/unordered
Merge branch 'develop' into feature/mixing-policy
This commit is contained in:
@ -98,9 +98,8 @@ namespace boost {
|
|||||||
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
|
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
|
||||||
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
|
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
|
||||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
|
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||||
boost::is_nothrow_move_assignable_v<Pred>);
|
|
||||||
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||||
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
|
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||||
|
|
||||||
@ -154,9 +153,8 @@ namespace boost {
|
|||||||
template<class K> size_type xref:#unordered_flat_map_transparent_erase_by_key[erase](K&& k);
|
template<class K> size_type xref:#unordered_flat_map_transparent_erase_by_key[erase](K&& k);
|
||||||
iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last);
|
iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last);
|
||||||
void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other)
|
void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_swappable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||||
boost::is_nothrow_swappable_v<Pred>);
|
|
||||||
void xref:#unordered_flat_map_clear[clear]() noexcept;
|
void xref:#unordered_flat_map_clear[clear]() noexcept;
|
||||||
|
|
||||||
template<class H2, class P2>
|
template<class H2, class P2>
|
||||||
@ -606,11 +604,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
|||||||
==== Move Assignment
|
==== Move Assignment
|
||||||
```c++
|
```c++
|
||||||
unordered_flat_map& operator=(unordered_flat_map&& other)
|
unordered_flat_map& operator=(unordered_flat_map&& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||||
boost::is_nothrow_move_assignable_v<Pred>);
|
|
||||||
```
|
```
|
||||||
The move assignment operator. Destroys previously existing elements, move-assigns the hash function and predicate from `other`,
|
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||||
If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container;
|
If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container;
|
||||||
otherwise, inserts move-constructed copies of the elements of `other`.
|
otherwise, inserts move-constructed copies of the elements of `other`.
|
||||||
@ -1043,9 +1040,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa
|
|||||||
==== swap
|
==== swap
|
||||||
```c++
|
```c++
|
||||||
void swap(unordered_flat_map& other)
|
void swap(unordered_flat_map& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_swappable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||||
boost::is_nothrow_swappable_v<Pred>);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Swaps the contents of the container with the parameter.
|
Swaps the contents of the container with the parameter.
|
||||||
|
@ -93,9 +93,8 @@ namespace boost {
|
|||||||
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
|
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
|
||||||
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
|
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
|
||||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
|
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||||
boost::is_nothrow_move_assignable_v<Pred>);
|
|
||||||
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||||
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
|
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
|
||||||
|
|
||||||
@ -128,9 +127,8 @@ namespace boost {
|
|||||||
template<class K> size_type xref:#unordered_flat_set_transparent_erase_by_key[erase](K&& k);
|
template<class K> size_type xref:#unordered_flat_set_transparent_erase_by_key[erase](K&& k);
|
||||||
iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last);
|
iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last);
|
||||||
void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other)
|
void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_swappable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||||
boost::is_nothrow_swappable_v<Pred>);
|
|
||||||
void xref:#unordered_flat_set_clear[clear]() noexcept;
|
void xref:#unordered_flat_set_clear[clear]() noexcept;
|
||||||
|
|
||||||
template<class H2, class P2>
|
template<class H2, class P2>
|
||||||
@ -565,11 +563,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
|||||||
==== Move Assignment
|
==== Move Assignment
|
||||||
```c++
|
```c++
|
||||||
unordered_flat_set& operator=(unordered_flat_set&& other)
|
unordered_flat_set& operator=(unordered_flat_set&& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||||
boost::is_nothrow_move_assignable_v<Pred>);
|
|
||||||
```
|
```
|
||||||
The move assignment operator. Destroys previously existing elements, move-assigns the hash function and predicate from `other`,
|
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||||
If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container;
|
If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container;
|
||||||
otherwise, inserts move-constructed copies of the elements of `other`.
|
otherwise, inserts move-constructed copies of the elements of `other`.
|
||||||
@ -863,9 +860,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa
|
|||||||
==== swap
|
==== swap
|
||||||
```c++
|
```c++
|
||||||
void swap(unordered_flat_set& other)
|
void swap(unordered_flat_set& other)
|
||||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||||
boost::is_nothrow_swappable_v<Hash> &&
|
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||||
boost::is_nothrow_swappable_v<Pred>);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Swaps the contents of the container with the parameter.
|
Swaps the contents of the container with the parameter.
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -69,6 +70,12 @@
|
|||||||
}while(0)
|
}while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \
|
||||||
|
static_assert(boost::is_nothrow_swappable<Hash>::value, \
|
||||||
|
"Template parameter Hash is required to be nothrow Swappable."); \
|
||||||
|
static_assert(boost::is_nothrow_swappable<Pred>::value, \
|
||||||
|
"Template parameter Pred is required to be nothrow Swappable");
|
||||||
|
|
||||||
namespace boost{
|
namespace boost{
|
||||||
namespace unordered{
|
namespace unordered{
|
||||||
namespace detail{
|
namespace detail{
|
||||||
@ -1062,6 +1069,47 @@ inline void prefetch(const void* p)
|
|||||||
|
|
||||||
struct try_emplace_args_t{};
|
struct try_emplace_args_t{};
|
||||||
|
|
||||||
|
template<typename Allocator>
|
||||||
|
struct is_std_allocator:std::false_type{};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_std_allocator<std::allocator<T>>:std::true_type{};
|
||||||
|
|
||||||
|
/* std::allocator::construct marked as deprecated */
|
||||||
|
#if defined(_LIBCPP_SUPPRESS_DEPRECATED_PUSH)
|
||||||
|
_LIBCPP_SUPPRESS_DEPRECATED_PUSH
|
||||||
|
#elif defined(_STL_DISABLE_DEPRECATED_WARNING)
|
||||||
|
_STL_DISABLE_DEPRECATED_WARNING
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename Allocator,typename Ptr,typename... Args>
|
||||||
|
struct alloc_has_construct
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template<typename Allocator2>
|
||||||
|
static decltype(
|
||||||
|
std::declval<Allocator2&>().construct(
|
||||||
|
std::declval<Ptr>(),std::declval<Args&&>()...),
|
||||||
|
std::true_type{}
|
||||||
|
) check(int);
|
||||||
|
|
||||||
|
template<typename> static std::false_type check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value=decltype(check<Allocator>(0))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_LIBCPP_SUPPRESS_DEPRECATED_POP)
|
||||||
|
_LIBCPP_SUPPRESS_DEPRECATED_POP
|
||||||
|
#elif defined(_STL_RESTORE_DEPRECATED_WARNING)
|
||||||
|
_STL_RESTORE_DEPRECATED_WARNING
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(BOOST_GCC)
|
#if defined(BOOST_GCC)
|
||||||
/* GCC's -Wshadow triggers at scenarios like this:
|
/* GCC's -Wshadow triggers at scenarios like this:
|
||||||
*
|
*
|
||||||
@ -1255,13 +1303,28 @@ public:
|
|||||||
|
|
||||||
table& operator=(const table& x)
|
table& operator=(const table& x)
|
||||||
{
|
{
|
||||||
|
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||||
|
|
||||||
static constexpr auto pocca=
|
static constexpr auto pocca=
|
||||||
alloc_traits::propagate_on_container_copy_assignment::value;
|
alloc_traits::propagate_on_container_copy_assignment::value;
|
||||||
|
|
||||||
if(this!=std::addressof(x)){
|
if(this!=std::addressof(x)){
|
||||||
clear();
|
// if copy construction here winds up throwing, the container is still
|
||||||
h()=x.h();
|
// left intact so we perform these operations first
|
||||||
pred()=x.pred();
|
hasher tmp_h=x.h();
|
||||||
|
key_equal tmp_p=x.pred();
|
||||||
|
|
||||||
|
// already noexcept, clear() before we swap the Hash, Pred just in case
|
||||||
|
// the clear() impl relies on them at some point in the future
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// because we've asserted at compile-time that Hash and Pred are nothrow
|
||||||
|
// swappable, we can safely mutate our source container and maintain
|
||||||
|
// consistency between the Hash, Pred compatibility
|
||||||
|
using std::swap;
|
||||||
|
swap(h(),tmp_h);
|
||||||
|
swap(pred(),tmp_p);
|
||||||
|
|
||||||
if_constexpr<pocca>([&,this]{
|
if_constexpr<pocca>([&,this]{
|
||||||
if(al()!=x.al())reserve(0);
|
if(al()!=x.al())reserve(0);
|
||||||
copy_assign_if<pocca>(al(),x.al());
|
copy_assign_if<pocca>(al(),x.al());
|
||||||
@ -1280,19 +1343,32 @@ public:
|
|||||||
|
|
||||||
table& operator=(table&& x)
|
table& operator=(table&& x)
|
||||||
noexcept(
|
noexcept(
|
||||||
alloc_traits::is_always_equal::value&&
|
alloc_traits::propagate_on_container_move_assignment::value||
|
||||||
std::is_nothrow_move_assignable<Hash>::value&&
|
alloc_traits::is_always_equal::value)
|
||||||
std::is_nothrow_move_assignable<Pred>::value)
|
|
||||||
{
|
{
|
||||||
|
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||||
|
|
||||||
static constexpr auto pocma=
|
static constexpr auto pocma=
|
||||||
alloc_traits::propagate_on_container_move_assignment::value;
|
alloc_traits::propagate_on_container_move_assignment::value;
|
||||||
|
|
||||||
if(this!=std::addressof(x)){
|
if(this!=std::addressof(x)){
|
||||||
|
/* Given ambiguity in implementation strategies briefly discussed here:
|
||||||
|
* https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2227
|
||||||
|
*
|
||||||
|
* we opt into requiring nothrow swappability and eschew the move
|
||||||
|
* operations associated with Hash, Pred.
|
||||||
|
*
|
||||||
|
* To this end, we ensure that the user never has to consider the
|
||||||
|
* moved-from state of their Hash, Pred objects
|
||||||
|
*/
|
||||||
|
|
||||||
|
using std::swap;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
h()=std::move(x.h());
|
swap(h(),x.h());
|
||||||
pred()=std::move(x.pred());
|
swap(pred(),x.pred());
|
||||||
|
|
||||||
if(pocma||al()==x.al()){
|
if(pocma||al()==x.al()){
|
||||||
using std::swap;
|
|
||||||
reserve(0);
|
reserve(0);
|
||||||
move_assign_if<pocma>(al(),x.al());
|
move_assign_if<pocma>(al(),x.al());
|
||||||
swap(size_,x.size_);
|
swap(size_,x.size_);
|
||||||
@ -1407,16 +1483,15 @@ public:
|
|||||||
|
|
||||||
void swap(table& x)
|
void swap(table& x)
|
||||||
noexcept(
|
noexcept(
|
||||||
alloc_traits::is_always_equal::value&&
|
alloc_traits::propagate_on_container_swap::value||
|
||||||
boost::is_nothrow_swappable<Hash>::value&&
|
alloc_traits::is_always_equal::value)
|
||||||
boost::is_nothrow_swappable<Pred>::value)
|
|
||||||
{
|
{
|
||||||
|
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||||
|
|
||||||
static constexpr auto pocs=
|
static constexpr auto pocs=
|
||||||
alloc_traits::propagate_on_container_swap::value;
|
alloc_traits::propagate_on_container_swap::value;
|
||||||
|
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(h(),x.h());
|
|
||||||
swap(pred(),x.pred());
|
|
||||||
if_constexpr<pocs>([&,this]{
|
if_constexpr<pocs>([&,this]{
|
||||||
swap_if<pocs>(al(),x.al());
|
swap_if<pocs>(al(),x.al());
|
||||||
},
|
},
|
||||||
@ -1424,6 +1499,9 @@ public:
|
|||||||
BOOST_ASSERT(al()==x.al());
|
BOOST_ASSERT(al()==x.al());
|
||||||
(void)this; /* makes sure captured this is used */
|
(void)this; /* makes sure captured this is used */
|
||||||
});
|
});
|
||||||
|
|
||||||
|
swap(h(),x.h());
|
||||||
|
swap(pred(),x.pred());
|
||||||
swap(size_,x.size_);
|
swap(size_,x.size_);
|
||||||
swap(arrays,x.arrays);
|
swap(arrays,x.arrays);
|
||||||
swap(ml,x.ml);
|
swap(ml,x.ml);
|
||||||
@ -1627,6 +1705,9 @@ private:
|
|||||||
#else
|
#else
|
||||||
std::is_trivially_copy_constructible<value_type>::value
|
std::is_trivially_copy_constructible<value_type>::value
|
||||||
#endif
|
#endif
|
||||||
|
&&(
|
||||||
|
is_std_allocator<Allocator>::value||
|
||||||
|
!alloc_has_construct<Allocator,value_type*,const value_type&>::value)
|
||||||
>{}
|
>{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -2067,6 +2148,7 @@ private:
|
|||||||
|
|
||||||
#undef BOOST_UNORDERED_ASSUME
|
#undef BOOST_UNORDERED_ASSUME
|
||||||
#undef BOOST_UNORDERED_HAS_BUILTIN
|
#undef BOOST_UNORDERED_HAS_BUILTIN
|
||||||
|
#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
|
||||||
#ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
#ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||||
#undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
#undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,6 +60,9 @@ template <class T> struct assign_base : public test::exception_base
|
|||||||
test::random_values<T> x_values, y_values;
|
test::random_values<T> x_values, y_values;
|
||||||
T x, y;
|
T x, y;
|
||||||
|
|
||||||
|
int t1;
|
||||||
|
int t2;
|
||||||
|
|
||||||
typedef typename T::hasher hasher;
|
typedef typename T::hasher hasher;
|
||||||
typedef typename T::key_equal key_equal;
|
typedef typename T::key_equal key_equal;
|
||||||
typedef typename T::allocator_type allocator_type;
|
typedef typename T::allocator_type allocator_type;
|
||||||
@ -67,7 +70,10 @@ template <class T> struct assign_base : public test::exception_base
|
|||||||
assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
||||||
: x_values(), y_values(),
|
: x_values(), y_values(),
|
||||||
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
||||||
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2))
|
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)),
|
||||||
|
t1(tag1),
|
||||||
|
t2(tag2)
|
||||||
|
|
||||||
{
|
{
|
||||||
x.max_load_factor(mlf1);
|
x.max_load_factor(mlf1);
|
||||||
y.max_load_factor(mlf2);
|
y.max_load_factor(mlf2);
|
||||||
@ -89,6 +95,22 @@ template <class T> struct assign_base : public test::exception_base
|
|||||||
{
|
{
|
||||||
test::check_equivalent_keys(x1);
|
test::check_equivalent_keys(x1);
|
||||||
|
|
||||||
|
if (x1.hash_function() == hasher(t1)) {
|
||||||
|
BOOST_TEST(x1.key_eq() == key_equal(t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.hash_function() == hasher(t2)) {
|
||||||
|
BOOST_TEST(x1.key_eq() == key_equal(t2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.key_eq() == key_equal(t1)) {
|
||||||
|
BOOST_TEST(x1.hash_function() == hasher(t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.key_eq() == key_equal(t2)) {
|
||||||
|
BOOST_TEST(x1.hash_function() == hasher(t2));
|
||||||
|
}
|
||||||
|
|
||||||
// If the container is empty at the point of the exception, the
|
// If the container is empty at the point of the exception, the
|
||||||
// internal structure is hidden, this exposes it, at the cost of
|
// internal structure is hidden, this exposes it, at the cost of
|
||||||
// messing up the data.
|
// messing up the data.
|
||||||
|
@ -20,6 +20,7 @@ template <class T> struct move_assign_base : public test::exception_base
|
|||||||
{
|
{
|
||||||
test::random_values<T> x_values, y_values;
|
test::random_values<T> x_values, y_values;
|
||||||
T x, y;
|
T x, y;
|
||||||
|
int t1, t2;
|
||||||
|
|
||||||
typedef typename T::hasher hasher;
|
typedef typename T::hasher hasher;
|
||||||
typedef typename T::key_equal key_equal;
|
typedef typename T::key_equal key_equal;
|
||||||
@ -28,7 +29,9 @@ template <class T> struct move_assign_base : public test::exception_base
|
|||||||
move_assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
move_assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
||||||
: x_values(), y_values(),
|
: x_values(), y_values(),
|
||||||
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
||||||
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2))
|
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)),
|
||||||
|
t1(tag1),
|
||||||
|
t2(tag2)
|
||||||
{
|
{
|
||||||
x.max_load_factor(mlf1);
|
x.max_load_factor(mlf1);
|
||||||
y.max_load_factor(mlf2);
|
y.max_load_factor(mlf2);
|
||||||
@ -52,6 +55,22 @@ template <class T> struct move_assign_base : public test::exception_base
|
|||||||
{
|
{
|
||||||
test::check_equivalent_keys(x1);
|
test::check_equivalent_keys(x1);
|
||||||
|
|
||||||
|
if (x1.hash_function() == hasher(t1)) {
|
||||||
|
BOOST_TEST(x1.key_eq() == key_equal(t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.hash_function() == hasher(t2)) {
|
||||||
|
BOOST_TEST(x1.key_eq() == key_equal(t2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.key_eq() == key_equal(t1)) {
|
||||||
|
BOOST_TEST(x1.hash_function() == hasher(t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1.key_eq() == key_equal(t2)) {
|
||||||
|
BOOST_TEST(x1.hash_function() == hasher(t2));
|
||||||
|
}
|
||||||
|
|
||||||
// If the container is empty at the point of the exception, the
|
// If the container is empty at the point of the exception, the
|
||||||
// internal structure is hidden, this exposes it, at the cost of
|
// internal structure is hidden, this exposes it, at the cost of
|
||||||
// messing up the data.
|
// messing up the data.
|
||||||
|
@ -4,15 +4,40 @@
|
|||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
#include "./containers.hpp"
|
#define BOOST_ENABLE_ASSERT_HANDLER
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
#include "./containers.hpp"
|
||||||
#define BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../helpers/invariants.hpp"
|
#include "../helpers/invariants.hpp"
|
||||||
#include "../helpers/random_values.hpp"
|
#include "../helpers/random_values.hpp"
|
||||||
#include "../helpers/tracker.hpp"
|
#include "../helpers/tracker.hpp"
|
||||||
|
#include "../objects/test.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
void assertion_failed(
|
||||||
|
char const* expr, char const* function, char const* file, long line)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << expr << "\nin " << function << " failed at : " << file << ", line "
|
||||||
|
<< line;
|
||||||
|
|
||||||
|
throw std::runtime_error(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertion_failed_msg(char const* expr, char const* msg,
|
||||||
|
char const* function, char const* file, long line)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << expr << "\nin " << function << " failed at : " << file << ", line "
|
||||||
|
<< line << "\n"
|
||||||
|
<< msg;
|
||||||
|
|
||||||
|
throw std::runtime_error(ss.str());
|
||||||
|
}
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
#if defined(BOOST_MSVC)
|
#if defined(BOOST_MSVC)
|
||||||
#pragma warning(disable : 4512) // assignment operator could not be generated
|
#pragma warning(disable : 4512) // assignment operator could not be generated
|
||||||
@ -39,15 +64,10 @@ template <class T> struct self_swap_base : public test::exception_base
|
|||||||
|
|
||||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
||||||
{
|
{
|
||||||
std::string scope(test::scope);
|
(void)x;
|
||||||
|
|
||||||
// TODO: In C++11 exceptions are only allowed in the swap function.
|
BOOST_ERROR("An exception leaked when it should not have. Allocator "
|
||||||
BOOST_TEST(scope == "hash::hash(hash)" ||
|
"equality assertion must precede all other ops");
|
||||||
scope == "hash::operator=(hash)" ||
|
|
||||||
scope == "equal_to::equal_to(equal_to)" ||
|
|
||||||
scope == "equal_to::operator=(equal_to)");
|
|
||||||
|
|
||||||
test::check_equivalent_keys(x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -140,11 +160,133 @@ template <class T> struct swap_test4 : swap_base<T>
|
|||||||
swap_test4() : swap_base<T>(10, 10, 1, 2) {}
|
swap_test4() : swap_base<T>(10, 10, 1, 2) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T> struct unequal_alloc_swap_base : public test::exception_base
|
||||||
|
{
|
||||||
|
const test::random_values<T> x_values, y_values;
|
||||||
|
const T initial_x, initial_y;
|
||||||
|
|
||||||
|
typedef typename T::hasher hasher;
|
||||||
|
typedef typename T::key_equal key_equal;
|
||||||
|
typedef typename T::allocator_type allocator_type;
|
||||||
|
|
||||||
|
unequal_alloc_swap_base(unsigned int count1, unsigned int count2)
|
||||||
|
: x_values(count1, test::limited_range),
|
||||||
|
y_values(count2, test::limited_range),
|
||||||
|
initial_x(x_values.begin(), x_values.end(), 0, allocator_type(1337)),
|
||||||
|
initial_y(y_values.begin(), y_values.end(), 0, allocator_type(7331))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct data_type
|
||||||
|
{
|
||||||
|
data_type(T const& x_, T const& y_) : x(x_), y(y_) {}
|
||||||
|
|
||||||
|
T x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
data_type init() const { return data_type(initial_x, initial_y); }
|
||||||
|
|
||||||
|
void run(data_type& d) const
|
||||||
|
{
|
||||||
|
bool assert_threw = false;
|
||||||
|
|
||||||
|
BOOST_TEST(d.x.get_allocator() != d.y.get_allocator());
|
||||||
|
|
||||||
|
try {
|
||||||
|
d.x.swap(d.y);
|
||||||
|
} catch (std::runtime_error&) {
|
||||||
|
assert_threw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DISABLE_EXCEPTIONS;
|
||||||
|
BOOST_TEST(assert_threw);
|
||||||
|
test::check_container(d.x, this->x_values);
|
||||||
|
test::check_equivalent_keys(d.x);
|
||||||
|
test::check_container(d.y, this->y_values);
|
||||||
|
test::check_equivalent_keys(d.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const
|
||||||
|
{
|
||||||
|
std::string scope(test::scope);
|
||||||
|
|
||||||
|
// TODO: In C++11 exceptions are only allowed in the swap function.
|
||||||
|
BOOST_TEST(scope == "hash::hash(hash)" ||
|
||||||
|
scope == "hash::operator=(hash)" ||
|
||||||
|
scope == "equal_to::equal_to(equal_to)" ||
|
||||||
|
scope == "equal_to::operator=(equal_to)");
|
||||||
|
|
||||||
|
test::check_equivalent_keys(d.x);
|
||||||
|
test::check_equivalent_keys(d.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> struct unequal_alloc_swap_test1 : unequal_alloc_swap_base<T>
|
||||||
|
{
|
||||||
|
unequal_alloc_swap_test1() : unequal_alloc_swap_base<T>(0, 0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> struct unequal_alloc_swap_test2 : unequal_alloc_swap_base<T>
|
||||||
|
{
|
||||||
|
unequal_alloc_swap_test2() : unequal_alloc_swap_base<T>(0, 10) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> struct unequal_alloc_swap_test3 : unequal_alloc_swap_base<T>
|
||||||
|
{
|
||||||
|
unequal_alloc_swap_test3() : unequal_alloc_swap_base<T>(10, 0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> struct unequal_alloc_swap_test4 : unequal_alloc_swap_base<T>
|
||||||
|
{
|
||||||
|
unequal_alloc_swap_test4() : unequal_alloc_swap_base<T>(10, 10) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
|
||||||
|
using unordered_flat_set = boost::unordered_flat_set<int, boost::hash<int>,
|
||||||
|
std::equal_to<int>, test::allocator1<int> >;
|
||||||
|
using unordered_flat_map = boost::unordered_flat_map<int, int, boost::hash<int>,
|
||||||
|
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
|
||||||
|
|
||||||
|
#define SWAP_CONTAINER_SEQ (unordered_flat_set)(unordered_flat_map)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||||
|
test::allocator1<int> >
|
||||||
|
unordered_set;
|
||||||
|
typedef boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||||
|
test::allocator1<std::pair<int const, int> > >
|
||||||
|
unordered_map;
|
||||||
|
typedef boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
|
||||||
|
test::allocator1<int> >
|
||||||
|
unordered_multiset;
|
||||||
|
typedef boost::unordered_multimap<int, int, boost::hash<int>,
|
||||||
|
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >
|
||||||
|
unordered_multimap;
|
||||||
|
|
||||||
|
#define SWAP_CONTAINER_SEQ \
|
||||||
|
(unordered_set)(unordered_map)(unordered_multiset)(unordered_multimap)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FOA containers deliberately choose to not offer the strong exception
|
||||||
|
// guarantee so we can't reliably test what happens if swapping one of the data
|
||||||
|
// members throws
|
||||||
|
//
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
#if !defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
EXCEPTION_TESTS(
|
EXCEPTION_TESTS(
|
||||||
(self_swap_test1)(self_swap_test2)
|
(self_swap_test1)(self_swap_test2)
|
||||||
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
|
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
|
||||||
CONTAINER_SEQ)
|
CONTAINER_SEQ)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// want to prove that when assertions are defined as throwing operations that we
|
||||||
|
// uphold invariants
|
||||||
|
EXCEPTION_TESTS(
|
||||||
|
(unequal_alloc_swap_test1)(unequal_alloc_swap_test2)
|
||||||
|
(unequal_alloc_swap_test3)(unequal_alloc_swap_test4),
|
||||||
|
SWAP_CONTAINER_SEQ)
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
RUN_TESTS()
|
RUN_TESTS()
|
||||||
|
@ -54,20 +54,10 @@ namespace test {
|
|||||||
if (test::has_unique_keys<X>::value && count != 1)
|
if (test::has_unique_keys<X>::value && count != 1)
|
||||||
BOOST_ERROR("Non-unique key.");
|
BOOST_ERROR("Non-unique key.");
|
||||||
|
|
||||||
#if !defined(BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS)
|
|
||||||
// we conditionally compile this check because our FOA implementation only
|
|
||||||
// exhibits the weak guarantee when swapping throws
|
|
||||||
//
|
|
||||||
// in this case, the hasher may be changed before the predicate and the
|
|
||||||
// arrays are swapped in which case, we can can find an element by
|
|
||||||
// iteration but unfortunately, it's in the wrong slot according to the
|
|
||||||
// new hash function so count(key) can wind up returning nothing when
|
|
||||||
// there really is something
|
|
||||||
if (x1.count(key) != count) {
|
if (x1.count(key) != count) {
|
||||||
BOOST_ERROR("Incorrect output of count.");
|
BOOST_ERROR("Incorrect output of count.");
|
||||||
std::cerr << x1.count(key) << "," << count << "\n";
|
std::cerr << x1.count(key) << "," << count << "\n";
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||||
// Check that the keys are in the correct bucket and are
|
// Check that the keys are in the correct bucket and are
|
||||||
|
@ -227,8 +227,21 @@ namespace test {
|
|||||||
}
|
}
|
||||||
return x1.tag_ != x2.tag_;
|
return x1.tag_ != x2.tag_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
friend void swap(hash&, hash&) noexcept;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
void swap(hash& lhs, hash& rhs) noexcept
|
||||||
|
{
|
||||||
|
int tag = lhs.tag_;
|
||||||
|
lhs.tag_ = rhs.tag_;
|
||||||
|
rhs.tag_ = tag;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
class less
|
class less
|
||||||
{
|
{
|
||||||
int tag_;
|
int tag_;
|
||||||
@ -364,8 +377,20 @@ namespace test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
friend less create_compare(equal_to x) { return less(x.tag_); }
|
friend less create_compare(equal_to x) { return less(x.tag_); }
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
friend void swap(equal_to&, equal_to&) noexcept;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
void swap(equal_to& lhs, equal_to& rhs) noexcept
|
||||||
|
{
|
||||||
|
int tag = lhs.tag_;
|
||||||
|
lhs.tag_ = rhs.tag_;
|
||||||
|
rhs.tag_ = tag;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <class T> class allocator
|
template <class T> class allocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -206,6 +206,11 @@ namespace test {
|
|||||||
hash& operator=(hash const&) { return *this; }
|
hash& operator=(hash const&) { return *this; }
|
||||||
~hash() {}
|
~hash() {}
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
hash(hash&&) = default;
|
||||||
|
hash& operator=(hash&&) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::size_t operator()(T const&) const { return 0; }
|
std::size_t operator()(T const&) const { return 0; }
|
||||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||||
ampersand_operator_used operator&() const
|
ampersand_operator_used operator&() const
|
||||||
@ -224,6 +229,11 @@ namespace test {
|
|||||||
equal_to& operator=(equal_to const&) { return *this; }
|
equal_to& operator=(equal_to const&) { return *this; }
|
||||||
~equal_to() {}
|
~equal_to() {}
|
||||||
|
|
||||||
|
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||||
|
equal_to(equal_to&&) = default;
|
||||||
|
equal_to& operator=(equal_to&&) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool operator()(T const&, T const&) const { return true; }
|
bool operator()(T const&, T const&) const { return true; }
|
||||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||||
ampersand_operator_used operator&() const
|
ampersand_operator_used operator&() const
|
||||||
|
@ -230,18 +230,18 @@ namespace test {
|
|||||||
|
|
||||||
std::size_t operator()(int x) const
|
std::size_t operator()(int x) const
|
||||||
{
|
{
|
||||||
int result;
|
unsigned result;
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case 1:
|
case 1:
|
||||||
result = x;
|
result = static_cast<unsigned>(x);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
result = x * 7;
|
result = static_cast<unsigned>(x) * 7;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result = x * 256;
|
result = static_cast<unsigned>(x) * 256;
|
||||||
}
|
}
|
||||||
return static_cast<std::size_t>(result);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(hash const& x1, hash const& x2)
|
friend bool operator==(hash const& x1, hash const& x2)
|
||||||
|
@ -225,11 +225,227 @@ namespace copy_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void copy_construct_tests_std_allocator1(
|
||||||
|
T*, test::random_generator const& generator)
|
||||||
|
{
|
||||||
|
typename T::hasher hf;
|
||||||
|
typename T::key_equal eq;
|
||||||
|
typename T::allocator_type al;
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x;
|
||||||
|
T y(x);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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::detail::tracker.count_allocations == 0);
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x(0);
|
||||||
|
T y(x);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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::detail::tracker.count_allocations == 0);
|
||||||
|
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));
|
||||||
|
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
|
||||||
|
// enough buckets to decrease the load factor appropriately.
|
||||||
|
test::random_values<T> v(1000, generator);
|
||||||
|
T x(v.begin(), v.end());
|
||||||
|
x.max_load_factor(x.load_factor() / 4);
|
||||||
|
T y(x);
|
||||||
|
test::unordered_equivalence_tester<T> equivalent(x);
|
||||||
|
BOOST_TEST(equivalent(y));
|
||||||
|
// This isn't guaranteed:
|
||||||
|
BOOST_TEST(y.load_factor() < y.max_load_factor());
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void copy_construct_tests_std_allocator2(
|
||||||
|
T*, test::random_generator const& generator)
|
||||||
|
{
|
||||||
|
typename T::hasher hf(1);
|
||||||
|
typename T::key_equal eq(1);
|
||||||
|
typename T::allocator_type al;
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x(0, hf, eq, al);
|
||||||
|
T y(x);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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::detail::tracker.count_allocations == 0);
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x(10000, hf, eq, al);
|
||||||
|
T y(x);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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());
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x(0, hf, eq, al);
|
||||||
|
T y(x, al);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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()) == 0);
|
||||||
|
BOOST_TEST(test::detail::tracker.count_allocations == 0);
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
T x(1000, hf, eq, al);
|
||||||
|
T y(x, al);
|
||||||
|
BOOST_TEST(y.empty());
|
||||||
|
BOOST_TEST(test::equivalent(y.hash_function(), hf));
|
||||||
|
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()) == 0);
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
test::random_values<T> v;
|
||||||
|
|
||||||
|
T x(v.begin(), v.end(), 0, hf, eq, al);
|
||||||
|
T y(x);
|
||||||
|
test::unordered_equivalence_tester<T> equivalent(x);
|
||||||
|
BOOST_TEST(equivalent(y));
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
BOOST_TEST(test::equivalent(y.get_allocator(), al));
|
||||||
|
BOOST_TEST(test::detail::tracker.count_allocations == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
test::random_values<T> v(1000, generator);
|
||||||
|
|
||||||
|
T x(v.begin(), v.end(), 0, hf, eq, al);
|
||||||
|
T y(x);
|
||||||
|
test::unordered_equivalence_tester<T> equivalent(x);
|
||||||
|
BOOST_TEST(equivalent(y));
|
||||||
|
test::check_equivalent_keys(y);
|
||||||
|
BOOST_TEST(test::equivalent(y.get_allocator(), al));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
test::random_values<T> v;
|
||||||
|
|
||||||
|
T x(v.begin(), v.end(), 0, hf, eq, al);
|
||||||
|
T y(x, al);
|
||||||
|
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(), al));
|
||||||
|
BOOST_TEST(test::detail::tracker.count_allocations == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
test::check_instances check_;
|
||||||
|
|
||||||
|
test::random_values<T> v(500, generator);
|
||||||
|
|
||||||
|
T x(v.begin(), v.end(), 0, hf, eq, al);
|
||||||
|
T y(x, al);
|
||||||
|
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(), al));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using test::default_generator;
|
using test::default_generator;
|
||||||
using test::generate_collisions;
|
using test::generate_collisions;
|
||||||
using test::limited_range;
|
using test::limited_range;
|
||||||
|
|
||||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||||
|
template <class T> struct allocator
|
||||||
|
{
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
allocator() = default;
|
||||||
|
allocator(allocator const&) = default;
|
||||||
|
allocator(allocator&&) = default;
|
||||||
|
|
||||||
|
template <class U> allocator(allocator<U> const&) {}
|
||||||
|
|
||||||
|
T* allocate(std::size_t n)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(::operator new(sizeof(value_type) * n));
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||||
|
|
||||||
|
friend inline bool operator==(allocator const&, allocator const&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator!=(allocator const&, allocator const&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||||
test::allocator1<test::object> >* test_set;
|
test::allocator1<test::object> >* test_set;
|
||||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||||
@ -249,13 +465,50 @@ namespace copy_tests {
|
|||||||
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
|
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
|
||||||
test_map_no_select_copy;
|
test_map_no_select_copy;
|
||||||
|
|
||||||
|
boost::unordered_flat_set<int, test::hash, test::equal_to,
|
||||||
|
test::allocator1<int> >* test_set_trivially_copyable;
|
||||||
|
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
|
||||||
|
test::allocator1<std::pair<int const, int> > >* test_map_trivially_copyable;
|
||||||
|
|
||||||
|
boost::unordered_flat_set<int, test::hash, test::equal_to,
|
||||||
|
std::allocator<int> >* test_set_trivially_copyable_std_allocator;
|
||||||
|
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
|
||||||
|
std::allocator<std::pair<int const, int> > >*
|
||||||
|
test_map_trivially_copyable_std_allocator;
|
||||||
|
|
||||||
|
boost::unordered_flat_set<int, test::hash, test::equal_to, allocator<int> >*
|
||||||
|
test_set_trivially_copyable_no_construct;
|
||||||
|
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
|
||||||
|
allocator<std::pair<int const, int> > >*
|
||||||
|
test_map_trivially_copyable_no_construct;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
UNORDERED_TEST(copy_construct_tests1,
|
UNORDERED_TEST(copy_construct_tests1,
|
||||||
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))(
|
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
|
||||||
(default_generator)(generate_collisions)(limited_range)))
|
(test_set_no_select_copy)(test_map_no_select_copy)
|
||||||
|
(test_set_trivially_copyable)(test_map_trivially_copyable))
|
||||||
|
((default_generator)(generate_collisions)(limited_range)))
|
||||||
|
|
||||||
UNORDERED_TEST(copy_construct_tests2,
|
UNORDERED_TEST(copy_construct_tests2,
|
||||||
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))(
|
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
|
||||||
(default_generator)(generate_collisions)(limited_range)))
|
(test_set_no_select_copy)(test_map_no_select_copy)
|
||||||
|
(test_set_trivially_copyable)(test_map_trivially_copyable))
|
||||||
|
((default_generator)(generate_collisions)(limited_range)))
|
||||||
|
|
||||||
|
UNORDERED_TEST(copy_construct_tests_std_allocator1,
|
||||||
|
((test_set_trivially_copyable_std_allocator)
|
||||||
|
(test_map_trivially_copyable_std_allocator)
|
||||||
|
(test_set_trivially_copyable_no_construct)
|
||||||
|
(test_map_trivially_copyable_no_construct))
|
||||||
|
((default_generator)(generate_collisions)(limited_range)))
|
||||||
|
|
||||||
|
UNORDERED_TEST(copy_construct_tests_std_allocator2,
|
||||||
|
((test_set_trivially_copyable_std_allocator)
|
||||||
|
(test_map_trivially_copyable_std_allocator)
|
||||||
|
(test_set_trivially_copyable_no_construct)
|
||||||
|
(test_map_trivially_copyable_no_construct))
|
||||||
|
((default_generator)(generate_collisions)(limited_range)))
|
||||||
|
// clang-format on
|
||||||
#else
|
#else
|
||||||
boost::unordered_set<test::object, test::hash, test::equal_to,
|
boost::unordered_set<test::object, test::hash, test::equal_to,
|
||||||
test::allocator1<test::object> >* test_set;
|
test::allocator1<test::object> >* test_set;
|
||||||
|
@ -437,13 +437,11 @@ UNORDERED_AUTO_TEST (prelim_allocator_checks) {
|
|||||||
using test::default_generator;
|
using test::default_generator;
|
||||||
|
|
||||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_move_assign,
|
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
|
||||||
noexcept_tests::equal_to_nothrow_move_assign, allocator1<int> >*
|
noexcept_tests::equal_to_nothrow_swap, allocator1<int> >* throwing_set_alloc1;
|
||||||
throwing_set_alloc1;
|
|
||||||
|
|
||||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_move_assign,
|
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
|
||||||
noexcept_tests::equal_to_nothrow_move_assign, allocator2<int> >*
|
noexcept_tests::equal_to_nothrow_swap, allocator2<int> >* throwing_set_alloc2;
|
||||||
throwing_set_alloc2;
|
|
||||||
|
|
||||||
UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
|
UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
|
||||||
((throwing_set_alloc1)(throwing_set_alloc2))((default_generator)))
|
((throwing_set_alloc1)(throwing_set_alloc2))((default_generator)))
|
||||||
|
Reference in New Issue
Block a user