diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 0ef15ef1..ccf48227 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1025,6 +1026,47 @@ inline void prefetch(const void* p) struct try_emplace_args_t{}; +template +struct is_std_allocator:std::false_type{}; + +template +struct is_std_allocator>: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 +struct alloc_has_construct +{ +private: + template + static decltype( + std::declval().construct( + std::declval(),std::declval()...), + std::true_type{} + ) check(int); + + template static std::false_type check(...); + +public: + static constexpr bool value=decltype(check(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) /* GCC's -Wshadow triggers at scenarios like this: * @@ -1590,6 +1632,9 @@ private: #else std::is_trivially_copy_constructible::value #endif + &&( + is_std_allocator::value|| + !alloc_has_construct::value) >{} ); } diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 0bcf78e8..b1a253e5 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -230,18 +230,18 @@ namespace test { std::size_t operator()(int x) const { - int result; + unsigned result; switch (type_) { case 1: - result = x; + result = static_cast(x); break; case 2: - result = x * 7; + result = static_cast(x) * 7; break; default: - result = x * 256; + result = static_cast(x) * 256; } - return static_cast(result); + return result; } friend bool operator==(hash const& x1, hash const& x2) diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 05c0f97a..91cf3ff2 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -225,11 +225,227 @@ namespace copy_tests { } } + template + 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 v(1000, generator); + + T x(v.begin(), v.end()); + T y(x); + test::unordered_equivalence_tester 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 v(1000, generator); + T x(v.begin(), v.end()); + x.max_load_factor(x.load_factor() / 4); + T y(x); + test::unordered_equivalence_tester 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 + 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 v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester 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 v(1000, generator); + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester 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 v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x, al); + test::unordered_equivalence_tester 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 v(500, generator); + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x, al); + test::unordered_equivalence_tester 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::generate_collisions; using test::limited_range; #ifdef BOOST_UNORDERED_FOA_TESTS + template struct allocator + { + using value_type = T; + + allocator() = default; + allocator(allocator const&) = default; + allocator(allocator&&) = default; + + template allocator(allocator const&) {} + + T* allocate(std::size_t n) + { + return static_cast(::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_set; boost::unordered_flat_map >* test_map_no_select_copy; + boost::unordered_flat_set >* test_set_trivially_copyable; + boost::unordered_flat_map > >* test_map_trivially_copyable; + + boost::unordered_flat_set >* test_set_trivially_copyable_std_allocator; + boost::unordered_flat_map > >* + test_map_trivially_copyable_std_allocator; + + boost::unordered_flat_set >* + test_set_trivially_copyable_no_construct; + boost::unordered_flat_map > >* + test_map_trivially_copyable_no_construct; + + // clang-format off 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))( - (default_generator)(generate_collisions)(limited_range))) + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy) + (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, - ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))( - (default_generator)(generate_collisions)(limited_range))) + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy) + (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 boost::unordered_set >* test_set;