mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
Merge pull request #173 from cmazakas/fix/exception-guarantees
Exception Guarantees
This commit is contained in:
@ -98,9 +98,8 @@ namespace boost {
|
||||
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_move_assignment[operator++=++](unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
||||
boost::is_nothrow_move_assignable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
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;
|
||||
|
||||
@ -154,9 +153,8 @@ namespace boost {
|
||||
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);
|
||||
void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_swappable_v<Hash> &&
|
||||
boost::is_nothrow_swappable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||
void xref:#unordered_flat_map_clear[clear]() noexcept;
|
||||
|
||||
template<class H2, class P2>
|
||||
@ -606,11 +604,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_map& operator=(unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
||||
boost::is_nothrow_move_assignable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
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`.
|
||||
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`.
|
||||
@ -1043,9 +1040,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa
|
||||
==== swap
|
||||
```c++
|
||||
void swap(unordered_flat_map& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_swappable_v<Hash> &&
|
||||
boost::is_nothrow_swappable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||
```
|
||||
|
||||
Swaps the contents of the container with the parameter.
|
||||
|
@ -93,9 +93,8 @@ namespace boost {
|
||||
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_move_assignment[operator++=++](unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
||||
boost::is_nothrow_move_assignable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
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;
|
||||
|
||||
@ -128,9 +127,8 @@ namespace boost {
|
||||
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);
|
||||
void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_swappable_v<Hash> &&
|
||||
boost::is_nothrow_swappable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||
void xref:#unordered_flat_set_clear[clear]() noexcept;
|
||||
|
||||
template<class H2, class P2>
|
||||
@ -565,11 +563,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_set& operator=(unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_move_assignable_v<Hash> &&
|
||||
boost::is_nothrow_move_assignable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
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`.
|
||||
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`.
|
||||
@ -863,9 +860,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa
|
||||
==== swap
|
||||
```c++
|
||||
void swap(unordered_flat_set& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
|
||||
boost::is_nothrow_swappable_v<Hash> &&
|
||||
boost::is_nothrow_swappable_v<Pred>);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
|
||||
```
|
||||
|
||||
Swaps the contents of the container with the parameter.
|
||||
|
@ -69,6 +69,12 @@
|
||||
}while(0)
|
||||
#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 unordered{
|
||||
namespace detail{
|
||||
@ -1260,13 +1266,28 @@ public:
|
||||
|
||||
table& operator=(const table& x)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
|
||||
static constexpr auto pocca=
|
||||
alloc_traits::propagate_on_container_copy_assignment::value;
|
||||
|
||||
if(this!=std::addressof(x)){
|
||||
clear();
|
||||
h()=x.h();
|
||||
pred()=x.pred();
|
||||
// if copy construction here winds up throwing, the container is still
|
||||
// left intact so we perform these operations first
|
||||
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(al()!=x.al())reserve(0);
|
||||
copy_assign_if<pocca>(al(),x.al());
|
||||
@ -1285,19 +1306,32 @@ public:
|
||||
|
||||
table& operator=(table&& x)
|
||||
noexcept(
|
||||
alloc_traits::is_always_equal::value&&
|
||||
std::is_nothrow_move_assignable<Hash>::value&&
|
||||
std::is_nothrow_move_assignable<Pred>::value)
|
||||
alloc_traits::propagate_on_container_move_assignment::value||
|
||||
alloc_traits::is_always_equal::value)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
|
||||
static constexpr auto pocma=
|
||||
alloc_traits::propagate_on_container_move_assignment::value;
|
||||
|
||||
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();
|
||||
h()=std::move(x.h());
|
||||
pred()=std::move(x.pred());
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
|
||||
if(pocma||al()==x.al()){
|
||||
using std::swap;
|
||||
reserve(0);
|
||||
move_assign_if<pocma>(al(),x.al());
|
||||
swap(size_,x.size_);
|
||||
@ -1412,16 +1446,15 @@ public:
|
||||
|
||||
void swap(table& x)
|
||||
noexcept(
|
||||
alloc_traits::is_always_equal::value&&
|
||||
boost::is_nothrow_swappable<Hash>::value&&
|
||||
boost::is_nothrow_swappable<Pred>::value)
|
||||
alloc_traits::propagate_on_container_swap::value||
|
||||
alloc_traits::is_always_equal::value)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
|
||||
static constexpr auto pocs=
|
||||
alloc_traits::propagate_on_container_swap::value;
|
||||
|
||||
using std::swap;
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
if_constexpr<pocs>([&,this]{
|
||||
swap_if<pocs>(al(),x.al());
|
||||
},
|
||||
@ -1429,6 +1462,9 @@ public:
|
||||
BOOST_ASSERT(al()==x.al());
|
||||
(void)this; /* makes sure captured this is used */
|
||||
});
|
||||
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
swap(size_,x.size_);
|
||||
swap(arrays,x.arrays);
|
||||
swap(ml,x.ml);
|
||||
@ -2075,6 +2111,7 @@ private:
|
||||
|
||||
#undef BOOST_UNORDERED_ASSUME
|
||||
#undef BOOST_UNORDERED_HAS_BUILTIN
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
|
||||
#ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#endif
|
||||
|
@ -60,6 +60,9 @@ template <class T> struct assign_base : public test::exception_base
|
||||
test::random_values<T> x_values, y_values;
|
||||
T x, y;
|
||||
|
||||
int t1;
|
||||
int t2;
|
||||
|
||||
typedef typename T::hasher hasher;
|
||||
typedef typename T::key_equal key_equal;
|
||||
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)
|
||||
: x_values(), y_values(),
|
||||
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);
|
||||
y.max_load_factor(mlf2);
|
||||
@ -89,6 +95,22 @@ template <class T> struct assign_base : public test::exception_base
|
||||
{
|
||||
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
|
||||
// internal structure is hidden, this exposes it, at the cost of
|
||||
// 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;
|
||||
T x, y;
|
||||
int t1, t2;
|
||||
|
||||
typedef typename T::hasher hasher;
|
||||
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)
|
||||
: x_values(), y_values(),
|
||||
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);
|
||||
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);
|
||||
|
||||
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
|
||||
// internal structure is hidden, this exposes it, at the cost of
|
||||
// messing up the data.
|
||||
|
@ -4,15 +4,40 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
#define BOOST_ENABLE_ASSERT_HANDLER
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
#define BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS
|
||||
#endif
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.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)
|
||||
#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
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
(void)x;
|
||||
|
||||
// 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(x);
|
||||
BOOST_ERROR("An exception leaked when it should not have. Allocator "
|
||||
"equality assertion must precede all other ops");
|
||||
}
|
||||
};
|
||||
|
||||
@ -140,11 +160,133 @@ template <class T> struct swap_test4 : swap_base<T>
|
||||
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
|
||||
#if !defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
EXCEPTION_TESTS(
|
||||
(self_swap_test1)(self_swap_test2)
|
||||
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
|
||||
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
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -54,20 +54,10 @@ namespace test {
|
||||
if (test::has_unique_keys<X>::value && count != 1)
|
||||
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) {
|
||||
BOOST_ERROR("Incorrect output of count.");
|
||||
std::cerr << x1.count(key) << "," << count << "\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
// Check that the keys are in the correct bucket and are
|
||||
|
@ -227,8 +227,21 @@ namespace test {
|
||||
}
|
||||
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
|
||||
{
|
||||
int tag_;
|
||||
@ -364,8 +377,20 @@ namespace test {
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -206,6 +206,11 @@ namespace test {
|
||||
hash& operator=(hash const&) { return *this; }
|
||||
~hash() {}
|
||||
|
||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
hash(hash&&) = default;
|
||||
hash& operator=(hash&&) = default;
|
||||
#endif
|
||||
|
||||
std::size_t operator()(T const&) const { return 0; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
@ -224,6 +229,11 @@ namespace test {
|
||||
equal_to& operator=(equal_to const&) { return *this; }
|
||||
~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; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
|
@ -437,13 +437,11 @@ UNORDERED_AUTO_TEST (prelim_allocator_checks) {
|
||||
using test::default_generator;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_move_assign,
|
||||
noexcept_tests::equal_to_nothrow_move_assign, allocator1<int> >*
|
||||
throwing_set_alloc1;
|
||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
|
||||
noexcept_tests::equal_to_nothrow_swap, allocator1<int> >* throwing_set_alloc1;
|
||||
|
||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_move_assign,
|
||||
noexcept_tests::equal_to_nothrow_move_assign, allocator2<int> >*
|
||||
throwing_set_alloc2;
|
||||
boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
|
||||
noexcept_tests::equal_to_nothrow_swap, allocator2<int> >* throwing_set_alloc2;
|
||||
|
||||
UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
|
||||
((throwing_set_alloc1)(throwing_set_alloc2))((default_generator)))
|
||||
|
Reference in New Issue
Block a user