diff --git a/test/cfoa/assign_tests.cpp b/test/cfoa/assign_tests.cpp index 409e9d8d..a9b0ca08 100644 --- a/test/cfoa/assign_tests.cpp +++ b/test/cfoa/assign_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include #if defined(__clang__) && defined(__has_warning) @@ -31,15 +33,76 @@ using test::sequential; using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator2 >; - -using flat_map_type = boost::unordered::unordered_flat_map; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; + +using fancy_map_type = boost::unordered::concurrent_flat_map > >; + +using fancy_set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; +fancy_map_type* fancy_test_map; +fancy_set_type* fancy_test_set; + +std::initializer_list map_init_list{ + {raii{0}, raii{0}}, + {raii{1}, raii{1}}, + {raii{2}, raii{2}}, + {raii{3}, raii{3}}, + {raii{4}, raii{4}}, + {raii{5}, raii{5}}, + {raii{6}, raii{6}}, + {raii{6}, raii{6}}, + {raii{7}, raii{7}}, + {raii{8}, raii{8}}, + {raii{9}, raii{9}}, + {raii{10}, raii{10}}, + {raii{9}, raii{9}}, + {raii{8}, raii{8}}, + {raii{7}, raii{7}}, + {raii{6}, raii{6}}, + {raii{5}, raii{5}}, + {raii{4}, raii{4}}, + {raii{3}, raii{3}}, + {raii{2}, raii{2}}, + {raii{1}, raii{1}}, + {raii{0}, raii{0}}, +}; + +std::initializer_list set_init_list{ + raii{0}, + raii{1}, + raii{2}, + raii{3}, + raii{4}, + raii{5}, + raii{6}, + raii{6}, + raii{7}, + raii{8}, + raii{9}, + raii{10}, + raii{9}, + raii{8}, + raii{7}, + raii{6}, + raii{5}, + raii{4}, + raii{3}, + raii{2}, + raii{1}, + raii{0}, +}; + +auto test_map_and_init_list=std::make_pair(test_map,map_init_list); +auto test_set_and_init_list=std::make_pair(test_set,set_init_list); template struct pocca_allocator { @@ -116,22 +179,28 @@ template struct pocma_allocator }; namespace { - template void copy_assign(G gen, test::random_generator rg) + template + void copy_assign(X*, GF gen_factory, test::random_generator rg) { + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); // lhs empty, rhs empty { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); - thread_runner(values, [&x](boost::span s) { + thread_runner(values, [&x](boost::span s) { (void)s; - map_type y; + X y; BOOST_TEST(x.empty()); BOOST_TEST(y.empty()); @@ -153,14 +222,14 @@ namespace { { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); - auto const old_size = reference_map.size(); + auto const old_size = reference_cont.size(); - thread_runner(values, [&x, &values](boost::span s) { + thread_runner(values, [&x, &values](boost::span s) { (void)s; - map_type y(values.size()); + X y(values.size()); for (auto const& v : values) { y.insert(v); } @@ -177,11 +246,13 @@ namespace { BOOST_TEST(y.empty()); }); - BOOST_TEST_EQ(raii::destructor, num_threads * (2 * old_size)); + BOOST_TEST_EQ( + raii::destructor, num_threads * (value_type_cardinality * old_size)); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ( - raii::copy_constructor, num_threads * 2 * reference_map.size()); + raii::copy_constructor, + num_threads * value_type_cardinality * reference_cont.size()); } check_raii_counts(); @@ -189,7 +260,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -197,10 +268,10 @@ namespace { auto const old_cc = +raii::copy_constructor; thread_runner( - values, [&x, &reference_map](boost::span s) { + values, [&x, &reference_cont](boost::span s) { (void)s; - map_type y; + X y; BOOST_TEST(!x.empty()); BOOST_TEST(y.empty()); @@ -211,14 +282,16 @@ namespace { BOOST_TEST_EQ(x.key_eq(), y.key_eq()); BOOST_TEST(x.get_allocator() != y.get_allocator()); - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); }); - BOOST_TEST_EQ(raii::destructor, num_threads * 2 * x.size()); + BOOST_TEST_EQ( + raii::destructor, num_threads * value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ( - raii::copy_constructor, old_cc + (num_threads * 2 * x.size())); + raii::copy_constructor, + old_cc + (num_threads * value_type_cardinality * x.size())); } check_raii_counts(); @@ -226,7 +299,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -234,10 +307,10 @@ namespace { auto const old_size = x.size(); auto const old_cc = +raii::copy_constructor; - thread_runner(values, [&x, &values](boost::span s) { + thread_runner(values, [&x, &values](boost::span s) { (void)s; - map_type y(values.size()); + X y(values.size()); for (auto const& v : values) { y.insert(v); } @@ -252,11 +325,13 @@ namespace { BOOST_TEST(x.get_allocator() != y.get_allocator()); }); - BOOST_TEST_EQ(raii::destructor, 2 * num_threads * 2 * old_size); + BOOST_TEST_EQ( + raii::destructor, 2 * num_threads * value_type_cardinality * old_size); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ( - raii::copy_constructor, old_cc + (2 * num_threads * 2 * x.size())); + raii::copy_constructor, + old_cc + (2 * num_threads * value_type_cardinality * x.size())); } check_raii_counts(); @@ -264,7 +339,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -272,7 +347,7 @@ namespace { auto const old_cc = +raii::copy_constructor; thread_runner( - values, [&x, &reference_map](boost::span s) { + values, [&x, &reference_cont](boost::span s) { (void)s; BOOST_TEST(!x.empty()); @@ -283,7 +358,7 @@ namespace { BOOST_TEST_EQ(x.key_eq(), key_equal(2)); BOOST_TEST(x.get_allocator() == allocator_type(3)); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); }); BOOST_TEST_EQ(raii::destructor, 0u); @@ -295,15 +370,13 @@ namespace { // propagation { + using pocca_container_type = replace_allocator; using pocca_allocator_type = - pocca_allocator >; - - using pocca_map_type = boost::unordered::concurrent_flat_map; + typename pocca_container_type::allocator_type; raii::reset_counts(); - pocca_map_type x( + pocca_container_type x( values.size(), hasher(1), key_equal(2), pocca_allocator_type(3)); for (auto const& v : values) { x.insert(v); @@ -312,10 +385,10 @@ namespace { auto const old_size = x.size(); auto const old_cc = +raii::copy_constructor; - thread_runner(values, [&x, &values](boost::span s) { + thread_runner(values, [&x, &values](boost::span s) { (void)s; - pocca_map_type y(values.size()); + pocca_container_type y(values.size()); for (auto const& v : values) { y.insert(v); } @@ -332,40 +405,43 @@ namespace { BOOST_TEST(x.get_allocator() == y.get_allocator()); }); - BOOST_TEST_EQ(raii::destructor, 2 * num_threads * 2 * old_size); + BOOST_TEST_EQ( + raii::destructor, 2 * num_threads * value_type_cardinality * old_size); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ( - raii::copy_constructor, old_cc + (2 * num_threads * 2 * x.size())); + raii::copy_constructor, + old_cc + (2 * num_threads * value_type_cardinality * x.size())); } check_raii_counts(); } - template void move_assign(G gen, test::random_generator rg) + template + void move_assign(X*, GF gen_factory, test::random_generator rg) { - using pocma_allocator_type = pocma_allocator >; + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; - using pocma_map_type = boost::unordered::concurrent_flat_map; + using pocma_container_type = replace_allocator; + using pocma_allocator_type = typename pocma_container_type::allocator_type; + + auto gen = gen_factory.template get(); BOOST_STATIC_ASSERT( - std::is_nothrow_move_assignable, std::equal_to, - std::allocator > > >::value); + std::is_nothrow_move_assignable< + replace_allocator >::value); BOOST_STATIC_ASSERT( - std::is_nothrow_move_assignable, std::equal_to, - pocma_allocator > > >::value); + std::is_nothrow_move_assignable::value); BOOST_STATIC_ASSERT( - !std::is_nothrow_move_assignable, std::equal_to, - stateful_allocator > > >::value); + !std::is_nothrow_move_assignable< + replace_allocator >::value); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); // move assignment has more complex requirements than copying // equal allocators: @@ -383,15 +459,15 @@ namespace { { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); std::atomic num_transfers{0}; thread_runner( - values, [&x, &num_transfers](boost::span s) { + values, [&x, &num_transfers](boost::span s) { (void)s; - map_type y(0, hasher(2), key_equal(1), allocator_type(3)); + X y(0, hasher(2), key_equal(1), allocator_type(3)); BOOST_TEST(x.empty()); BOOST_TEST(y.empty()); @@ -423,15 +499,15 @@ namespace { { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); std::atomic num_transfers{0}; thread_runner( - values, [&x, &values, &num_transfers](boost::span s) { + values, [&x, &values, &num_transfers](boost::span s) { (void)s; - map_type y(values.size(), hasher(2), key_equal(1), allocator_type(3)); + X y(values.size(), hasher(2), key_equal(1), allocator_type(3)); for (auto const& v : values) { y.insert(v); } @@ -458,11 +534,13 @@ namespace { BOOST_TEST_EQ(num_transfers, 1u); - BOOST_TEST_EQ(raii::destructor, num_threads * 2 * reference_map.size()); + BOOST_TEST_EQ( + raii::destructor, num_threads * value_type_cardinality * reference_cont.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ( - raii::copy_constructor, num_threads * 2 * reference_map.size()); + raii::copy_constructor, + num_threads * value_type_cardinality * reference_cont.size()); } check_raii_counts(); @@ -470,7 +548,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -480,10 +558,10 @@ namespace { std::atomic num_transfers{0}; thread_runner(values, - [&x, &reference_map, &num_transfers](boost::span s) { + [&x, &reference_cont, &num_transfers](boost::span s) { (void)s; - map_type y(allocator_type(3)); + X y(allocator_type(3)); BOOST_TEST(y.empty()); BOOST_TEST(x.get_allocator() == y.get_allocator()); @@ -491,7 +569,7 @@ namespace { y = std::move(x); if (!y.empty()) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.hash_function(), hasher(1)); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); @@ -509,7 +587,8 @@ namespace { BOOST_TEST_EQ(num_transfers, 1u); - BOOST_TEST_EQ(raii::destructor, 2 * reference_map.size()); + BOOST_TEST_EQ( + raii::destructor, value_type_cardinality * reference_cont.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::copy_constructor, old_cc); @@ -521,7 +600,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -532,11 +611,11 @@ namespace { std::atomic num_transfers{0}; - thread_runner(values, [&x, &values, &num_transfers, &reference_map]( - boost::span s) { + thread_runner(values, [&x, &values, &num_transfers, &reference_cont]( + boost::span s) { (void)s; - map_type y(values.size(), hasher(2), key_equal(1), allocator_type(3)); + X y(values.size(), hasher(2), key_equal(1), allocator_type(3)); for (auto const& v : values) { y.insert(v); } @@ -547,7 +626,7 @@ namespace { y = std::move(x); if (y.hash_function() == hasher(1)) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); } else { @@ -565,12 +644,15 @@ namespace { BOOST_TEST_EQ(num_transfers, 1u); BOOST_TEST_EQ( - raii::destructor, 2 * old_size + num_threads * 2 * old_size); + raii::destructor, + value_type_cardinality * old_size + + num_threads * value_type_cardinality * old_size); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::copy_constructor, - old_cc + (num_threads * 2 * reference_map.size())); + BOOST_TEST_EQ( + raii::copy_constructor, + old_cc + (num_threads * value_type_cardinality * reference_cont.size())); } check_raii_counts(); @@ -578,7 +660,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -589,11 +671,11 @@ namespace { std::atomic num_transfers{0}; - thread_runner(values, [&x, &values, &num_transfers, &reference_map]( - boost::span s) { + thread_runner(values, [&x, &values, &num_transfers, &reference_cont]( + boost::span s) { (void)s; - map_type y(values.size(), hasher(2), key_equal(1), allocator_type(13)); + X y(values.size(), hasher(2), key_equal(1), allocator_type(13)); for (auto const& v : values) { y.insert(v); } @@ -610,7 +692,7 @@ namespace { y = std::move(x); if (y.hash_function() == hasher(1)) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); } else { @@ -628,12 +710,16 @@ namespace { BOOST_TEST_EQ(num_transfers, 1u); BOOST_TEST_EQ( - raii::destructor, 2 * 2 * old_size + num_threads * 2 * old_size); + raii::destructor, + 2 * value_type_cardinality * old_size + + num_threads * value_type_cardinality * old_size); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); - BOOST_TEST_EQ(raii::move_constructor, old_mc + 2 * old_size); - BOOST_TEST_EQ(raii::copy_constructor, - old_cc + (num_threads * 2 * reference_map.size())); + BOOST_TEST_EQ( + raii::move_constructor, old_mc + value_type_cardinality * old_size); + BOOST_TEST_EQ( + raii::copy_constructor, + old_cc + (num_threads * value_type_cardinality * reference_cont.size())); } check_raii_counts(); @@ -641,7 +727,7 @@ namespace { { raii::reset_counts(); - pocma_map_type x( + pocma_container_type x( values.size(), hasher(1), key_equal(2), pocma_allocator_type(3)); for (auto const& v : values) { x.insert(v); @@ -653,11 +739,11 @@ namespace { std::atomic num_transfers{0}; - thread_runner(values, [&x, &values, &num_transfers, &reference_map]( - boost::span s) { + thread_runner(values, [&x, &values, &num_transfers, &reference_cont]( + boost::span s) { (void)s; - pocma_map_type y( + pocma_container_type y( values.size(), hasher(2), key_equal(1), pocma_allocator_type(13)); for (auto const& v : values) { y.insert(v); @@ -669,7 +755,7 @@ namespace { y = std::move(x); if (y.hash_function() == hasher(1)) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); } else { @@ -687,12 +773,15 @@ namespace { BOOST_TEST_EQ(num_transfers, 1u); BOOST_TEST_EQ( - raii::destructor, 2 * old_size + num_threads * 2 * old_size); + raii::destructor, + value_type_cardinality * old_size + + num_threads * value_type_cardinality * old_size); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::copy_constructor, - old_cc + (num_threads * 2 * reference_map.size())); + BOOST_TEST_EQ( + raii::copy_constructor, + old_cc + (num_threads * value_type_cardinality * reference_cont.size())); } check_raii_counts(); @@ -700,7 +789,7 @@ namespace { { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); for (auto const& v : values) { x.insert(v); } @@ -709,7 +798,7 @@ namespace { auto const old_mc = +raii::move_constructor; thread_runner( - values, [&x, &reference_map](boost::span s) { + values, [&x, &reference_cont](boost::span s) { (void)s; x = std::move(x); @@ -720,7 +809,7 @@ namespace { BOOST_TEST_EQ(x.key_eq(), key_equal(2)); BOOST_TEST(x.get_allocator() == allocator_type(3)); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); }); BOOST_TEST_EQ(raii::destructor, 0u); @@ -732,52 +821,39 @@ namespace { check_raii_counts(); } - UNORDERED_AUTO_TEST (initializer_list_assignment) { - std::initializer_list values{ - map_value_type{raii{0}, raii{0}}, - map_value_type{raii{1}, raii{1}}, - map_value_type{raii{2}, raii{2}}, - map_value_type{raii{3}, raii{3}}, - map_value_type{raii{4}, raii{4}}, - map_value_type{raii{5}, raii{5}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{7}, raii{7}}, - map_value_type{raii{8}, raii{8}}, - map_value_type{raii{9}, raii{9}}, - map_value_type{raii{10}, raii{10}}, - map_value_type{raii{9}, raii{9}}, - map_value_type{raii{8}, raii{8}}, - map_value_type{raii{7}, raii{7}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{5}, raii{5}}, - map_value_type{raii{4}, raii{4}}, - map_value_type{raii{3}, raii{3}}, - map_value_type{raii{2}, raii{2}}, - map_value_type{raii{1}, raii{1}}, - map_value_type{raii{0}, raii{0}}, - }; + template + void initializer_list_assign(std::pair p) + { + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); - auto v = std::vector(values.begin(), values.end()); + auto init_list = p.second; + auto reference_cont = reference_container( + init_list.begin(), init_list.end()); + auto v = std::vector(init_list.begin(), init_list.end()); { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); - thread_runner(v, [&x, &values](boost::span s) { + thread_runner(v, [&x, &init_list](boost::span s) { (void)s; - x = values; + x = init_list; }); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_EQ(x.hash_function(), hasher(1)); BOOST_TEST_EQ(x.key_eq(), key_equal(2)); BOOST_TEST(x.get_allocator() == allocator_type(3)); - BOOST_TEST_EQ(raii::copy_constructor, num_threads * 2 * x.size()); - BOOST_TEST_EQ(raii::destructor, (num_threads - 1) * 2 * x.size()); + BOOST_TEST_EQ( + raii::copy_constructor, + num_threads * value_type_cardinality * x.size()); + BOOST_TEST_EQ( + raii::destructor, + (num_threads - 1) * value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::move_constructor, 0u); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); @@ -785,8 +861,12 @@ namespace { check_raii_counts(); } - template void insert_and_assign(G gen, test::random_generator rg) + template + void insert_and_assign(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); std::thread t1, t2, t3; @@ -796,40 +876,39 @@ namespace { auto v2 = v1; shuffle_values(v2); - auto reference_map = - boost::unordered_flat_map(v1.begin(), v1.end()); + auto reference_cont = reference_container(v1.begin(), v1.end()); raii::reset_counts(); { - map_type map1(v1.size(), hasher(1), key_equal(2), allocator_type(3)); - map_type map2(v2.size(), hasher(1), key_equal(2), allocator_type(3)); + X c1(v1.size(), hasher(1), key_equal(2), allocator_type(3)); + X c2(v2.size(), hasher(1), key_equal(2), allocator_type(3)); - t1 = std::thread([&v1, &map1, &start_latch, &end_latch] { + t1 = std::thread([&v1, &c1, &start_latch, &end_latch] { start_latch.arrive_and_wait(); for (auto const& v : v1) { - map1.insert(v); + c1.insert(v); } end_latch.arrive_and_wait(); }); - t2 = std::thread([&v2, &map2, &end_latch, &start_latch] { + t2 = std::thread([&v2, &c2, &end_latch, &start_latch] { start_latch.arrive_and_wait(); for (auto const& v : v2) { - map2.insert(v); + c2.insert(v); } end_latch.arrive_and_wait(); }); std::atomic num_assignments{0}; - t3 = std::thread([&map1, &map2, &end_latch, &num_assignments] { - while (map1.empty() && map2.empty()) { + t3 = std::thread([&c1, &c2, &end_latch, &num_assignments] { + while (c1.empty() && c2.empty()) { std::this_thread::sleep_for(std::chrono::microseconds(10)); } do { - map1 = map2; + c1 = c2; std::this_thread::sleep_for(std::chrono::milliseconds(100)); - map2 = map1; + c2 = c1; std::this_thread::sleep_for(std::chrono::milliseconds(100)); ++num_assignments; } while (!end_latch.try_wait()); @@ -841,21 +920,23 @@ namespace { BOOST_TEST_GT(num_assignments, 0u); - test_fuzzy_matches_reference(map1, reference_map, rg); - test_fuzzy_matches_reference(map2, reference_map, rg); + test_fuzzy_matches_reference(c1, reference_cont, rg); + test_fuzzy_matches_reference(c2, reference_cont, rg); } check_raii_counts(); } - template - void flat_map_move_assign( - FlatMapType*, MapType*, G gen, test::random_generator rg) + template + void flat_move_assign(X*, GF gen_factory, test::random_generator rg) { - using alloc_type = typename MapType::allocator_type; + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); /* * basically test that a temporary container is materialized and we @@ -868,83 +949,29 @@ namespace { { raii::reset_counts(); - FlatMapType flat_map(values.begin(), values.end(), values.size(), - hasher(1), key_equal(2), alloc_type(3)); + flat_container flat(values.begin(), values.end(), values.size(), + hasher(1), key_equal(2), allocator_type(3)); - MapType map(0, hasher(2), key_equal(1), alloc_type(3)); + X x(0, hasher(2), key_equal(1), allocator_type(3)); - BOOST_TEST(flat_map.get_allocator() == map.get_allocator()); + BOOST_TEST(flat.get_allocator() == x.get_allocator()); - map = std::move(flat_map); + x = std::move(flat); - BOOST_TEST(flat_map.empty()); - BOOST_TEST_EQ(map.size(), reference_map.size()); + BOOST_TEST(flat.empty()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); - test_fuzzy_matches_reference(map, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); - BOOST_TEST_EQ(map.hash_function(), hasher(1)); - BOOST_TEST_EQ(map.key_eq(), key_equal(2)); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); - BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); - BOOST_TEST_EQ(raii::destructor, 2 * values.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * reference_map.size()); - BOOST_TEST_EQ(raii::copy_assignment, 0u); - BOOST_TEST_EQ(raii::move_assignment, 0u); - } - - check_raii_counts(); - - { - raii::reset_counts(); - - MapType map(values.begin(), values.end(), values.size(), hasher(1), - key_equal(2), alloc_type(3)); - - FlatMapType flat_map(0, hasher(2), key_equal(1), alloc_type(3)); - - BOOST_TEST(flat_map.get_allocator() == map.get_allocator()); - - flat_map = std::move(map); - - BOOST_TEST(map.empty()); - BOOST_TEST_EQ(flat_map.size(), reference_map.size()); - - BOOST_TEST_EQ(flat_map.hash_function(), hasher(1)); - BOOST_TEST_EQ(flat_map.key_eq(), key_equal(2)); - - BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); - BOOST_TEST_EQ(raii::destructor, 2 * values.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * reference_map.size()); - BOOST_TEST_EQ(raii::copy_assignment, 0u); - BOOST_TEST_EQ(raii::move_assignment, 0u); - } - - check_raii_counts(); - - { - raii::reset_counts(); - - FlatMapType flat_map(values.begin(), values.end(), values.size(), - hasher(1), key_equal(2), alloc_type(3)); - - MapType map(0, hasher(2), key_equal(1), alloc_type(4)); - - BOOST_TEST(flat_map.get_allocator() != map.get_allocator()); - - map = std::move(flat_map); - - BOOST_TEST(flat_map.empty()); - BOOST_TEST_EQ(map.size(), reference_map.size()); - - test_fuzzy_matches_reference(map, reference_map, rg); - - BOOST_TEST_EQ(map.hash_function(), hasher(1)); - BOOST_TEST_EQ(map.key_eq(), key_equal(2)); - - BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); BOOST_TEST_EQ( - raii::destructor, 2 * values.size() + 2 * reference_map.size()); - BOOST_TEST_EQ(raii::move_constructor, 4 * reference_map.size()); + raii::copy_constructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::destructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * reference_cont.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); } @@ -954,25 +981,95 @@ namespace { { raii::reset_counts(); - MapType map(values.begin(), values.end(), values.size(), hasher(1), - key_equal(2), alloc_type(3)); + X x(values.begin(), values.end(), values.size(), hasher(1), + key_equal(2), allocator_type(3)); - FlatMapType flat_map(0, hasher(2), key_equal(1), alloc_type(4)); + flat_container flat(0, hasher(2), key_equal(1), allocator_type(3)); - BOOST_TEST(flat_map.get_allocator() != map.get_allocator()); + BOOST_TEST(flat.get_allocator() == x.get_allocator()); - flat_map = std::move(map); + flat = std::move(x); - BOOST_TEST(map.empty()); - BOOST_TEST_EQ(flat_map.size(), reference_map.size()); + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(flat.size(), reference_cont.size()); - BOOST_TEST_EQ(flat_map.hash_function(), hasher(1)); - BOOST_TEST_EQ(flat_map.key_eq(), key_equal(2)); + BOOST_TEST_EQ(flat.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat.key_eq(), key_equal(2)); - BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); BOOST_TEST_EQ( - raii::destructor, 2 * values.size() + 2 * reference_map.size()); - BOOST_TEST_EQ(raii::move_constructor, 4 * reference_map.size()); + raii::copy_constructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::destructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * reference_cont.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + + { + raii::reset_counts(); + + flat_container flat(values.begin(), values.end(), values.size(), + hasher(1), key_equal(2), allocator_type(3)); + + X x(0, hasher(2), key_equal(1), allocator_type(4)); + + BOOST_TEST(flat.get_allocator() != x.get_allocator()); + + x = std::move(flat); + + BOOST_TEST(flat.empty()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); + + test_fuzzy_matches_reference(x, reference_cont, rg); + + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::destructor, + value_type_cardinality * values.size() + + value_type_cardinality * reference_cont.size()); + BOOST_TEST_EQ( + raii::move_constructor, + 2 * value_type_cardinality * reference_cont.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + + { + raii::reset_counts(); + + X x(values.begin(), values.end(), values.size(), hasher(1), + key_equal(2), allocator_type(3)); + + flat_container flat(0, hasher(2), key_equal(1), allocator_type(4)); + + BOOST_TEST(flat.get_allocator() != x.get_allocator()); + + flat = std::move(x); + + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(flat.size(), reference_cont.size()); + + BOOST_TEST_EQ(flat.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat.key_eq(), key_equal(2)); + + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * values.size()); + BOOST_TEST_EQ( + raii::destructor, + value_type_cardinality * values.size() + + value_type_cardinality * reference_cont.size()); + BOOST_TEST_EQ( + raii::move_constructor, + 2 * value_type_cardinality * reference_cont.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); } @@ -985,17 +1082,24 @@ namespace { // clang-format off UNORDERED_TEST( copy_assign, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( move_assign, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) +UNORDERED_TEST( + initializer_list_assign, + ((test_map_and_init_list)(test_set_and_init_list))) + UNORDERED_TEST( insert_and_assign, - ((init_type_generator)) + ((test_map)(test_set)) + ((init_type_generator_factory)) ((default_generator)(sequential)(limited_range))) boost::unordered::unordered_flat_map > >* map_fancy; UNORDERED_TEST( - flat_map_move_assign, - ((flat_map_plain)) - ((map_plain)) - ((init_type_generator)) - ((default_generator)(sequential)(limited_range))) - -UNORDERED_TEST( - flat_map_move_assign, - ((flat_map_fancy)) - ((map_fancy)) - ((init_type_generator)) + flat_move_assign, + ((test_map)(test_set)(fancy_test_map)(fancy_test_set)) + ((init_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/clear_tests.cpp b/test/cfoa/clear_tests.cpp index 79240db3..4a00d08a 100644 --- a/test/cfoa/clear_tests.cpp +++ b/test/cfoa/clear_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include test::seed_t initialize_seed{674140082}; @@ -14,49 +16,62 @@ using test::sequential; using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; namespace { - template void clear_tests(G gen, test::random_generator rg) + template + void clear_tests(X*, GF gen_factory, test::random_generator rg) { + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); raii::reset_counts(); - map_type x(values.begin(), values.end(), values.size(), hasher(1), + X x(values.begin(), values.end(), values.size(), hasher(1), key_equal(2), allocator_type(3)); auto const old_size = x.size(); auto const old_d = +raii::destructor; - thread_runner(values, [&x](boost::span s) { + thread_runner(values, [&x](boost::span s) { (void)s; x.clear(); }); BOOST_TEST(x.empty()); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size); + BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size); check_raii_counts(); } - template void insert_and_clear(G gen, test::random_generator rg) + template + void insert_and_clear(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); std::thread t1, t2; { - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); std::mutex m; std::condition_variable cv; @@ -103,7 +118,7 @@ namespace { BOOST_TEST_GE(num_clears, 1u); if (!x.empty()) { - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } } @@ -115,11 +130,13 @@ namespace { // clang-format off UNORDERED_TEST( clear_tests, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST(insert_and_clear, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/common_helpers.hpp b/test/cfoa/common_helpers.hpp new file mode 100644 index 00000000..a8cb7f85 --- /dev/null +++ b/test/cfoa/common_helpers.hpp @@ -0,0 +1,137 @@ +// Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz +// 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_TEST_CFOA_COMMON_HELPERS_HPP +#define BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP + +#include +#include +#include +#include + +#include +#include +#include + +template +struct value_cardinality +{ + static constexpr std::size_t value=1; +}; + +template +struct value_cardinality > +{ + static constexpr std::size_t value=2; +}; + +template +struct reference_container_impl; + +template +using reference_container = typename reference_container_impl::type; + +template +struct reference_container_impl > +{ + using type = boost::unordered_flat_map; +}; + +template +struct reference_container_impl > +{ + using type = boost::unordered_flat_set; +}; + +template +struct flat_container_impl; + +template +using flat_container = typename flat_container_impl::type; + +template +struct flat_container_impl > +{ + using type = boost::unordered_flat_map; +}; + +template +struct flat_container_impl > +{ + using type = boost::unordered_flat_set; +}; + +template class Allocator> +struct replace_allocator_impl; + +template class Allocator> +using replace_allocator = + typename replace_allocator_impl::type; + +template < + typename K, typename V, typename H, typename P, typename A, + template class Allocator +> +struct replace_allocator_impl< + boost::concurrent_flat_map, Allocator> +{ + using value_type = + typename boost::concurrent_flat_map::value_type; + using type = + boost::concurrent_flat_map >; +}; + +template < + typename K, typename H, typename P, typename A, + template class Allocator +> +struct replace_allocator_impl< + boost::concurrent_flat_set, Allocator> +{ + using value_type = + typename boost::concurrent_flat_set::value_type; + using type = + boost::concurrent_flat_set >; +}; + +template +K const& get_key(K const& x) { return x; } + +template +K const& get_key(const std::pair& x) { return x.first; } + +template +K const& get_value(K const& x) { return x; } + +template +V const& get_value(const std::pair& x) { return x.second; } + +template +V& get_value(std::pair& x) { return x.second; } + +template +void test_matches_reference(X const& x, Y const& reference_cont) +{ + using value_type = typename X::value_type; + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); + })); +} + +template +void test_fuzzy_matches_reference( + X const& x, Y const& reference_cont, test::random_generator rg) +{ + using value_type = typename X::value_type; + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + if (rg == test::sequential) { + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); + } + })); +} + +#endif // BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP diff --git a/test/cfoa/constructor_tests.cpp b/test/cfoa/constructor_tests.cpp index 595cc281..4b3f927d 100644 --- a/test/cfoa/constructor_tests.cpp +++ b/test/cfoa/constructor_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include test::seed_t initialize_seed(4122023); @@ -46,87 +48,148 @@ template struct soccc_allocator using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; -UNORDERED_AUTO_TEST (default_constructor) { - boost::unordered::concurrent_flat_map x; - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.size(), 0u); -} +map_type* test_map; +set_type* test_set; -UNORDERED_AUTO_TEST (bucket_count_with_hasher_key_equal_and_allocator) { - raii::reset_counts(); - { - map_type x(0); +std::initializer_list map_init_list{ + {raii{0}, raii{0}}, + {raii{1}, raii{1}}, + {raii{2}, raii{2}}, + {raii{3}, raii{3}}, + {raii{4}, raii{4}}, + {raii{5}, raii{5}}, + {raii{6}, raii{6}}, + {raii{6}, raii{6}}, + {raii{7}, raii{7}}, + {raii{8}, raii{8}}, + {raii{9}, raii{9}}, + {raii{10}, raii{10}}, + {raii{9}, raii{9}}, + {raii{8}, raii{8}}, + {raii{7}, raii{7}}, + {raii{6}, raii{6}}, + {raii{5}, raii{5}}, + {raii{4}, raii{4}}, + {raii{3}, raii{3}}, + {raii{2}, raii{2}}, + {raii{1}, raii{1}}, + {raii{0}, raii{0}}, +}; - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher()); - BOOST_TEST_EQ(x.key_eq(), key_equal()); - } +std::initializer_list set_init_list{ + raii{0}, + raii{1}, + raii{2}, + raii{3}, + raii{4}, + raii{5}, + raii{6}, + raii{6}, + raii{7}, + raii{8}, + raii{9}, + raii{10}, + raii{9}, + raii{8}, + raii{7}, + raii{6}, + raii{5}, + raii{4}, + raii{3}, + raii{2}, + raii{1}, + raii{0}, +}; - { - map_type x(0, hasher(1)); - - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal()); - } - - { - map_type x(0, hasher(1), key_equal(2)); - - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal(2)); - } - - { - map_type x(0, hasher(1), key_equal(2), allocator_type{}); - - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal(2)); - BOOST_TEST(x.get_allocator() == allocator_type{}); - } -} - -UNORDERED_AUTO_TEST (soccc) { - raii::reset_counts(); - - boost::unordered::concurrent_flat_map > > - x; - - boost::unordered::concurrent_flat_map > > - y(x); - - BOOST_TEST_EQ(y.hash_function(), x.hash_function()); - BOOST_TEST_EQ(y.key_eq(), x.key_eq()); - BOOST_TEST(y.get_allocator() != x.get_allocator()); -} +auto test_map_and_init_list=std::make_pair(test_map,map_init_list); +auto test_set_and_init_list=std::make_pair(test_set,set_init_list); namespace { - template void from_iterator_range(G gen, test::random_generator rg) + template + void default_constructor(X*) { + X x; + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.size(), 0u); + } + + template + void bucket_count_with_hasher_key_equal_and_allocator(X*) + { + using allocator_type = typename X::allocator_type; + + raii::reset_counts(); + { + X x(0); + + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher()); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + } + + { + X x(0, hasher(1)); + + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + } + + { + X x(0, hasher(1), key_equal(2)); + + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + } + + { + X x(0, hasher(1), key_equal(2), allocator_type{}); + + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type{}); + } + } + + template + void soccc(X*) + { + raii::reset_counts(); + + replace_allocator x, y(x); + + BOOST_TEST_EQ(y.hash_function(), x.hash_function()); + BOOST_TEST_EQ(y.key_eq(), x.key_eq()); + BOOST_TEST(y.get_allocator() != x.get_allocator()); + } + + template + void from_iterator_range(X*, GF gen_factory, test::random_generator rg) + { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { - map_type x(values.begin(), values.end()); + X x(values.begin(), values.end()); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); BOOST_TEST_EQ(x.hash_function(), hasher()); @@ -138,9 +201,9 @@ namespace { } { - map_type x(values.begin(), values.end(), 0); + X x(values.begin(), values.end(), 0); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); BOOST_TEST_EQ(x.hash_function(), hasher()); @@ -152,9 +215,9 @@ namespace { } { - map_type x(values.begin(), values.end(), 0, hasher(1)); + X x(values.begin(), values.end(), 0, hasher(1)); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); BOOST_TEST_EQ(x.hash_function(), hasher(1)); @@ -166,9 +229,9 @@ namespace { } { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2)); + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2)); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); BOOST_TEST_EQ(x.hash_function(), hasher(1)); @@ -180,10 +243,10 @@ namespace { } { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type{}); - test_matches_reference(x, reference_map); + test_matches_reference(x, reference_cont); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); BOOST_TEST_EQ(x.hash_function(), hasher(1)); @@ -197,11 +260,14 @@ namespace { check_raii_counts(); } - template void copy_constructor(G gen, test::random_generator rg) + template + void copy_constructor(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + { - map_type x(0, hasher(1), key_equal(2), allocator_type{}); - map_type y(x); + X x(0, hasher(1), key_equal(2), allocator_type{}); + X y(x); BOOST_TEST_EQ(y.size(), x.size()); BOOST_TEST_EQ(y.hash_function(), x.hash_function()); @@ -209,24 +275,24 @@ namespace { BOOST_TEST(y.get_allocator() == x.get_allocator()); } + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type{}); thread_runner( - values, [&x, &reference_map]( + values, [&x, &reference_cont]( boost::span > s) { (void)s; - map_type y(x); + X y(x); - test_matches_reference(x, reference_map); - test_matches_reference(y, reference_map); + test_matches_reference(x, reference_cont); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.size(), x.size()); BOOST_TEST_EQ(y.hash_function(), x.hash_function()); BOOST_TEST_EQ(y.key_eq(), x.key_eq()); @@ -241,16 +307,16 @@ namespace { { allocator_type a; - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), a); + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), a); thread_runner( - values, [&x, &reference_map, a]( + values, [&x, &reference_cont, a]( boost::span > s) { (void)s; - map_type y(x, a); + X y(x, a); - test_matches_reference(x, reference_map); - test_matches_reference(y, reference_map); + test_matches_reference(x, reference_cont); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.size(), x.size()); BOOST_TEST_EQ(y.hash_function(), x.hash_function()); BOOST_TEST_EQ(y.key_eq(), x.key_eq()); @@ -261,12 +327,14 @@ namespace { check_raii_counts(); } - template - void copy_constructor_with_insertion(G gen, test::random_generator rg) + template + void copy_constructor_with_insertion(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); std::mutex m; @@ -274,7 +342,7 @@ namespace { bool ready = false; { - map_type x(0, hasher(1), key_equal(2), allocator_type{}); + X x(0, hasher(1), key_equal(2), allocator_type{}); auto f = [&x, &values, &m, &cv, &ready] { { @@ -292,7 +360,7 @@ namespace { std::thread t2(f); thread_runner( - values, [&x, &reference_map, &values, rg, &m, &cv, &ready]( + values, [&x, &reference_cont, &values, rg, &m, &cv, &ready]( boost::span > s) { (void)s; @@ -301,18 +369,18 @@ namespace { cv.wait(lk, [&] { return ready; }); } - map_type y(x); + X y(x); BOOST_TEST_LE(y.size(), values.size()); BOOST_TEST_EQ(y.hash_function(), x.hash_function()); BOOST_TEST_EQ(y.key_eq(), x.key_eq()); BOOST_TEST(y.get_allocator() == x.get_allocator()); - x.visit_all([&reference_map, rg]( - typename map_type::value_type const& val) { - BOOST_TEST(reference_map.contains(val.first)); + x.visit_all([&reference_cont, rg]( + typename X::value_type const& val) { + BOOST_TEST(reference_cont.contains(get_key(val))); if (rg == sequential) { - BOOST_TEST_EQ(val.second, reference_map.find(val.first)->second); + BOOST_TEST_EQ(val, *reference_cont.find(get_key(val))); } }); }); @@ -324,13 +392,19 @@ namespace { check_raii_counts(); } - template void move_constructor(G gen, test::random_generator rg) + template + void move_constructor(X*, GF gen_factory, test::random_generator rg) { + using value_type = typename X::value_type; + using allocator_type = typename X::allocator_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + { - map_type x(0, hasher(1), key_equal(2), allocator_type{}); + X x(0, hasher(1), key_equal(2), allocator_type{}); auto const old_size = x.size(); - map_type y(std::move(x)); + X y(std::move(x)); BOOST_TEST_EQ(y.size(), old_size); BOOST_TEST_EQ(y.hash_function(), hasher(1)); @@ -343,14 +417,14 @@ namespace { BOOST_TEST(y.get_allocator() == x.get_allocator()); } + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type{}); std::atomic_uint num_transfers{0}; @@ -358,17 +432,17 @@ namespace { auto const old_mc = +raii::move_constructor; thread_runner( - values, [&x, &reference_map, &num_transfers]( + values, [&x, &reference_cont, &num_transfers]( boost::span > s) { (void)s; auto const old_size = x.size(); - map_type y(std::move(x)); + X y(std::move(x)); if (!y.empty()) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.size(), old_size); BOOST_TEST_EQ(y.hash_function(), hasher(1)); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); @@ -395,7 +469,7 @@ namespace { raii::reset_counts(); { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type{1}); std::atomic_uint num_transfers{0}; @@ -404,19 +478,19 @@ namespace { auto const old_size = x.size(); thread_runner( - values, [&x, &reference_map, &num_transfers, old_size]( + values, [&x, &reference_cont, &num_transfers, old_size]( boost::span > s) { (void)s; auto a = allocator_type{2}; BOOST_TEST(a != x.get_allocator()); - map_type y(std::move(x), a); + X y(std::move(x), a); if (!y.empty()) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.size(), old_size); BOOST_TEST_EQ(y.hash_function(), hasher(1)); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); @@ -435,7 +509,8 @@ namespace { }); BOOST_TEST_EQ(num_transfers, 1u); - BOOST_TEST_EQ(raii::move_constructor, old_mc + (2 * old_size)); + BOOST_TEST_EQ( + raii::move_constructor, old_mc + (value_type_cardinality * old_size)); } check_raii_counts(); @@ -444,7 +519,7 @@ namespace { raii::reset_counts(); { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type{1}); std::atomic_uint num_transfers{0}; @@ -453,19 +528,19 @@ namespace { auto const old_size = x.size(); thread_runner( - values, [&x, &reference_map, &num_transfers, old_size]( + values, [&x, &reference_cont, &num_transfers, old_size]( boost::span > s) { (void)s; auto a = allocator_type{1}; BOOST_TEST(a == x.get_allocator()); - map_type y(std::move(x), a); + X y(std::move(x), a); if (!y.empty()) { ++num_transfers; - test_matches_reference(y, reference_map); + test_matches_reference(y, reference_cont); BOOST_TEST_EQ(y.size(), old_size); BOOST_TEST_EQ(y.hash_function(), hasher(1)); BOOST_TEST_EQ(y.key_eq(), key_equal(2)); @@ -490,12 +565,16 @@ namespace { check_raii_counts(); } - template - void move_constructor_with_insertion(G gen, test::random_generator rg) + template + void move_constructor_with_insertion( + X*, GF gen_factory, test::random_generator rg) { + using value_type = typename X::value_type; + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); @@ -504,7 +583,7 @@ namespace { bool ready = false; { - map_type x(0, hasher(1), key_equal(2), allocator_type{}); + X x(0, hasher(1), key_equal(2), allocator_type{}); std::atomic_uint num_transfers{0}; @@ -527,7 +606,7 @@ namespace { }); thread_runner( - values, [&x, &reference_map, &num_transfers, rg, &m, &ready, &cv]( + values, [&x, &reference_cont, &num_transfers, rg, &m, &ready, &cv]( boost::span > s) { (void)s; @@ -536,15 +615,15 @@ namespace { cv.wait(lk, [&] { return ready; }); } - map_type y(std::move(x)); + X y(std::move(x)); if (!y.empty()) { ++num_transfers; - y.cvisit_all([&reference_map, rg](map_value_type const& val) { - BOOST_TEST(reference_map.contains(val.first)); + y.cvisit_all([&reference_cont, rg](value_type const& val) { + BOOST_TEST(reference_cont.contains(get_key(val))); if (rg == sequential) { BOOST_TEST_EQ( - val.second, reference_map.find(val.first)->second); + val, *reference_cont.find(get_key(val))); } }); } @@ -559,18 +638,21 @@ namespace { check_raii_counts(); } - template - void iterator_range_with_allocator(G gen, test::random_generator rg) + template + void iterator_range_with_allocator( + X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { allocator_type a; - map_type x(values.begin(), values.end(), a); + X x(values.begin(), values.end(), a); BOOST_TEST_GT(x.size(), 0u); BOOST_TEST_LE(x.size(), values.size()); @@ -583,18 +665,22 @@ namespace { BOOST_TEST(x.get_allocator() == a); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); } - UNORDERED_AUTO_TEST (explicit_allocator) { + template + void explicit_allocator(X*) + { + using allocator_type = typename X::allocator_type; + raii::reset_counts(); { allocator_type a; - map_type x(a); + X x(a); BOOST_TEST_EQ(x.size(), 0u); BOOST_TEST_EQ(x.hash_function(), hasher()); @@ -604,37 +690,20 @@ namespace { } } - UNORDERED_AUTO_TEST (initializer_list_with_all_params) { - // hard-code 11 unique values - std::initializer_list ilist{ - map_value_type{raii{0}, raii{0}}, - map_value_type{raii{1}, raii{1}}, - map_value_type{raii{2}, raii{2}}, - map_value_type{raii{3}, raii{3}}, - map_value_type{raii{4}, raii{4}}, - map_value_type{raii{5}, raii{5}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{7}, raii{7}}, - map_value_type{raii{8}, raii{8}}, - map_value_type{raii{9}, raii{9}}, - map_value_type{raii{10}, raii{10}}, - map_value_type{raii{9}, raii{9}}, - map_value_type{raii{8}, raii{8}}, - map_value_type{raii{7}, raii{7}}, - map_value_type{raii{6}, raii{6}}, - map_value_type{raii{5}, raii{5}}, - map_value_type{raii{4}, raii{4}}, - map_value_type{raii{3}, raii{3}}, - map_value_type{raii{2}, raii{2}}, - map_value_type{raii{1}, raii{1}}, - map_value_type{raii{0}, raii{0}}, - }; + template + void initializer_list_with_all_params(std::pair p) + { + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + + auto init_list = p.second; { raii::reset_counts(); - map_type x(ilist, 0, hasher(1), key_equal(2), allocator_type(3)); + X x(init_list, 0, hasher(1), key_equal(2), allocator_type(3)); BOOST_TEST_EQ(x.size(), 11u); BOOST_TEST_EQ(x.hash_function(), hasher(1)); @@ -642,15 +711,17 @@ namespace { BOOST_TEST(x.get_allocator() == allocator_type(3)); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * 11u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * 11u); } check_raii_counts(); { raii::reset_counts(); - map_type x(ilist, allocator_type(3)); + X x(init_list, allocator_type(3)); BOOST_TEST_EQ(x.size(), 11u); BOOST_TEST_EQ(x.hash_function(), hasher()); @@ -658,15 +729,17 @@ namespace { BOOST_TEST(x.get_allocator() == allocator_type(3)); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * 11u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * 11u); } check_raii_counts(); { raii::reset_counts(); - map_type x(ilist, 0, allocator_type(3)); + X x(init_list, 0, allocator_type(3)); BOOST_TEST_EQ(x.size(), 11u); BOOST_TEST_EQ(x.hash_function(), hasher()); @@ -674,15 +747,17 @@ namespace { BOOST_TEST(x.get_allocator() == allocator_type(3)); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * 11u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * 11u); } check_raii_counts(); { raii::reset_counts(); - map_type x(ilist, 0, hasher(1), allocator_type(3)); + X x(init_list, 0, hasher(1), allocator_type(3)); BOOST_TEST_EQ(x.size(), 11u); BOOST_TEST_EQ(x.hash_function(), hasher(1)); @@ -690,58 +765,70 @@ namespace { BOOST_TEST(x.get_allocator() == allocator_type(3)); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size()); - BOOST_TEST_EQ(raii::move_constructor, 2 * 11u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * 11u); } check_raii_counts(); } - UNORDERED_AUTO_TEST (bucket_count_and_allocator) { - raii::reset_counts(); - - { - map_type x(0, allocator_type(3)); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher()); - BOOST_TEST_EQ(x.key_eq(), key_equal()); - BOOST_TEST(x.get_allocator() == allocator_type(3)); - } - - { - map_type x(4096, allocator_type(3)); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher()); - BOOST_TEST_EQ(x.key_eq(), key_equal()); - BOOST_TEST(x.get_allocator() == allocator_type(3)); - } - } - - UNORDERED_AUTO_TEST (bucket_count_with_hasher_and_allocator) { - raii::reset_counts(); - - { - map_type x(0, hasher(1), allocator_type(3)); - BOOST_TEST_EQ(x.size(), 0u); - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal()); - BOOST_TEST(x.get_allocator() == allocator_type(3)); - } - } - - template - void iterator_range_with_bucket_count_and_allocator( - G gen, test::random_generator rg) + template + void bucket_count_and_allocator(X*) { + using allocator_type = typename X::allocator_type; + + raii::reset_counts(); + + { + X x(0, allocator_type(3)); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher()); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + } + + { + X x(4096, allocator_type(3)); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher()); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + } + } + + template + void bucket_count_with_hasher_and_allocator(X*) + { + using allocator_type = typename X::allocator_type; + + raii::reset_counts(); + + { + X x(0, hasher(1), allocator_type(3)); + BOOST_TEST_EQ(x.size(), 0u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + } + } + + template + void iterator_range_with_bucket_count_and_allocator( + X*, GF gen_factory, test::random_generator rg) + { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { allocator_type a(3); - map_type x(values.begin(), values.end(), 0, a); - test_fuzzy_matches_reference(x, reference_map, rg); + X x(values.begin(), values.end(), 0, a); + test_fuzzy_matches_reference(x, reference_cont, rg); BOOST_TEST_EQ(x.hash_function(), hasher()); BOOST_TEST_EQ(x.key_eq(), key_equal()); @@ -751,21 +838,23 @@ namespace { check_raii_counts(); } - template + template void iterator_range_with_bucket_count_hasher_and_allocator( - G gen, test::random_generator rg) + X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { allocator_type a(3); hasher hf(1); - map_type x(values.begin(), values.end(), 0, hf, a); - test_fuzzy_matches_reference(x, reference_map, rg); + X x(values.begin(), values.end(), 0, hf, a); + test_fuzzy_matches_reference(x, reference_cont, rg); BOOST_TEST_EQ(x.hash_function(), hf); BOOST_TEST_EQ(x.key_eq(), key_equal()); @@ -775,19 +864,22 @@ namespace { check_raii_counts(); } - template void flat_map_constructor(G gen, test::random_generator rg) + template + void flat_constructor(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map( - values.begin(), values.end(), values.size()); + auto reference_cont = reference_container(values.begin(), values.end()); + auto reference_flat= flat_container(values.begin(), values.end()); raii::reset_counts(); { - boost::unordered_flat_map - flat_map(values.begin(), values.end(), reference_map.size(), hasher(1), - key_equal(2), allocator_type(3)); + flat_container flat( + values.begin(), values.end(), reference_cont.size(), hasher(1), + key_equal(2), allocator_type(3)); auto const old_dc = +raii::default_constructor; auto const old_mc = +raii::move_constructor; @@ -797,9 +889,9 @@ namespace { BOOST_TEST_GT(old_mc, 0u); BOOST_TEST_GT(old_cc, 0u); - map_type x(std::move(flat_map)); + X x(std::move(flat)); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); BOOST_TEST_EQ(+raii::default_constructor, old_dc); BOOST_TEST_EQ(+raii::move_constructor, old_mc); @@ -809,16 +901,15 @@ namespace { BOOST_TEST_EQ(x.key_eq(), key_equal(2)); BOOST_TEST(x.get_allocator() == allocator_type(3)); - BOOST_TEST(flat_map.empty()); + BOOST_TEST(flat.empty()); } check_raii_counts(); { - boost::unordered_flat_map - flat_map(0, hasher(1), key_equal(2), allocator_type(3)); + flat_container flat(0, hasher(1), key_equal(2), allocator_type(3)); - map_type x(std::move(flat_map)); + X x(std::move(flat)); BOOST_TEST(x.empty()); @@ -826,13 +917,13 @@ namespace { BOOST_TEST_EQ(x.key_eq(), key_equal(2)); BOOST_TEST(x.get_allocator() == allocator_type(3)); - BOOST_TEST(flat_map.empty()); + BOOST_TEST(flat.empty()); } check_raii_counts(); { - map_type flat_map(values.begin(), values.end(), reference_map.size(), + X x(values.begin(), values.end(), reference_cont.size(), hasher(1), key_equal(2), allocator_type(3)); auto const old_dc = +raii::default_constructor; @@ -843,89 +934,124 @@ namespace { BOOST_TEST_GT(old_mc, 0u); BOOST_TEST_GT(old_cc, 0u); - boost::unordered_flat_map - x(std::move(flat_map)); + flat_container flat(std::move(x)); - BOOST_TEST(x == reference_map); + BOOST_TEST(flat == reference_flat); BOOST_TEST_EQ(+raii::default_constructor, old_dc); BOOST_TEST_EQ(+raii::move_constructor, old_mc); BOOST_TEST_EQ(+raii::copy_constructor, old_cc); - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal(2)); - BOOST_TEST(x.get_allocator() == allocator_type(3)); + BOOST_TEST_EQ(flat.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat.key_eq(), key_equal(2)); + BOOST_TEST(flat.get_allocator() == allocator_type(3)); - BOOST_TEST(flat_map.empty()); + BOOST_TEST(x.empty()); } check_raii_counts(); { - map_type flat_map(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); - boost::unordered_flat_map - x(std::move(flat_map)); + flat_container flat(std::move(x)); + + BOOST_TEST(flat.empty()); + + BOOST_TEST_EQ(flat.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat.key_eq(), key_equal(2)); + BOOST_TEST(flat.get_allocator() == allocator_type(3)); BOOST_TEST(x.empty()); - - BOOST_TEST_EQ(x.hash_function(), hasher(1)); - BOOST_TEST_EQ(x.key_eq(), key_equal(2)); - BOOST_TEST(x.get_allocator() == allocator_type(3)); - - BOOST_TEST(flat_map.empty()); } check_raii_counts(); } + } // namespace // clang-format off +UNORDERED_TEST( + default_constructor, + ((test_map)(test_set))) + +UNORDERED_TEST( + bucket_count_with_hasher_key_equal_and_allocator, + ((test_map)(test_set))) + +UNORDERED_TEST( + soccc, + ((test_map)(test_set))) + UNORDERED_TEST( from_iterator_range, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( copy_constructor, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( copy_constructor_with_insertion, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( move_constructor, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( move_constructor_with_insertion, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( iterator_range_with_allocator, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) +UNORDERED_TEST( + explicit_allocator, + ((test_map)(test_set))) + +UNORDERED_TEST( + initializer_list_with_all_params, + ((test_map_and_init_list)(test_set_and_init_list))) + +UNORDERED_TEST( + bucket_count_and_allocator, + ((test_map)(test_set))) + +UNORDERED_TEST( + bucket_count_with_hasher_and_allocator, + ((test_map)(test_set))) + UNORDERED_TEST( iterator_range_with_bucket_count_and_allocator, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( iterator_range_with_bucket_count_hasher_and_allocator, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( - flat_map_constructor, - ((value_type_generator)) + flat_constructor, + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) - // clang-format on RUN_TESTS() diff --git a/test/cfoa/emplace_tests.cpp b/test/cfoa/emplace_tests.cpp index ba8ac70d..0ecc66d4 100644 --- a/test/cfoa/emplace_tests.cpp +++ b/test/cfoa/emplace_tests.cpp @@ -1,34 +1,114 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include #include namespace { test::seed_t initialize_seed(335740237); + template + bool member_emplace(Container& x, Value const & v) + { + return x.emplace(v.x_); + } + + template + bool member_emplace(Container& x, Value& v) + { + return x.emplace(v.x_); + } + + template + bool member_emplace(Container& x, std::pair const & v) + { + return x.emplace(v.first.x_, v.second.x_); + } + + template + bool member_emplace(Container& x, std::pair& v) + { + return x.emplace(v.first.x_, v.second.x_); + } + + template + bool member_emplace_or_visit(Container& x, Value const & v, F f) + { + return x.emplace_or_visit(v.x_, f); + } + + template + bool member_emplace_or_visit(Container& x, Value& v, F f) + { + return x.emplace_or_visit(v.x_, f); + } + + template + bool member_emplace_or_visit( + Container& x, std::pair const & v, F f) + { + return x.emplace_or_visit(v.first.x_, v.second.x_, f); + } + + template + bool member_emplace_or_visit(Container& x, std::pair& v, F f) + { + return x.emplace_or_visit(v.first.x_, v.second.x_, f); + } + + template + bool member_emplace_or_cvisit(Container& x, Value const & v, F f) + { + return x.emplace_or_cvisit(v.x_, f); + } + + template + bool member_emplace_or_cvisit(Container& x, Value& v, F f) + { + return x.emplace_or_cvisit(v.x_, f); + } + + template + bool member_emplace_or_cvisit( + Container& x, std::pair const & v, F f) + { + return x.emplace_or_cvisit(v.first.x_, v.second.x_, f); + } + + template + bool member_emplace_or_cvisit(Container& x, std::pair& v, F f) + { + return x.emplace_or_cvisit(v.first.x_, v.second.x_, f); + } + struct lvalue_emplacer_type { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_inserts{0}; thread_runner(values, [&x, &num_inserts](boost::span s) { for (auto const& r : s) { - bool b = x.emplace(r.first.x_, r.second.x_); + bool b = member_emplace(x, r); if (b) { ++num_inserts; } } }); BOOST_TEST_EQ(num_inserts, x.size()); - BOOST_TEST_EQ(raii::default_constructor, 2 * values.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); BOOST_TEST_EQ(raii::copy_assignment, 0u); @@ -40,9 +120,12 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + x.reserve(values.size()); lvalue_emplacer_type::operator()(values, x); - BOOST_TEST_EQ(raii::move_constructor, 2 * x.size()); + BOOST_TEST_EQ(raii::move_constructor, value_type_cardinality * x.size()); } } norehash_lvalue_emplacer; @@ -50,12 +133,15 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { for (auto& r : s) { - bool b = x.emplace_or_cvisit( - r.first.x_, r.second.x_, + bool b = member_emplace_or_cvisit( + x, r, [&num_invokes](typename X::value_type const& v) { (void)v; ++num_invokes; @@ -70,9 +156,10 @@ namespace { BOOST_TEST_EQ(num_inserts, x.size()); BOOST_TEST_EQ(num_invokes, values.size() - x.size()); - BOOST_TEST_EQ(raii::default_constructor, 2 * values.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::copy_assignment, 0u); } @@ -82,13 +169,23 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { for (auto& r : s) { - bool b = x.emplace_or_visit( - r.first.x_, r.second.x_, - [&num_invokes](typename X::value_type& v) { + bool b = member_emplace_or_visit( + x, r, + [&num_invokes](arg_type& v) { (void)v; ++num_invokes; }); @@ -102,20 +199,21 @@ namespace { BOOST_TEST_EQ(num_inserts, x.size()); BOOST_TEST_EQ(num_invokes, values.size() - x.size()); - BOOST_TEST_EQ(raii::default_constructor, 2 * values.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::copy_assignment, 0u); } } lvalue_emplace_or_visit; - template - void emplace(X*, G gen, F emplacer, test::random_generator rg) + template + void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { @@ -123,13 +221,13 @@ namespace { emplacer(values, x); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); if (rg == test::sequential) { - BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); } })); } @@ -145,6 +243,7 @@ namespace { } boost::unordered::concurrent_flat_map* map; + boost::unordered::concurrent_flat_set* set; } // namespace @@ -156,8 +255,8 @@ using test::sequential; UNORDERED_TEST( emplace, - ((map)) - ((value_type_generator)(init_type_generator)) + ((map)(set)) + ((value_type_generator_factory)(init_type_generator_factory)) ((lvalue_emplacer)(norehash_lvalue_emplacer) (lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)) ((default_generator)(sequential)(limited_range))) diff --git a/test/cfoa/equality_tests.cpp b/test/cfoa/equality_tests.cpp index 8ab2fbb6..391be096 100644 --- a/test/cfoa/equality_tests.cpp +++ b/test/cfoa/equality_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include test::seed_t initialize_seed{1634048962}; @@ -14,16 +16,21 @@ using test::sequential; using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; namespace { - UNORDERED_AUTO_TEST (simple_equality) { + UNORDERED_AUTO_TEST (simple_map_equality) { + using allocator_type = map_type::allocator_type; + { map_type x1( {{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3)); @@ -50,17 +57,42 @@ namespace { } } - template void insert_and_compare(G gen, test::random_generator rg) + UNORDERED_AUTO_TEST (simple_set_equality) { + using allocator_type = set_type::allocator_type; + + { + set_type x1( + {1, 2}, 0, hasher(1), key_equal(2), allocator_type(3)); + + set_type x2( + {1, 2}, 0, hasher(2), key_equal(2), allocator_type(3)); + + set_type x3({1}, 0, hasher(2), key_equal(2), allocator_type(3)); + + BOOST_TEST_EQ(x1.size(), x2.size()); + BOOST_TEST(x1 == x2); + BOOST_TEST(!(x1 != x2)); + + BOOST_TEST(x1.size() != x3.size()); + BOOST_TEST(!(x1 == x3)); + BOOST_TEST(x1 != x3); + } + } + + template + void insert_and_compare(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); - boost::unordered_flat_map reference_map( - vals1.begin(), vals1.end()); + auto reference_cont = reference_container(vals1.begin(), vals1.end()); { raii::reset_counts(); - map_type x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3)); - map_type x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2), + X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3)); + X x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2), key_equal(2), allocator_type(3)); std::thread t1, t2; @@ -126,7 +158,7 @@ namespace { BOOST_TEST(x1 == x2); BOOST_TEST(!(x1 != x2)); - test_matches_reference(x1, reference_map); + test_matches_reference(x1, reference_cont); } check_raii_counts(); } @@ -135,7 +167,8 @@ namespace { // clang-format off UNORDERED_TEST( insert_and_compare, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/erase_tests.cpp b/test/cfoa/erase_tests.cpp index 0bf4041b..bb870701 100644 --- a/test/cfoa/erase_tests.cpp +++ b/test/cfoa/erase_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include #include @@ -15,6 +17,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_erased{0}; auto const old_size = x.size(); @@ -26,11 +31,11 @@ namespace { BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor + raii::move_constructor, - raii::destructor + 2 * x.size()); + raii::destructor + value_type_cardinality * x.size()); thread_runner(values, [&values, &num_erased, &x](boost::span) { - for (auto const& k : values) { - auto count = x.erase(k.first); + for (auto const& v : values) { + auto count = x.erase(get_key(v)); num_erased += count; BOOST_TEST_LE(count, 1u); BOOST_TEST_GE(count, 0u); @@ -41,7 +46,7 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size); + BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size); BOOST_TEST_EQ(x.size(), 0u); BOOST_TEST(x.empty()); @@ -53,6 +58,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_erased{0}; auto const old_size = x.size(); @@ -64,7 +72,7 @@ namespace { BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor + raii::move_constructor, - raii::destructor + 2 * x.size()); + raii::destructor + value_type_cardinality * x.size()); thread_runner(values, [&num_erased, &x](boost::span s) { for (auto const& k : s) { @@ -92,6 +100,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_erased{0}; @@ -105,8 +122,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -114,15 +131,15 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); thread_runner(values, [&num_erased, &x, threshold](boost::span s) { - for (auto const& k : s) { - auto count = x.erase_if(k.first, - [threshold](value_type& v) { return v.second.x_ > threshold; }); + for (auto const& v : s) { + auto count = x.erase_if(get_key(v), + [threshold](arg_type& w) { return get_value(w).x_ > threshold; }); num_erased += count; BOOST_TEST_LE(count, 1u); BOOST_TEST_GE(count, 0u); @@ -136,7 +153,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } lvalue_eraser_if; @@ -145,6 +163,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_erased{0}; @@ -158,8 +185,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -167,15 +194,15 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); thread_runner(values, [&num_erased, &x, threshold](boost::span s) { - for (auto const& k : s) { - auto count = x.erase_if(k.first.x_, - [threshold](value_type& v) { return v.second.x_ > threshold; }); + for (auto const& v : s) { + auto count = x.erase_if(get_key(v).x_, + [threshold](arg_type& w) { return get_value(w).x_ > threshold; }); num_erased += count; BOOST_TEST_LE(count, 1u); BOOST_TEST_GE(count, 0u); @@ -189,7 +216,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } transp_lvalue_eraser_if; @@ -198,6 +226,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_erased{0}; @@ -211,8 +248,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -220,7 +257,7 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); @@ -229,7 +266,7 @@ namespace { values, [&num_erased, &x, threshold](boost::span /* s */) { for (std::size_t i = 0; i < 128; ++i) { auto count = x.erase_if( - [threshold](value_type& v) { return v.second.x_ > threshold; }); + [threshold](arg_type& v) { return get_value(v).x_ > threshold; }); num_erased += count; } }); @@ -241,7 +278,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } erase_if; @@ -250,6 +288,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_erased{0}; @@ -263,8 +310,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -272,7 +319,7 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); @@ -281,7 +328,8 @@ namespace { values, [&num_erased, &x, threshold](boost::span /* s */) { for (std::size_t i = 0; i < 128; ++i) { auto count = boost::unordered::erase_if(x, - [threshold](value_type& v) { return v.second.x_ > threshold; }); + [threshold](arg_type& v) { + return get_value(v).x_ > threshold; }); num_erased += count; } }); @@ -293,7 +341,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } free_fn_erase_if; @@ -303,6 +352,15 @@ namespace { { #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_invokes{0}; @@ -316,8 +374,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -325,7 +383,7 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); @@ -333,9 +391,9 @@ namespace { thread_runner(values, [&num_invokes, &x, threshold](boost::span s) { (void)s; x.erase_if( - std::execution::par, [&num_invokes, threshold](value_type& v) { + std::execution::par, [&num_invokes, threshold](arg_type& v) { ++num_invokes; - return v.second.x_ > threshold; + return get_value(v).x_ > threshold; }); }); @@ -346,7 +404,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * expected_erasures); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * expected_erasures); #else (void)values; (void)x; @@ -354,12 +413,12 @@ namespace { } } erase_if_exec_policy; - template - void erase(X*, G gen, F eraser, test::random_generator rg) + template + void erase(X*, GF gen_factory, F eraser, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { @@ -367,20 +426,23 @@ namespace { x.insert(values.begin(), values.end()); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); eraser(values, x); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); } boost::unordered::concurrent_flat_map* map; + boost::unordered::concurrent_flat_set* set; boost::unordered::concurrent_flat_map* transparent_map; + boost::unordered::concurrent_flat_map* transparent_set; } // namespace @@ -391,15 +453,15 @@ using test::sequential; // clang-format off UNORDERED_TEST( erase, - ((map)) - ((value_type_generator)(init_type_generator)) + ((map)(set)) + ((value_type_generator_factory)(init_type_generator_factory)) ((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)(erase_if_exec_policy)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( erase, ((transparent_map)) - ((value_type_generator)(init_type_generator)) + ((value_type_generator_factory)(init_type_generator_factory)) ((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy)) ((default_generator)(sequential)(limited_range))) diff --git a/test/cfoa/exception_assign_tests.cpp b/test/cfoa/exception_assign_tests.cpp index 36e94367..96199973 100644 --- a/test/cfoa/exception_assign_tests.cpp +++ b/test/cfoa/exception_assign_tests.cpp @@ -1,24 +1,87 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "exception_helpers.hpp" #include - -using allocator_type = stateful_allocator >; +#include using hasher = stateful_hash; using key_equal = stateful_key_equal; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; + +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; + +std::initializer_list map_init_list{ + {raii{0}, raii{0}}, + {raii{1}, raii{1}}, + {raii{2}, raii{2}}, + {raii{3}, raii{3}}, + {raii{4}, raii{4}}, + {raii{5}, raii{5}}, + {raii{6}, raii{6}}, + {raii{6}, raii{6}}, + {raii{7}, raii{7}}, + {raii{8}, raii{8}}, + {raii{9}, raii{9}}, + {raii{10}, raii{10}}, + {raii{9}, raii{9}}, + {raii{8}, raii{8}}, + {raii{7}, raii{7}}, + {raii{6}, raii{6}}, + {raii{5}, raii{5}}, + {raii{4}, raii{4}}, + {raii{3}, raii{3}}, + {raii{2}, raii{2}}, + {raii{1}, raii{1}}, + {raii{0}, raii{0}}, +}; + +std::initializer_list set_init_list{ + raii{0}, + raii{1}, + raii{2}, + raii{3}, + raii{4}, + raii{5}, + raii{6}, + raii{6}, + raii{7}, + raii{8}, + raii{9}, + raii{10}, + raii{9}, + raii{8}, + raii{7}, + raii{6}, + raii{5}, + raii{4}, + raii{3}, + raii{2}, + raii{1}, + raii{0}, +}; + +auto test_map_and_init_list=std::make_pair(test_map,map_init_list); +auto test_set_and_init_list=std::make_pair(test_set,set_init_list); namespace { test::seed_t initialize_seed(1794114520); - template void copy_assign(G gen, test::random_generator rg) + template + void copy_assign(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); { @@ -31,12 +94,12 @@ namespace { values.begin() + static_cast(values.size() / 2); auto end = values.end(); - auto reference_map = boost::unordered_flat_map(begin, mid); + auto reference_cont = reference_container(begin, mid); - map_type x( + X x( begin, mid, values.size(), hasher(1), key_equal(2), allocator_type(3)); - map_type y( + X y( mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4)); BOOST_TEST(!y.empty()); @@ -53,13 +116,17 @@ namespace { disable_exceptions(); BOOST_TEST_GT(num_throws, 0u); - test_fuzzy_matches_reference(y, reference_map, rg); + test_fuzzy_matches_reference(y, reference_cont, rg); } check_raii_counts(); } - template void move_assign(G gen, test::random_generator rg) + template + void move_assign(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); { @@ -72,7 +139,7 @@ namespace { values.begin() + static_cast(values.size() / 2); auto end = values.end(); - auto reference_map = boost::unordered_flat_map(begin, mid); + auto reference_cont = reference_container(begin, mid); BOOST_TEST( !boost::allocator_is_always_equal::type::value); @@ -83,10 +150,10 @@ namespace { for (std::size_t i = 0; i < 2 * alloc_throw_threshold; ++i) { disable_exceptions(); - map_type x(begin, mid, values.size(), hasher(1), key_equal(2), + X x(begin, mid, values.size(), hasher(1), key_equal(2), allocator_type(3)); - map_type y( + X y( mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4)); enable_exceptions(); @@ -96,7 +163,7 @@ namespace { ++num_throws; } disable_exceptions(); - test_fuzzy_matches_reference(y, reference_map, rg); + test_fuzzy_matches_reference(y, reference_cont, rg); } BOOST_TEST_GT(num_throws, 0u); @@ -104,43 +171,22 @@ namespace { check_raii_counts(); } - UNORDERED_AUTO_TEST (intializer_list_assign) { - using value_type = typename map_type::value_type; + template + void intializer_list_assign(std::pair p) + { + using allocator_type = typename X::allocator_type; - std::initializer_list values{ - value_type{raii{0}, raii{0}}, - value_type{raii{1}, raii{1}}, - value_type{raii{2}, raii{2}}, - value_type{raii{3}, raii{3}}, - value_type{raii{4}, raii{4}}, - value_type{raii{5}, raii{5}}, - value_type{raii{6}, raii{6}}, - value_type{raii{6}, raii{6}}, - value_type{raii{7}, raii{7}}, - value_type{raii{8}, raii{8}}, - value_type{raii{9}, raii{9}}, - value_type{raii{10}, raii{10}}, - value_type{raii{9}, raii{9}}, - value_type{raii{8}, raii{8}}, - value_type{raii{7}, raii{7}}, - value_type{raii{6}, raii{6}}, - value_type{raii{5}, raii{5}}, - value_type{raii{4}, raii{4}}, - value_type{raii{3}, raii{3}}, - value_type{raii{2}, raii{2}}, - value_type{raii{1}, raii{1}}, - value_type{raii{0}, raii{0}}, - }; + auto init_list = p.second; { raii::reset_counts(); unsigned num_throws = 0; for (std::size_t i = 0; i < throw_threshold; ++i) { - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); enable_exceptions(); try { - x = values; + x = init_list; } catch (...) { ++num_throws; } @@ -160,13 +206,19 @@ using test::sequential; // clang-format off UNORDERED_TEST( copy_assign, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( move_assign, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential))) + +UNORDERED_TEST( + intializer_list_assign, + ((test_map_and_init_list)(test_set_and_init_list))) // clang-format on RUN_TESTS() diff --git a/test/cfoa/exception_constructor_tests.cpp b/test/cfoa/exception_constructor_tests.cpp index 998a6411..0899138e 100644 --- a/test/cfoa/exception_constructor_tests.cpp +++ b/test/cfoa/exception_constructor_tests.cpp @@ -1,23 +1,84 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "exception_helpers.hpp" #include - -using allocator_type = stateful_allocator >; +#include using hasher = stateful_hash; using key_equal = stateful_key_equal; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; + +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; + +std::initializer_list map_init_list{ + {raii{0}, raii{0}}, + {raii{1}, raii{1}}, + {raii{2}, raii{2}}, + {raii{3}, raii{3}}, + {raii{4}, raii{4}}, + {raii{5}, raii{5}}, + {raii{6}, raii{6}}, + {raii{6}, raii{6}}, + {raii{7}, raii{7}}, + {raii{8}, raii{8}}, + {raii{9}, raii{9}}, + {raii{10}, raii{10}}, + {raii{9}, raii{9}}, + {raii{8}, raii{8}}, + {raii{7}, raii{7}}, + {raii{6}, raii{6}}, + {raii{5}, raii{5}}, + {raii{4}, raii{4}}, + {raii{3}, raii{3}}, + {raii{2}, raii{2}}, + {raii{1}, raii{1}}, + {raii{0}, raii{0}}, +}; + +std::initializer_list set_init_list{ + raii{0}, + raii{1}, + raii{2}, + raii{3}, + raii{4}, + raii{5}, + raii{6}, + raii{6}, + raii{7}, + raii{8}, + raii{9}, + raii{10}, + raii{9}, + raii{8}, + raii{7}, + raii{6}, + raii{5}, + raii{4}, + raii{3}, + raii{2}, + raii{1}, + raii{0}, +}; + +auto test_map_and_init_list=std::make_pair(test_map,map_init_list); +auto test_set_and_init_list=std::make_pair(test_set,set_init_list); namespace { test::seed_t initialize_seed(795610904); - UNORDERED_AUTO_TEST (bucket_constructor) { + template + void bucket_constructor(X*) + { raii::reset_counts(); bool was_thrown = false; @@ -25,7 +86,7 @@ namespace { enable_exceptions(); for (std::size_t i = 0; i < alloc_throw_threshold; ++i) { try { - map_type m(128); + X m(128); } catch (...) { was_thrown = true; } @@ -35,8 +96,12 @@ namespace { BOOST_TEST(was_thrown); } - template void iterator_range(G gen, test::random_generator rg) + template + void iterator_range(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); { @@ -46,7 +111,7 @@ namespace { enable_exceptions(); try { - map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), + X x(values.begin(), values.end(), 0, hasher(1), key_equal(2), allocator_type(3)); } catch (...) { was_thrown = true; @@ -64,7 +129,7 @@ namespace { enable_exceptions(); try { - map_type x(values.begin(), values.end(), allocator_type(3)); + X x(values.begin(), values.end(), allocator_type(3)); } catch (...) { was_thrown = true; } @@ -81,7 +146,7 @@ namespace { enable_exceptions(); try { - map_type x( + X x( values.begin(), values.end(), values.size(), allocator_type(3)); } catch (...) { was_thrown = true; @@ -99,7 +164,7 @@ namespace { enable_exceptions(); try { - map_type x(values.begin(), values.end(), values.size(), hasher(1), + X x(values.begin(), values.end(), values.size(), hasher(1), allocator_type(3)); } catch (...) { was_thrown = true; @@ -111,8 +176,12 @@ namespace { } } - template void copy_constructor(G gen, test::random_generator rg) + template + void copy_constructor(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); { @@ -121,10 +190,10 @@ namespace { bool was_thrown = false; try { - map_type x(values.begin(), values.end(), 0); + X x(values.begin(), values.end(), 0); enable_exceptions(); - map_type y(x); + X y(x); } catch (...) { was_thrown = true; } @@ -140,10 +209,10 @@ namespace { bool was_thrown = false; try { - map_type x(values.begin(), values.end(), 0); + X x(values.begin(), values.end(), 0); enable_exceptions(); - map_type y(x, allocator_type(4)); + X y(x, allocator_type(4)); } catch (...) { was_thrown = true; } @@ -154,20 +223,24 @@ namespace { } } - template void move_constructor(G gen, test::random_generator rg) + template + void move_constructor(X*, GF gen_factory, test::random_generator rg) { - auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); + using allocator_type = typename X::allocator_type; + auto gen = gen_factory.template get(); + auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); + { raii::reset_counts(); bool was_thrown = false; try { - map_type x(values.begin(), values.end(), 0); + X x(values.begin(), values.end(), 0); enable_exceptions(); - map_type y(std::move(x), allocator_type(4)); + X y(std::move(x), allocator_type(4)); } catch (...) { was_thrown = true; } @@ -178,33 +251,13 @@ namespace { } } - UNORDERED_AUTO_TEST (initializer_list_bucket_count) { - using value_type = typename map_type::value_type; + template + void initializer_list_bucket_count(std::pair p) + { + using value_type = typename X::value_type; + using allocator_type = typename X::allocator_type; - std::initializer_list values{ - value_type{raii{0}, raii{0}}, - value_type{raii{1}, raii{1}}, - value_type{raii{2}, raii{2}}, - value_type{raii{3}, raii{3}}, - value_type{raii{4}, raii{4}}, - value_type{raii{5}, raii{5}}, - value_type{raii{6}, raii{6}}, - value_type{raii{6}, raii{6}}, - value_type{raii{7}, raii{7}}, - value_type{raii{8}, raii{8}}, - value_type{raii{9}, raii{9}}, - value_type{raii{10}, raii{10}}, - value_type{raii{9}, raii{9}}, - value_type{raii{8}, raii{8}}, - value_type{raii{7}, raii{7}}, - value_type{raii{6}, raii{6}}, - value_type{raii{5}, raii{5}}, - value_type{raii{4}, raii{4}}, - value_type{raii{3}, raii{3}}, - value_type{raii{2}, raii{2}}, - value_type{raii{1}, raii{1}}, - value_type{raii{0}, raii{0}}, - }; + auto init_list = p.second; { raii::reset_counts(); @@ -213,7 +266,7 @@ namespace { enable_exceptions(); for (std::size_t i = 0; i < throw_threshold; ++i) { try { - map_type x(values, 0, hasher(1), key_equal(2), allocator_type(3)); + X x(init_list, 0, hasher(1), key_equal(2), allocator_type(3)); } catch (...) { ++num_throws; } @@ -231,7 +284,7 @@ namespace { enable_exceptions(); for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) { try { - map_type x(values, allocator_type(3)); + X x(init_list, allocator_type(3)); } catch (...) { ++num_throws; } @@ -249,7 +302,7 @@ namespace { enable_exceptions(); for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) { try { - map_type x(values, values.size() * 2, allocator_type(3)); + X x(init_list, init_list.size() * 2, allocator_type(3)); } catch (...) { ++num_throws; } @@ -267,7 +320,7 @@ namespace { enable_exceptions(); for (std::size_t i = 0; i < throw_threshold; ++i) { try { - map_type x(values, values.size() * 2, hasher(1), allocator_type(3)); + X x(init_list, init_list.size() * 2, hasher(1), allocator_type(3)); } catch (...) { ++num_throws; } @@ -285,20 +338,31 @@ using test::limited_range; using test::sequential; // clang-format off +UNORDERED_TEST( + bucket_constructor, + ((test_map)(test_set))) + UNORDERED_TEST( iterator_range, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( copy_constructor, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential))) UNORDERED_TEST( move_constructor, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential))) + +UNORDERED_TEST( + initializer_list_bucket_count, + ((test_map_and_init_list)(test_set_and_init_list))) // clang-format on RUN_TESTS() diff --git a/test/cfoa/exception_erase_tests.cpp b/test/cfoa/exception_erase_tests.cpp index 11d167e7..32a51f5e 100644 --- a/test/cfoa/exception_erase_tests.cpp +++ b/test/cfoa/exception_erase_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "exception_helpers.hpp" #include +#include #include @@ -15,6 +17,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_erased{0}; auto const old_size = x.size(); @@ -27,9 +32,9 @@ namespace { enable_exceptions(); thread_runner(values, [&values, &num_erased, &x](boost::span) { - for (auto const& k : values) { + for (auto const& v : values) { try { - auto count = x.erase(k.first); + auto count = x.erase(get_key(v)); BOOST_TEST_LE(count, 1u); BOOST_TEST_GE(count, 0u); @@ -46,7 +51,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } lvalue_eraser; @@ -55,6 +61,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; std::atomic num_erased{0}; @@ -68,8 +83,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -77,17 +92,17 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); enable_exceptions(); thread_runner(values, [&num_erased, &x, threshold](boost::span s) { - for (auto const& k : s) { + for (auto const& v : s) { try { - auto count = x.erase_if(k.first, - [threshold](value_type& v) { return v.second.x_ > threshold; }); + auto count = x.erase_if(get_key(v), + [threshold](arg_type& w) { return get_value(w).x_ > threshold; }); num_erased += count; BOOST_TEST_LE(count, 1u); BOOST_TEST_GE(count, 0u); @@ -104,7 +119,8 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased); + BOOST_TEST_EQ( + raii::destructor, old_d + value_type_cardinality * num_erased); } } lvalue_eraser_if; @@ -113,6 +129,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; auto const old_size = x.size(); @@ -124,8 +149,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -133,7 +158,7 @@ namespace { auto expected_erasures = 0u; x.visit_all([&expected_erasures, threshold](value_type const& v) { - if (v.second.x_ > threshold) { + if (get_value(v).x_ > threshold) { ++expected_erasures; } }); @@ -142,14 +167,14 @@ namespace { thread_runner(values, [&x, threshold](boost::span /* s */) { for (std::size_t i = 0; i < 256; ++i) { try { - x.erase_if([threshold](value_type& v) { + x.erase_if([threshold](arg_type& v) { static std::atomic c{0}; auto t = ++c; if (should_throw && (t % throw_threshold == 0)) { throw exception_tag{}; } - return v.second.x_ > threshold; + return get_value(v).x_ > threshold; }); } catch (...) { } @@ -161,7 +186,9 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size())); + BOOST_TEST_EQ( + raii::destructor, + old_d + value_type_cardinality * (old_size - x.size())); } } erase_if; @@ -170,6 +197,15 @@ namespace { template void operator()(std::vector& values, X& x) { using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; auto const old_size = x.size(); @@ -181,8 +217,8 @@ namespace { auto max = 0; x.visit_all([&max](value_type const& v) { - if (v.second.x_ > max) { - max = v.second.x_; + if (get_value(v).x_ > max) { + max = get_value(v).x_; } }); @@ -192,14 +228,14 @@ namespace { thread_runner(values, [&x, threshold](boost::span /* s */) { for (std::size_t i = 0; i < 256; ++i) { try { - boost::unordered::erase_if(x, [threshold](value_type& v) { + boost::unordered::erase_if(x, [threshold](arg_type& v) { static std::atomic c{0}; auto t = ++c; if (should_throw && (t % throw_threshold == 0)) { throw exception_tag{}; } - return v.second.x_ > threshold; + return get_value(v).x_ > threshold; }); } catch (...) { @@ -212,16 +248,18 @@ namespace { BOOST_TEST_EQ(raii::copy_constructor, old_cc); BOOST_TEST_EQ(raii::move_constructor, old_mc); - BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size())); + BOOST_TEST_EQ( + raii::destructor, + old_d + value_type_cardinality * (old_size - x.size())); } } free_fn_erase_if; - template - void erase(X*, G gen, F eraser, test::random_generator rg) + template + void erase(X*, GF gen_factory, F eraser, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); @@ -231,13 +269,13 @@ namespace { x.insert(v); } - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); BOOST_TEST_EQ(raii::destructor, 0u); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); eraser(values, x); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); @@ -245,6 +283,8 @@ namespace { boost::unordered::concurrent_flat_map > >* map; + boost::unordered::concurrent_flat_set >* set; } // namespace @@ -255,8 +295,9 @@ using test::sequential; // clang-format off UNORDERED_TEST( erase, - ((map)) - ((exception_value_type_generator)(exception_init_type_generator)) + ((map)(set)) + ((exception_value_type_generator_factory) + (exception_init_type_generator_factory)) ((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)) ((default_generator)(sequential)(limited_range))) diff --git a/test/cfoa/exception_helpers.hpp b/test/cfoa/exception_helpers.hpp index 87528558..5afe1c24 100644 --- a/test/cfoa/exception_helpers.hpp +++ b/test/cfoa/exception_helpers.hpp @@ -1,14 +1,20 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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_TEST_CFOA_EXCEPTION_HELPERS_HPP +#define BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP + #include "../helpers/generators.hpp" #include "../helpers/test.hpp" +#include "common_helpers.hpp" #include #include #include #include +#include #include #include @@ -308,16 +314,54 @@ std::size_t hash_value(raii const& r) noexcept return hasher(r.x_); } -struct exception_value_type_generator_type +template +struct exception_value_generator { - std::pair operator()(test::random_generator rg) + using value_type = raii; + + value_type operator()(test::random_generator rg) + { + int* p = nullptr; + int a = generate(p, rg); + return value_type(a); + } +}; + +template +struct exception_value_generator > +{ + static constexpr bool const_key = std::is_const::value; + static constexpr bool const_mapped = std::is_const::value; + using value_type = std::pair< + typename std::conditional::type, + typename std::conditional::type>; + + value_type operator()(test::random_generator rg) { int* p = nullptr; int a = generate(p, rg); int b = generate(p, rg); return std::make_pair(raii{a}, raii{b}); } -} exception_value_type_generator; +}; + +struct exception_value_type_generator_factory_type +{ + template + exception_value_generator get() + { + return {}; + } +} exception_value_type_generator_factory; + +struct exception_init_type_generator_factory_type +{ + template + exception_value_generator get() + { + return {}; + } +} exception_init_type_generator_factory; struct exception_init_type_generator_type { @@ -388,29 +432,6 @@ template void thread_runner(std::vector& values, F f) } } -template -void test_matches_reference(X const& x, Y const& reference_map) -{ - using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); - })); -} - -template -void test_fuzzy_matches_reference( - X const& x, Y const& reference_map, test::random_generator rg) -{ - using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - if (rg == test::sequential) { - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); - } - })); -} - template using span_value_type = typename T::value_type; void check_raii_counts() @@ -442,3 +463,5 @@ auto make_random_values(std::size_t count, F f) -> std::vector } return v; } + +#endif // BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP diff --git a/test/cfoa/exception_insert_tests.cpp b/test/cfoa/exception_insert_tests.cpp index 4804caa6..d9b22c31 100644 --- a/test/cfoa/exception_insert_tests.cpp +++ b/test/cfoa/exception_insert_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "exception_helpers.hpp" #include +#include #include @@ -84,6 +86,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + x.reserve(values.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); @@ -92,11 +97,19 @@ namespace { rvalue_inserter_type::operator()(values, x); if (std::is_same::value) { - BOOST_TEST_EQ(raii::copy_constructor, x.size()); - BOOST_TEST_EQ(raii::move_constructor, x.size()); + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_EQ(raii::move_constructor, x.size()); + } + else { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_EQ(raii::move_constructor, x.size()); + } } else { BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_EQ(raii::move_constructor, 2 * x.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * x.size()); } } } norehash_rvalue_inserter; @@ -246,6 +259,13 @@ namespace { { template void operator()(std::vector& values, X& x) { + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_inserts{0}; enable_exceptions(); @@ -253,7 +273,7 @@ namespace { for (auto& r : s) { try { bool b = - x.insert_or_visit(r, [](typename X::value_type& v) { (void)v; }); + x.insert_or_visit(r, [](arg_type& v) { (void)v; }); if (b) { ++num_inserts; @@ -306,6 +326,13 @@ namespace { { template void operator()(std::vector& values, X& x) { + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_inserts{0}; enable_exceptions(); @@ -313,7 +340,7 @@ namespace { for (auto& r : s) { try { bool b = x.insert_or_visit( - std::move(r), [](typename X::value_type& v) { (void)v; }); + std::move(r), [](arg_type& v) { (void)v; }); if (b) { ++num_inserts; @@ -377,14 +404,14 @@ namespace { } } iterator_range_insert_or_visit; - template - void insert(X*, G gen, F inserter, test::random_generator rg) + template + void insert(X*, GF gen_factory, F inserter, test::random_generator rg) { disable_exceptions(); + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { @@ -392,13 +419,15 @@ namespace { inserter(values, x); - test_fuzzy_matches_reference(x, reference_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); } boost::unordered::concurrent_flat_map > >* map; + boost::unordered::concurrent_flat_set >* set; } // namespace @@ -409,8 +438,9 @@ using test::sequential; // clang-format off UNORDERED_TEST( insert, - ((map)) - ((exception_value_type_generator)(exception_init_type_generator)) + ((map)(set)) + ((exception_value_type_generator_factory) + (exception_init_type_generator_factory)) ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter) (norehash_lvalue_inserter)(norehash_rvalue_inserter) (lvalue_insert_or_cvisit)(lvalue_insert_or_visit) @@ -421,7 +451,7 @@ UNORDERED_TEST( UNORDERED_TEST( insert, ((map)) - ((exception_init_type_generator)) + ((exception_init_type_generator_factory)) ((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign) (rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign)) ((default_generator)(sequential)(limited_range))) diff --git a/test/cfoa/exception_merge_tests.cpp b/test/cfoa/exception_merge_tests.cpp index 0f54eb27..d9e0cc2f 100644 --- a/test/cfoa/exception_merge_tests.cpp +++ b/test/cfoa/exception_merge_tests.cpp @@ -1,29 +1,38 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "exception_helpers.hpp" #include +#include #include -using allocator_type = stateful_allocator >; - using hasher = stateful_hash; using key_equal = stateful_key_equal; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; + +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; namespace { test::seed_t initialize_seed(223333016); - template void merge(G gen, test::random_generator rg) + template + void merge(X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); @@ -37,10 +46,10 @@ namespace { for (unsigned i = 0; i < 5 * alloc_throw_threshold; ++i) { disable_exceptions(); - map_type x1(0, hasher(1), key_equal(2), allocator_type(3)); + X x1(0, hasher(1), key_equal(2), allocator_type(3)); x1.insert(begin, mid); - map_type x2(0, hasher(2), key_equal(1), allocator_type(3)); + X x2(0, hasher(2), key_equal(1), allocator_type(3)); x2.insert(mid, end); enable_exceptions(); @@ -51,8 +60,8 @@ namespace { } disable_exceptions(); - test_fuzzy_matches_reference(x1, reference_map, rg); - test_fuzzy_matches_reference(x2, reference_map, rg); + test_fuzzy_matches_reference(x1, reference_cont, rg); + test_fuzzy_matches_reference(x2, reference_cont, rg); } BOOST_TEST_GT(num_throws, 0u); @@ -70,7 +79,8 @@ using test::sequential; // clang-format off UNORDERED_TEST( merge, - ((exception_value_type_generator)) + ((test_map)(test_set)) + ((exception_value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/fwd_tests.cpp b/test/cfoa/fwd_tests.cpp index dced611e..5b37dddd 100644 --- a/test/cfoa/fwd_tests.cpp +++ b/test/cfoa/fwd_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include #include +#include #include test::seed_t initialize_seed{32304628}; @@ -34,37 +36,89 @@ bool unequal_call(boost::unordered::concurrent_flat_map& x1, return x1 != x2; } -#include - -using map_type = boost::unordered::concurrent_flat_map; - -#if !defined(BOOST_CLANG_VERSION) || \ - BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 30700) || \ - BOOST_WORKAROUND(BOOST_CLANG_VERSION, >= 30800) -// clang-3.7 seems to have a codegen bug here so we workaround it -UNORDERED_AUTO_TEST (fwd_swap_call) { - map_type x1, x2; - swap_call(x1, x2); +template +void swap_call(boost::unordered::concurrent_flat_set& x1, + boost::unordered::concurrent_flat_set& x2) +{ + swap(x1, x2); } -#endif +template +bool equal_call(boost::unordered::concurrent_flat_set& x1, + boost::unordered::concurrent_flat_set& x2) +{ + return x1 == x2; +} -UNORDERED_AUTO_TEST (fwd_equal_call) { - map_type x1, x2; +template +bool unequal_call(boost::unordered::concurrent_flat_set& x1, + boost::unordered::concurrent_flat_set& x2) +{ + return x1 != x2; +} + +#include +#include + +using map_type = boost::unordered::concurrent_flat_map; +using set_type = boost::unordered::concurrent_flat_map; + +map_type* test_map; +set_type* test_set; + +template +void fwd_swap_call(X*) +{ +#if !defined(BOOST_CLANG_VERSION) || \ + BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 30700) || \ + BOOST_WORKAROUND(BOOST_CLANG_VERSION, >= 30800) +// clang-3.7 seems to have a codegen bug here so we workaround it + + X x1, x2; + swap_call(x1, x2); +#endif +} + +template +void fwd_equal_call(X*) +{ + X x1, x2; BOOST_TEST(equal_call(x1, x2)); } -UNORDERED_AUTO_TEST (fwd_unequal_call) { - map_type x1, x2; +template +void fwd_unequal_call(X*) +{ + X x1, x2; BOOST_TEST_NOT(unequal_call(x1, x2)); } // this isn't the best place for this test but it's better than introducing a // new file -UNORDERED_AUTO_TEST (max_size) { - map_type x1; +template +void max_size(X*) +{ + X x1; BOOST_TEST_EQ( - x1.max_size(), std::numeric_limits::max()); + x1.max_size(), std::numeric_limits::max()); } +// clang-format off +UNORDERED_TEST( + fwd_swap_call, + ((test_map)(test_set))) + +UNORDERED_TEST( + fwd_equal_call, + ((test_map)(test_set))) + +UNORDERED_TEST( + fwd_unequal_call, + ((test_map)(test_set))) + +UNORDERED_TEST( + max_size, + ((test_map)(test_set))) +// clang-format on + RUN_TESTS() diff --git a/test/cfoa/helpers.hpp b/test/cfoa/helpers.hpp index 79b91bb0..307a1d7f 100644 --- a/test/cfoa/helpers.hpp +++ b/test/cfoa/helpers.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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) @@ -7,11 +8,15 @@ #include "../helpers/generators.hpp" #include "../helpers/test.hpp" +#include "common_helpers.hpp" #include #include #include +#include +#include #include +#include #include #include @@ -328,27 +333,48 @@ auto make_random_values(std::size_t count, F f) -> std::vector return v; } -struct value_type_generator_type +template +struct value_generator { - std::pair operator()(test::random_generator rg) - { - int* p = nullptr; - int a = generate(p, rg); - int b = generate(p, rg); - return std::make_pair(raii{a}, raii{b}); - } -} value_type_generator; + using value_type = raii; -struct init_type_generator_type + value_type operator()(test::random_generator rg) + { + int* p = nullptr; + int a = generate(p, rg); + return value_type(a); + } +}; + +template +struct value_generator > { - std::pair operator()(test::random_generator rg) + static constexpr bool const_key = std::is_const::value; + static constexpr bool const_mapped = std::is_const::value; + using value_type = std::pair< + typename std::conditional::type, + typename std::conditional::type>; + + value_type operator()(test::random_generator rg) { int* p = nullptr; int a = generate(p, rg); int b = generate(p, rg); return std::make_pair(raii{a}, raii{b}); } -} init_type_generator; +}; + +struct value_type_generator_factory_type +{ + template + value_generator get() { return {}; } +} value_type_generator_factory; + +struct init_type_generator_factory_type +{ + template + value_generator get() { return {}; } +} init_type_generator_factory; template std::vector > split( @@ -408,29 +434,6 @@ template void thread_runner(std::vector& values, F f) } } -template -void test_matches_reference(X const& x, Y const& reference_map) -{ - using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); - })); -} - -template -void test_fuzzy_matches_reference( - X const& x, Y const& reference_map, test::random_generator rg) -{ - using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - if (rg == test::sequential) { - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); - } - })); -} - template using span_value_type = typename T::value_type; void check_raii_counts() @@ -642,4 +645,4 @@ public: fancy_allocator& operator=(fancy_allocator const&) { return *this; } }; -#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP \ No newline at end of file +#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index b3bd4d51..e1b53e68 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -1,18 +1,27 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include #include struct raii_convertible { - int x, y; - raii_convertible(int x_, int y_) : x{x_}, y{y_} {} + int x = 0, y = 0 ; + template + raii_convertible(T const & t) : x{t.x_} {} + + template + raii_convertible(std::pair const & p) : x{p.first.x_}, y{p.second.x_} + {} + + operator raii() { return {x}; } operator std::pair() { return {x, y}; } }; @@ -23,6 +32,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_inserts{0}; thread_runner(values, [&x, &num_inserts](boost::span s) { for (auto const& r : s) { @@ -33,7 +45,8 @@ namespace { } }); BOOST_TEST_EQ(num_inserts, x.size()); - BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); } @@ -43,9 +56,13 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + x.reserve(values.size()); lvalue_inserter_type::operator()(values, x); - BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * x.size()); BOOST_TEST_EQ(raii::move_constructor, 0u); } } norehash_lvalue_inserter; @@ -67,7 +84,8 @@ namespace { }); BOOST_TEST_EQ(num_inserts, x.size()); - if (std::is_same::value) { + if (std::is_same::value && + !std::is_same::value) { BOOST_TEST_EQ(raii::copy_constructor, x.size()); } else { BOOST_TEST_EQ(raii::copy_constructor, 0u); @@ -82,6 +100,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + x.reserve(values.size()); BOOST_TEST_EQ(raii::copy_constructor, 0u); @@ -90,11 +111,19 @@ namespace { rvalue_inserter_type::operator()(values, x); if (std::is_same::value) { - BOOST_TEST_EQ(raii::copy_constructor, x.size()); - BOOST_TEST_EQ(raii::move_constructor, x.size()); + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_EQ(raii::move_constructor, x.size()); + } + else { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_EQ(raii::move_constructor, x.size()); + } } else { BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_EQ(raii::move_constructor, 2 * x.size()); + BOOST_TEST_EQ( + raii::move_constructor, value_type_cardinality * x.size()); } } } norehash_rvalue_inserter; @@ -103,17 +132,21 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::vector values2; values2.reserve(values.size()); - for (auto const& p : values) { - values2.push_back(raii_convertible(p.first.x_, p.second.x_)); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); } thread_runner(values2, [&x](boost::span s) { x.insert(s.begin(), s.end()); }); - BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); #if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \ BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500) // some versions of old gcc have trouble eliding copies here @@ -253,6 +286,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { @@ -273,7 +309,8 @@ namespace { BOOST_TEST_EQ(num_invokes, values.size() - x.size()); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * x.size()); // don't check move construction count here because of rehashing BOOST_TEST_GT(raii::move_constructor, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); @@ -284,12 +321,22 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { for (auto& r : s) { bool b = - x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) { + x.insert_or_visit(r, [&num_invokes](arg_type& v) { (void)v; ++num_invokes; }); @@ -304,7 +351,7 @@ namespace { BOOST_TEST_EQ(num_invokes, values.size() - x.size()); BOOST_TEST_EQ(raii::default_constructor, 0u); - BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size()); // don't check move construction count here because of rehashing BOOST_TEST_GT(raii::move_constructor, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u); @@ -315,6 +362,9 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { @@ -337,11 +387,19 @@ namespace { BOOST_TEST_EQ(raii::default_constructor, 0u); if (std::is_same::value) { - BOOST_TEST_EQ(raii::copy_constructor, x.size()); - BOOST_TEST_GE(raii::move_constructor, x.size()); + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } + else { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } } else { BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + BOOST_TEST_GE( + raii::move_constructor, value_type_cardinality * x.size()); } } } rvalue_insert_or_cvisit; @@ -350,12 +408,22 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_inserts{0}; std::atomic num_invokes{0}; thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { for (auto& r : s) { bool b = x.insert_or_visit( - std::move(r), [&num_invokes](typename X::value_type& v) { + std::move(r), [&num_invokes](arg_type& v) { (void)v; ++num_invokes; }); @@ -371,11 +439,19 @@ namespace { BOOST_TEST_EQ(raii::default_constructor, 0u); if (std::is_same::value) { - BOOST_TEST_EQ(raii::copy_constructor, x.size()); - BOOST_TEST_GE(raii::move_constructor, x.size()); + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } + else { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } } else { BOOST_TEST_EQ(raii::copy_constructor, 0u); - BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + BOOST_TEST_GE( + raii::move_constructor, value_type_cardinality * x.size()); } } } rvalue_insert_or_visit; @@ -384,10 +460,13 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::vector values2; values2.reserve(values.size()); - for (auto const& p : values) { - values2.push_back(raii_convertible(p.first.x_, p.second.x_)); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); } std::atomic num_invokes{0}; @@ -402,7 +481,8 @@ namespace { BOOST_TEST_EQ(num_invokes, values.size() - x.size()); - BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); #if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \ BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500) // skip test @@ -417,10 +497,13 @@ namespace { { template void operator()(std::vector& values, X& x) { + static constexpr auto value_type_cardinality = + value_cardinality::value; + std::vector values2; values2.reserve(values.size()); - for (auto const& p : values) { - values2.push_back(raii_convertible(p.first.x_, p.second.x_)); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); } std::atomic num_invokes{0}; @@ -435,7 +518,8 @@ namespace { BOOST_TEST_EQ(num_invokes, values.size() - x.size()); - BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size()); + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); #if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \ BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500) // skip test @@ -446,12 +530,12 @@ namespace { } } iterator_range_insert_or_visit; - template - void insert(X*, G gen, F inserter, test::random_generator rg) + template + void insert(X*, GF gen_factory, F inserter, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); { @@ -459,13 +543,13 @@ namespace { inserter(values, x); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); using value_type = typename X::value_type; - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); if (rg == test::sequential) { - BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); } })); } @@ -480,39 +564,21 @@ namespace { raii::destructor); } - template void insert_initializer_list(X*) + template + void insert_initializer_list(std::pair p) { using value_type = typename X::value_type; + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; - std::initializer_list values{ - value_type{raii{0}, raii{0}}, - value_type{raii{1}, raii{1}}, - value_type{raii{2}, raii{2}}, - value_type{raii{3}, raii{3}}, - value_type{raii{4}, raii{4}}, - value_type{raii{5}, raii{5}}, - value_type{raii{6}, raii{6}}, - value_type{raii{6}, raii{6}}, - value_type{raii{7}, raii{7}}, - value_type{raii{8}, raii{8}}, - value_type{raii{9}, raii{9}}, - value_type{raii{10}, raii{10}}, - value_type{raii{9}, raii{9}}, - value_type{raii{8}, raii{8}}, - value_type{raii{7}, raii{7}}, - value_type{raii{6}, raii{6}}, - value_type{raii{5}, raii{5}}, - value_type{raii{4}, raii{4}}, - value_type{raii{3}, raii{3}}, - value_type{raii{2}, raii{2}}, - value_type{raii{1}, raii{1}}, - value_type{raii{0}, raii{0}}, - }; - + auto init_list = p.second; std::vector dummy; - - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container( + init_list.begin(), init_list.end()); raii::reset_counts(); { @@ -520,13 +586,13 @@ namespace { X x; thread_runner( - dummy, [&x, &values](boost::span) { x.insert(values); }); + dummy, [&x, &init_list](boost::span) { x.insert(init_list); }); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); })); } @@ -549,27 +615,27 @@ namespace { X x; - thread_runner(dummy, [&x, &values, &num_invokes](boost::span) { - x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) { + thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span) { + x.insert_or_visit(init_list, [&num_invokes](arg_type& v) { (void)v; ++num_invokes; }); x.insert_or_cvisit( - values, [&num_invokes](typename X::value_type const& v) { + init_list, [&num_invokes](typename X::value_type const& v) { (void)v; ++num_invokes; }); }); - BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) + - (num_threads - 1) * values.size() + - num_threads * values.size()); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) + + (num_threads - 1) * init_list.size() + + num_threads * init_list.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); })); } @@ -606,6 +672,64 @@ namespace { std::equal_to, fancy_allocator > >* fancy_map; + boost::unordered::concurrent_flat_set* set; + boost::unordered::concurrent_flat_set, + std::equal_to, fancy_allocator > >* + fancy_set; + + std::initializer_list > map_init_list{ + {raii{0}, raii{0}}, + {raii{1}, raii{1}}, + {raii{2}, raii{2}}, + {raii{3}, raii{3}}, + {raii{4}, raii{4}}, + {raii{5}, raii{5}}, + {raii{6}, raii{6}}, + {raii{6}, raii{6}}, + {raii{7}, raii{7}}, + {raii{8}, raii{8}}, + {raii{9}, raii{9}}, + {raii{10}, raii{10}}, + {raii{9}, raii{9}}, + {raii{8}, raii{8}}, + {raii{7}, raii{7}}, + {raii{6}, raii{6}}, + {raii{5}, raii{5}}, + {raii{4}, raii{4}}, + {raii{3}, raii{3}}, + {raii{2}, raii{2}}, + {raii{1}, raii{1}}, + {raii{0}, raii{0}}, + }; + + std::initializer_list set_init_list{ + raii{0}, + raii{1}, + raii{2}, + raii{3}, + raii{4}, + raii{5}, + raii{6}, + raii{6}, + raii{7}, + raii{8}, + raii{9}, + raii{10}, + raii{9}, + raii{8}, + raii{7}, + raii{6}, + raii{5}, + raii{4}, + raii{3}, + raii{2}, + raii{1}, + raii{0}, + }; + + auto map_and_init_list=std::make_pair(map,map_init_list); + auto set_and_init_list=std::make_pair(set,set_init_list); + } // namespace using test::default_generator; @@ -615,12 +739,12 @@ using test::sequential; // clang-format off UNORDERED_TEST( insert_initializer_list, - ((map))) + ((map_and_init_list)(set_and_init_list))) UNORDERED_TEST( insert, - ((map)(fancy_map)) - ((value_type_generator)(init_type_generator)) + ((map)(fancy_map)(set)(fancy_set)) + ((value_type_generator_factory)(init_type_generator_factory)) ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter) (norehash_lvalue_inserter)(norehash_rvalue_inserter) (lvalue_insert_or_cvisit)(lvalue_insert_or_visit) @@ -631,7 +755,7 @@ UNORDERED_TEST( UNORDERED_TEST( insert, ((map)) - ((init_type_generator)) + ((init_type_generator_factory)) ((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign) (rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign)) ((default_generator)(sequential)(limited_range))) @@ -639,7 +763,7 @@ UNORDERED_TEST( UNORDERED_TEST( insert, ((trans_map)) - ((init_type_generator)) + ((init_type_generator_factory)) ((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/merge_tests.cpp b/test/cfoa/merge_tests.cpp index 60d35065..eab8cc29 100644 --- a/test/cfoa/merge_tests.cpp +++ b/test/cfoa/merge_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include test::seed_t initialize_seed{402031699}; @@ -14,12 +16,25 @@ using test::sequential; using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; -using map_type = boost::unordered::concurrent_flat_map; +using map_type = boost::unordered::concurrent_flat_map > >; +using map2_type = boost::unordered::concurrent_flat_map, std::equal_to, + stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; +using set2_type = boost::unordered::concurrent_flat_set, + std::equal_to, stateful_allocator >; + +map_type* test_map; +map2_type* test_map2; +auto test_maps=std::make_pair(test_map,test_map2); + +set_type* test_set; +set2_type* test_set2; +auto test_sets=std::make_pair(test_set,test_set2); struct { @@ -40,18 +55,23 @@ struct } rvalue_merge; namespace { - template - void merge_tests(F merger, G gen, test::random_generator rg) + template + void merge_tests( + std::pair, F merger, GF gen_factory, test::random_generator rg) { - auto values = make_random_values(1024 * 8, [&] { return gen(rg); }); + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; - auto ref_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto gen = gen_factory.template get(); + auto values = make_random_values(1024 * 8, [&] { return gen(rg); }); + auto reference_cont = reference_container(values.begin(), values.end()); { raii::reset_counts(); - map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3)); + X x(values.size(), hasher(1), key_equal(2), allocator_type(3)); auto const old_cc = +raii::copy_constructor; @@ -59,48 +79,50 @@ namespace { std::atomic num_merged{0}; thread_runner(values, [&x, &expected_copies, &num_merged, merger]( - boost::span s) { - using map2_type = boost::unordered::concurrent_flat_map, std::equal_to, allocator_type>; - - map2_type y(s.size(), allocator_type(3)); + boost::span s) { + Y y(s.size(), allocator_type(3)); for (auto const& v : s) { y.insert(v); } - expected_copies += 2 * y.size(); + expected_copies += value_type_cardinality * y.size(); BOOST_TEST(x.get_allocator() == y.get_allocator()); num_merged += merger(x, y); }); BOOST_TEST_EQ(raii::copy_constructor, old_cc + expected_copies); - BOOST_TEST_EQ(raii::move_constructor, 2 * ref_map.size()); - BOOST_TEST_EQ(+num_merged, ref_map.size()); + BOOST_TEST_EQ( + raii::move_constructor, + value_type_cardinality * reference_cont.size()); + BOOST_TEST_EQ(+num_merged, reference_cont.size()); - test_fuzzy_matches_reference(x, ref_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); } - template - void insert_and_merge_tests(G gen, test::random_generator rg) + template + void insert_and_merge_tests( + std::pair, GF gen_factory, test::random_generator rg) { - using map2_type = boost::unordered::concurrent_flat_map, std::equal_to, allocator_type>; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + auto gen = gen_factory.template get(); auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); }); - auto ref_map = boost::unordered_flat_map(); - ref_map.insert(vals1.begin(), vals1.end()); - ref_map.insert(vals2.begin(), vals2.end()); + auto reference_cont = reference_container(); + reference_cont.insert(vals1.begin(), vals1.end()); + reference_cont.insert(vals2.begin(), vals2.end()); { raii::reset_counts(); - map_type x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3)); + X x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3)); - map2_type x2(2 * vals1.size(), allocator_type(3)); + Y x2(2 * vals1.size(), allocator_type(3)); std::thread t1, t2, t3; boost::compat::latch l(2); @@ -190,12 +212,13 @@ namespace { if (num_merges > 0) { // num merges is 0 most commonly in the cast of the limited_range // generator as both maps will contains keys from 0 to 99 - BOOST_TEST_EQ(+raii::move_constructor, 2 * num_merges); + BOOST_TEST_EQ( + +raii::move_constructor, value_type_cardinality * num_merges); BOOST_TEST_GE(call_count, 1u); } x1.merge(x2); - test_fuzzy_matches_reference(x1, ref_map, rg); + test_fuzzy_matches_reference(x1, reference_cont, rg); } check_raii_counts(); @@ -206,13 +229,15 @@ namespace { // clang-format off UNORDERED_TEST( merge_tests, + ((test_maps)(test_sets)) ((lvalue_merge)(rvalue_merge)) - ((value_type_generator)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( insert_and_merge_tests, - ((value_type_generator)) + ((test_maps)(test_sets)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/reentrancy_check_test.cpp b/test/cfoa/reentrancy_check_test.cpp index 69ef5efc..0bbb41b0 100644 --- a/test/cfoa/reentrancy_check_test.cpp +++ b/test/cfoa/reentrancy_check_test.cpp @@ -12,23 +12,36 @@ namespace boost { // Caveat lector: a proper handler shouldn't throw as it may be executed // within a noexcept function. -void assertion_failed_msg( - char const*, char const*, char const*, char const*, long) -{ - reentrancy_detected = true; - throw 0; -} + void assertion_failed_msg( + char const*, char const*, char const*, char const*, long) + { + reentrancy_detected = true; + throw 0; + } -void assertion_failed(char const*, char const*, char const*, long) // LCOV_EXCL_START -{ - std::abort(); -} // LCOV_EXCL_STOP + // LCOV_EXCL_START + void assertion_failed(char const*, char const*, char const*, long) + { + std::abort(); + } + // LCOV_EXCL_STOP -} +} // namespace boost + +#include "helpers.hpp" #include +#include #include +using test::default_generator; + +using map_type = boost::unordered::concurrent_flat_map; +using set_type = boost::unordered::concurrent_flat_set; + +map_type* test_map; +set_type* test_set; + template void detect_reentrancy(F f) { @@ -40,40 +53,61 @@ void detect_reentrancy(F f) BOOST_TEST(reentrancy_detected); } -int main() -{ - using map = boost::concurrent_flat_map; - using value_type = typename map::value_type; +namespace { + template + void reentrancy_tests(X*, GF gen_factory, test::random_generator rg) + { + using key_type = typename X::key_type; - map m1, m2; - m1.emplace(0, 0); - m2.emplace(1, 0); + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; - detect_reentrancy([&] { - m1.visit_all([&](value_type&) { (void)m1.contains(0); }); - }); // LCOV_EXCL_LINE + auto gen = gen_factory.template get(); + auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - detect_reentrancy([&] { - m1.visit_all([&](value_type&) { m1.rehash(0); }); - }); // LCOV_EXCL_LINE + X x1, x2; + x1.insert(values.begin(), values.end()); + x2.insert(values.begin(), values.end()); - detect_reentrancy([&] { - m1.visit_all([&](value_type&) { - m2.visit_all([&](value_type&) { - m1=m2; - }); // LCOV_EXCL_START + detect_reentrancy([&] { + x1.visit_all([&](arg_type&) { (void)x1.contains(key_type()); }); + }); // LCOV_EXCL_LINE + + detect_reentrancy([&] { + x1.visit_all([&](arg_type&) { x1.rehash(0); }); + }); // LCOV_EXCL_LINE + + detect_reentrancy([&] { + x1.visit_all([&](arg_type&) { + x2.visit_all([&](arg_type&) { + x1=x2; + }); // LCOV_EXCL_START + }); }); - }); - // LCOV_EXCL_STOP + // LCOV_EXCL_STOP - detect_reentrancy([&] { - m1.visit_all([&](value_type&) { - m2.visit_all([&](value_type&) { - m2=m1; - }); // LCOV_EXCL_START + detect_reentrancy([&] { + x1.visit_all([&](arg_type&) { + x2.visit_all([&](arg_type&) { + x2=x1; + }); // LCOV_EXCL_START + }); }); - }); - // LCOV_EXCL_STOP + // LCOV_EXCL_STOP + } - return boost::report_errors(); -} +} // namespace + +// clang-format off +UNORDERED_TEST( + reentrancy_tests, + ((test_map)(test_set)) + ((value_type_generator_factory)) + ((default_generator))) +// clang-format on + +RUN_TESTS() diff --git a/test/cfoa/rehash_tests.cpp b/test/cfoa/rehash_tests.cpp index 51810f54..fd0b31df 100644 --- a/test/cfoa/rehash_tests.cpp +++ b/test/cfoa/rehash_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include using test::default_generator; using test::limited_range; @@ -12,18 +14,25 @@ using test::sequential; using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; +using set_type = boost::unordered::concurrent_flat_set >; + +map_type* test_map; +set_type* test_set; namespace { test::seed_t initialize_seed{748775921}; - UNORDERED_AUTO_TEST (rehash_no_insert) { - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + template + void rehash_no_insert(X*) + { + using allocator_type = typename X::allocator_type; + + X x(0, hasher(1), key_equal(2), allocator_type(3)); BOOST_TEST_EQ(x.bucket_count(), 0u); x.rehash(1024); @@ -37,10 +46,13 @@ namespace { BOOST_TEST_EQ(x.bucket_count(), 0u); } - UNORDERED_AUTO_TEST (reserve_no_insert) { - using size_type = map_type::size_type; + template + void reserve_no_insert(X*) + { + using allocator_type = typename X::allocator_type; + using size_type = typename X::size_type; - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); auto f = [&x](double c) { return static_cast(std::ceil(c / x.max_load_factor())); @@ -59,9 +71,13 @@ namespace { BOOST_TEST_EQ(x.bucket_count(), f(0.0)); } - template - void insert_and_erase_with_rehash(G gen, test::random_generator rg) + template + void insert_and_erase_with_rehash( + X*, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); auto erase_indices = std::vector(vals1.size()); @@ -70,13 +86,13 @@ namespace { } shuffle_values(erase_indices); - auto ref_map = boost::unordered_flat_map(); - ref_map.insert(vals1.begin(), vals1.end()); + auto reference_cont = reference_container(); + reference_cont.insert(vals1.begin(), vals1.end()); { raii::reset_counts(); - map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + X x(0, hasher(1), key_equal(2), allocator_type(3)); std::thread t1, t2, t3; boost::compat::latch l(2); @@ -121,7 +137,7 @@ namespace { for (std::size_t idx = 0; idx < erase_indices.size(); ++idx) { auto const& val = vals1[erase_indices[idx]]; - x.erase(val.first); + x.erase(get_key(val)); if (idx % 100 == 0) { std::this_thread::yield(); } @@ -161,7 +177,7 @@ namespace { BOOST_TEST_GE(call_count, 1u); - test_fuzzy_matches_reference(x, ref_map, rg); + test_fuzzy_matches_reference(x, reference_cont, rg); } check_raii_counts(); @@ -169,9 +185,18 @@ namespace { } // namespace // clang-format off +UNORDERED_TEST( + rehash_no_insert, + ((test_map)(test_set))) + +UNORDERED_TEST( + reserve_no_insert, + ((test_map)(test_set))) + UNORDERED_TEST( insert_and_erase_with_rehash, - ((value_type_generator)) + ((test_map)(test_set)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/swap_tests.cpp b/test/cfoa/swap_tests.cpp index 264dc460..42408e24 100644 --- a/test/cfoa/swap_tests.cpp +++ b/test/cfoa/swap_tests.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include test::seed_t initialize_seed{996130204}; @@ -56,17 +58,12 @@ template struct pocs_allocator using hasher = stateful_hash; using key_equal = stateful_key_equal; -using allocator_type = stateful_allocator >; using map_type = boost::unordered::concurrent_flat_map; + key_equal, stateful_allocator > >; -using map_value_type = typename map_type::value_type; - -using pocs_allocator_type = pocs_allocator >; - -using pocs_map_type = boost::unordered::concurrent_flat_map; +using set_type = boost::unordered::concurrent_flat_set >; template struct is_nothrow_member_swappable { @@ -75,13 +72,21 @@ template struct is_nothrow_member_swappable }; BOOST_STATIC_ASSERT(is_nothrow_member_swappable< - boost::unordered::concurrent_flat_map, - std::equal_to, std::allocator > > >::value); + replace_allocator >::value); -BOOST_STATIC_ASSERT(is_nothrow_member_swappable::value); +BOOST_STATIC_ASSERT(is_nothrow_member_swappable< + replace_allocator >::value); BOOST_STATIC_ASSERT(!is_nothrow_member_swappable::value); +BOOST_STATIC_ASSERT(is_nothrow_member_swappable< + replace_allocator >::value); + +BOOST_STATIC_ASSERT(is_nothrow_member_swappable< + replace_allocator >::value); + +BOOST_STATIC_ASSERT(!is_nothrow_member_swappable::value); + namespace { struct { @@ -97,31 +102,31 @@ namespace { } } free_fn_swap; - template - void swap_tests(X*, F swapper, G gen, test::random_generator rg) + template + void swap_tests(X*, F swapper, GF gen_factory, test::random_generator rg) { - using allocator = typename X::allocator_type; + using value_type = typename X::value_type; + using allocator_type = typename X::allocator_type; bool const pocs = - boost::allocator_propagate_on_container_swap::type::value; + boost::allocator_propagate_on_container_swap< + allocator_type>::type::value; + auto gen = gen_factory.template get(); auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); }); - auto ref_map1 = - boost::unordered_flat_map(vals1.begin(), vals1.end()); - - auto ref_map2 = - boost::unordered_flat_map(vals2.begin(), vals2.end()); + auto reference_cont1 = reference_container(vals1.begin(), vals1.end()); + auto reference_cont2 = reference_container(vals2.begin(), vals2.end()); { raii::reset_counts(); X x1(vals1.begin(), vals1.end(), vals1.size(), hasher(1), key_equal(2), - allocator(3)); + allocator_type(3)); X x2(vals2.begin(), vals2.end(), vals2.size(), hasher(2), key_equal(1), - pocs ? allocator(4) : allocator(3)); + pocs ? allocator_type(4) : allocator_type(3)); if (pocs) { BOOST_TEST(x1.get_allocator() != x2.get_allocator()); @@ -132,7 +137,7 @@ namespace { auto const old_cc = +raii::copy_constructor; auto const old_mc = +raii::move_constructor; - thread_runner(vals1, [&x1, &x2, swapper](boost::span s) { + thread_runner(vals1, [&x1, &x2, swapper](boost::span s) { (void)s; swapper(x1, x2); @@ -143,20 +148,20 @@ namespace { BOOST_TEST_EQ(raii::move_constructor, old_mc); if (pocs) { - if (x1.get_allocator() == allocator(3)) { - BOOST_TEST(x2.get_allocator() == allocator(4)); + if (x1.get_allocator() == allocator_type(3)) { + BOOST_TEST(x2.get_allocator() == allocator_type(4)); } else { - BOOST_TEST(x1.get_allocator() == allocator(4)); - BOOST_TEST(x2.get_allocator() == allocator(3)); + BOOST_TEST(x1.get_allocator() == allocator_type(4)); + BOOST_TEST(x2.get_allocator() == allocator_type(3)); } } else { - BOOST_TEST(x1.get_allocator() == allocator(3)); + BOOST_TEST(x1.get_allocator() == allocator_type(3)); BOOST_TEST(x1.get_allocator() == x2.get_allocator()); } - if (x1.size() == ref_map1.size()) { - test_matches_reference(x1, ref_map1); - test_matches_reference(x2, ref_map2); + if (x1.size() == reference_cont1.size()) { + test_matches_reference(x1, reference_cont1); + test_matches_reference(x2, reference_cont2); BOOST_TEST_EQ(x1.hash_function(), hasher(1)); BOOST_TEST_EQ(x1.key_eq(), key_equal(2)); @@ -164,8 +169,8 @@ namespace { BOOST_TEST_EQ(x2.hash_function(), hasher(2)); BOOST_TEST_EQ(x2.key_eq(), key_equal(1)); } else { - test_matches_reference(x2, ref_map1); - test_matches_reference(x1, ref_map2); + test_matches_reference(x2, reference_cont1); + test_matches_reference(x1, reference_cont2); BOOST_TEST_EQ(x1.hash_function(), hasher(2)); BOOST_TEST_EQ(x1.key_eq(), key_equal(1)); @@ -177,17 +182,21 @@ namespace { check_raii_counts(); } - template - void insert_and_swap(F swapper, G gen, test::random_generator rg) + template + void insert_and_swap( + X*, F swapper, GF gen_factory, test::random_generator rg) { + using allocator_type = typename X::allocator_type; + + auto gen = gen_factory.template get(); auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); }); { raii::reset_counts(); - map_type x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3)); - map_type x2(vals2.size(), hasher(2), key_equal(1), allocator_type(3)); + X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3)); + X x2(vals2.size(), hasher(2), key_equal(1), allocator_type(3)); std::thread t1, t2, t3; boost::compat::latch l(2); @@ -282,21 +291,25 @@ namespace { } map_type* map; - pocs_map_type* pocs_map; + replace_allocator* pocs_map; + + set_type* set; + replace_allocator* pocs_set; } // namespace // clang-format off UNORDERED_TEST( swap_tests, - ((map)(pocs_map)) + ((map)(pocs_map)(set)(pocs_set)) ((member_fn_swap)(free_fn_swap)) - ((value_type_generator)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST(insert_and_swap, + ((map)(set)) ((member_fn_swap)(free_fn_swap)) - ((value_type_generator)) + ((value_type_generator_factory)) ((default_generator)(sequential)(limited_range))) // clang-format on diff --git a/test/cfoa/visit_tests.cpp b/test/cfoa/visit_tests.cpp index 267457df..6660fe7d 100644 --- a/test/cfoa/visit_tests.cpp +++ b/test/cfoa/visit_tests.cpp @@ -1,38 +1,64 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2023 Joaquin M Lopez Munoz // 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 "helpers.hpp" #include +#include #include +#include #include #include namespace { test::seed_t initialize_seed(335740237); + auto non_present_keys = [] + { + std::array a; + for(std::size_t i = 0; i < a.size(); ++i) { + a[i].x_ = -((int)i + 1); + } + return a; + }(); + + template + raii const & get_non_present_key(T const & x) + { + return non_present_keys[get_key(x).x_ % non_present_keys.size()]; + } + struct lvalue_visitor_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { using value_type = typename X::value_type; + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_visits{0}; std::atomic total_count{0}; - auto mut_visitor = [&num_visits, &reference_map](value_type& v) { - BOOST_TEST(reference_map.contains(v.first)); - BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second); + auto mut_visitor = [&num_visits, &reference_cont](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; - auto const_visitor = [&num_visits, &reference_map](value_type const& v) { - BOOST_TEST(reference_map.contains(v.first)); - BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second); + auto const_visitor = + [&num_visits, &reference_cont](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; @@ -40,14 +66,14 @@ namespace { thread_runner( values, [&x, &mut_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.visit(val.first, mut_visitor); + auto count = x.visit(get_key(val), mut_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.visit(val.second, mut_visitor); + count = x.visit(get_non_present_key(val), mut_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -63,16 +89,16 @@ namespace { thread_runner( values, [&x, &const_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); auto const& y = x; - auto count = y.visit(val.first, const_visitor); + auto count = y.visit(get_key(val), const_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = y.visit(val.second, const_visitor); + count = y.visit(get_non_present_key(val), const_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -88,15 +114,15 @@ namespace { thread_runner( values, [&x, &const_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.cvisit(val.first, const_visitor); + auto count = x.cvisit(get_key(val), const_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.cvisit(val.second, const_visitor); + count = x.cvisit(get_non_present_key(val), const_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -111,14 +137,14 @@ namespace { { thread_runner(values, [&x, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.count(val.first); + auto count = x.count(get_key(val)); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.count(val.second); + count = x.count(get_non_present_key(val)); BOOST_TEST_EQ(count, 0u); } }); @@ -132,13 +158,13 @@ namespace { { thread_runner(values, [&x](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto contains = x.contains(val.first); + auto contains = x.contains(get_key(val)); BOOST_TEST(contains); - contains = x.contains(val.second); + contains = x.contains(get_non_present_key(val)); BOOST_TEST(!contains); } }); @@ -152,22 +178,29 @@ namespace { struct transp_visitor_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { using value_type = typename X::value_type; + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic num_visits{0}; std::atomic total_count{0}; - auto mut_visitor = [&num_visits, &reference_map](value_type& v) { - BOOST_TEST(reference_map.contains(v.first)); - BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second); + auto mut_visitor = [&num_visits, &reference_cont](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; - auto const_visitor = [&num_visits, &reference_map](value_type const& v) { - BOOST_TEST(reference_map.contains(v.first)); - BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second); + auto const_visitor = [&num_visits, &reference_cont](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; @@ -175,15 +208,15 @@ namespace { thread_runner( values, [&x, &mut_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.visit(val.first.x_, mut_visitor); + auto count = x.visit(get_key(val).x_, mut_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.visit(val.second.x_, mut_visitor); + count = x.visit(get_non_present_key(val).x_, mut_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -199,16 +232,16 @@ namespace { thread_runner( values, [&x, &const_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); auto const& y = x; - auto count = y.visit(val.first.x_, const_visitor); + auto count = y.visit(get_key(val).x_, const_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = y.visit(val.second.x_, const_visitor); + count = y.visit(get_non_present_key(val).x_, const_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -224,15 +257,15 @@ namespace { thread_runner( values, [&x, &const_visitor, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.cvisit(val.first.x_, const_visitor); + auto count = x.cvisit(get_key(val).x_, const_visitor); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.cvisit(val.second.x_, const_visitor); + count = x.cvisit(get_non_present_key(val).x_, const_visitor); BOOST_TEST_EQ(count, 0u); } }); @@ -247,14 +280,14 @@ namespace { { thread_runner(values, [&x, &total_count](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto count = x.count(val.first.x_); + auto count = x.count(get_key(val).x_); BOOST_TEST_EQ(count, 1u); total_count += count; - count = x.count(val.second.x_); + count = x.count(get_non_present_key(val).x_); BOOST_TEST_EQ(count, 0u); } }); @@ -268,13 +301,13 @@ namespace { { thread_runner(values, [&x](boost::span s) { for (auto const& val : s) { - auto r = val.first.x_; + auto r = get_key(val).x_; BOOST_TEST(r >= 0); - auto contains = x.contains(val.first.x_); + auto contains = x.contains(get_key(val).x_); BOOST_TEST(contains); - contains = x.contains(val.second.x_); + contains = x.contains(get_non_present_key(val).x_); BOOST_TEST(!contains); } }); @@ -288,24 +321,31 @@ namespace { struct visit_all_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { using value_type = typename X::value_type; + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + std::atomic total_count{0}; - auto mut_visitor = [&reference_map](std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + auto mut_visitor = [&reference_cont](std::atomic& num_visits) { + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; }; - auto const_visitor = [&reference_map](std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + auto const_visitor = [&reference_cont](std::atomic& num_visits) { + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; }; @@ -352,45 +392,52 @@ namespace { struct visit_while_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { using value_type = typename X::value_type; - auto mut_truthy_visitor = [&reference_map]( + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + auto mut_truthy_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; return true; }; }; - auto const_truthy_visitor = [&reference_map]( + auto const_truthy_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; return true; }; }; - auto mut_falsey_visitor = [&reference_map]( + auto mut_falsey_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); ++num_visits; - return (kv.second.x_ % 100) == 0; + return (get_value(v).x_ % 100) == 0; }; }; - auto const_falsey_visitor = [&reference_map]( + auto const_falsey_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); ++num_visits; - return (kv.second.x_ % 100) == 0; + return (get_value(v).x_ % 100) == 0; }; }; @@ -452,23 +499,30 @@ namespace { struct exec_policy_visit_all_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) using value_type = typename X::value_type; - auto mut_visitor = [&reference_map](std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + auto mut_visitor = [&reference_cont](std::atomic& num_visits) { + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; }; - auto const_visitor = [&reference_map](std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + auto const_visitor = [&reference_cont](std::atomic& num_visits) { + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; }; }; @@ -502,7 +556,7 @@ namespace { #else (void)values; (void)x; - (void)reference_map; + (void)reference_cont; #endif } } exec_policy_visit_all; @@ -510,48 +564,55 @@ namespace { struct exec_policy_visit_while_type { template - void operator()(std::vector& values, X& x, M const& reference_map) + void operator()(std::vector& values, X& x, M const& reference_cont) { #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) using value_type = typename X::value_type; - auto mut_truthy_visitor = [&reference_map]( + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + auto mut_truthy_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; return true; }; }; - auto const_truthy_visitor = [&reference_map]( + auto const_truthy_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; return true; }; }; - auto mut_falsey_visitor = [&reference_map]( + auto mut_falsey_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](arg_type& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; - return (kv.second.x_ % 100) == 0; + return (get_value(v).x_ % 100) == 0; }; }; - auto const_falsey_visitor = [&reference_map]( + auto const_falsey_visitor = [&reference_cont]( std::atomic& num_visits) { - return [&reference_map, &num_visits](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + return [&reference_cont, &num_visits](value_type const& v) { + BOOST_TEST(reference_cont.contains(get_key(v))); + BOOST_TEST_EQ(v, *reference_cont.find(get_key(v))); ++num_visits; - return (kv.second.x_ % 100) == 0; + return (get_value(v).x_ % 100) == 0; }; }; @@ -616,24 +677,17 @@ namespace { #else (void)values; (void)x; - (void)reference_map; + (void)reference_cont; #endif } } exec_policy_visit_while; - template - void visit(X*, G gen, F visitor, test::random_generator rg) + template + void visit(X*, GF gen_factory, F visitor, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); - for (auto& val : values) { - if (val.second.x_ == 0) { - val.second.x_ = 1; - } - val.second.x_ *= -1; - } - - auto reference_map = - boost::unordered_flat_map(values.begin(), values.end()); + auto reference_cont = reference_container(values.begin(), values.end()); raii::reset_counts(); @@ -642,7 +696,7 @@ namespace { for (auto const& v : values) { x.insert(v); } - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_cont.size()); std::uint64_t old_default_constructor = raii::default_constructor; std::uint64_t old_copy_constructor = raii::copy_constructor; @@ -650,7 +704,7 @@ namespace { std::uint64_t old_copy_assignment = raii::copy_assignment; std::uint64_t old_move_assignment = raii::move_assignment; - visitor(values, x, reference_map); + visitor(values, x, reference_cont); BOOST_TEST_EQ(old_default_constructor, raii::default_constructor); BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor); @@ -669,9 +723,10 @@ namespace { raii::destructor); } - template - void empty_visit(X*, G gen, test::random_generator rg) + template + void empty_visit(X*, GF gen_factory, test::random_generator rg) { + auto gen = gen_factory.template get(); auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); using values_type = decltype(values); using span_value_type = typename values_type::value_type; @@ -696,7 +751,7 @@ namespace { BOOST_TEST_EQ(num_visits, 0u); for (auto const& val : s) { - auto count = x.visit(val.first, + auto count = x.visit(get_key(val), [&num_visits](typename X::value_type const&) { ++num_visits; }); BOOST_TEST_EQ(count, 0u); } @@ -716,8 +771,8 @@ namespace { BOOST_TEST_EQ(raii::destructor, 0u); } - template - void insert_and_visit(X*, G gen, test::random_generator rg) + template + void insert_and_visit(X*, GF gen_factory, test::random_generator rg) { // here we attempt to ensure happens-before and synchronizes-with // the visitation thread essentially chases the insertion one @@ -726,6 +781,7 @@ namespace { BOOST_TEST(rg == test::sequential); + auto gen = gen_factory.template get(); auto const values = make_random_values(1024 * 16, [&] { return gen(rg); }); { @@ -752,9 +808,9 @@ namespace { for (std::size_t idx = 0; idx < values.size(); ++idx) { std::atomic_bool b{false}; while (!b) { - x.cvisit(values[idx].first, + x.cvisit(get_key(values[idx]), [&b, &strs, idx, &values](typename X::value_type const& v) { - BOOST_TEST_EQ(v.second, values[idx].second); + BOOST_TEST_EQ(get_value(v), get_value(values[idx])); BOOST_TEST_EQ(strs[idx], "rawr"); b = true; }); @@ -771,6 +827,9 @@ namespace { boost::unordered::concurrent_flat_map* map; boost::unordered::concurrent_flat_map* transp_map; + boost::unordered::concurrent_flat_set* set; + boost::unordered::concurrent_flat_set* transp_set; } // namespace @@ -782,29 +841,30 @@ using test::sequential; UNORDERED_TEST( visit, - ((map)) - ((value_type_generator)(init_type_generator)) - ((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)(exec_policy_visit_while)) + ((map)(set)) + ((value_type_generator_factory)(init_type_generator_factory)) + ((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all) + (exec_policy_visit_while)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( visit, - ((transp_map)) - ((value_type_generator)(init_type_generator)) + ((transp_map)(transp_set)) + ((value_type_generator_factory)(init_type_generator_factory)) ((transp_visitor)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( empty_visit, - ((map)(transp_map)) - ((value_type_generator)(init_type_generator)) + ((map)(transp_map)(set)(transp_set)) + ((value_type_generator_factory)(init_type_generator_factory)) ((default_generator)(sequential)(limited_range)) ) UNORDERED_TEST( insert_and_visit, - ((map)) - ((value_type_generator)) + ((map)(set)) + ((value_type_generator_factory)) ((sequential)) )