added insert_and_visit and similar operations to concurrent containers (#283)

This commit is contained in:
joaquintides
2024-09-21 10:58:30 +02:00
committed by GitHub
parent 57546ed7e3
commit 834580b539
17 changed files with 2473 additions and 75 deletions
+133
View File
@@ -53,6 +53,34 @@ namespace {
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
}
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.first.x_, v.second.x_, f1, f2);
}
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.first.x_, v.second.x_, f1, f2);
}
struct lvalue_emplacer_type
{
template <class T, class X> void call_impl(std::vector<T>& values, X& x)
@@ -133,6 +161,55 @@ namespace {
}
} lvalue_emplace_or_cvisit;
struct lvalue_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_cvisit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.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, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_cvisit;
struct lvalue_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -176,6 +253,55 @@ namespace {
}
} lvalue_emplace_or_visit;
struct lvalue_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_visit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.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, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_visit;
struct copy_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -312,6 +438,13 @@ UNORDERED_TEST(
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
emplace,
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_emplace_and_cvisit)(lvalue_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
namespace {
+41 -3
View File
@@ -86,7 +86,7 @@ namespace {
while (!nh.empty()) {
auto& o = out[br2++ % out.size()];
typename X::insert_return_type r;
switch (br3++ % 3) {
switch (br3++ % 5) {
case 0:
r = o.insert(std::move(nh));
break;
@@ -97,13 +97,37 @@ namespace {
(void)v2;
});
break;
case 2: default:
case 2:
r = o.insert_or_cvisit(
std::move(nh), [&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 3:
r = o.insert_and_visit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 4: default:
r = o.insert_and_cvisit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
}
BOOST_ASSERT(r.inserted || !r.node.empty());
nh = std::move(r.node);
@@ -144,6 +168,20 @@ namespace {
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_and_visit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_and_cvisit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
}
} // namespace
@@ -156,7 +194,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert_empty_node_tests,
((test_node_map)(test_node_set)))
((test_node_map)(test_node_set)))
// clang-format on
RUN_TESTS()
+413 -2
View File
@@ -376,6 +376,62 @@ namespace {
}
} lvalue_insert_or_cvisit;
struct lvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_cvisit;
struct lvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -424,6 +480,61 @@ namespace {
}
} lvalue_insert_or_visit;
struct lvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b =
x.insert_and_visit(r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_visit;
struct rvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -470,6 +581,66 @@ namespace {
}
} rvalue_insert_or_cvisit;
struct rvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::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, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_cvisit;
struct rvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -522,6 +693,65 @@ namespace {
}
} rvalue_insert_or_visit;
struct rvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_visit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::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, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_visit;
struct iterator_range_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -561,6 +791,58 @@ namespace {
}
} iterator_range_insert_or_cvisit;
struct iterator_range_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
x.insert_and_cvisit(s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.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)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_cvisit;
struct iterator_range_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -568,6 +850,13 @@ namespace {
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
@@ -578,7 +867,7 @@ namespace {
thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
@@ -600,6 +889,58 @@ namespace {
}
} iterator_range_insert_or_visit;
struct iterator_range_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
x.insert_and_visit(s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.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)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_visit;
template <class X, class GF, class F>
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
{
@@ -721,6 +1062,62 @@ namespace {
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
{
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
X x;
thread_runner(dummy,
[&x, &init_list, &num_inserts, &num_invokes](boost::span<raii>) {
x.insert_and_visit(init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
x.insert_and_cvisit(
init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.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& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
}
@@ -735,6 +1132,9 @@ namespace {
x.insert_or_visit({2, 3}, [](value_type&) {});
x.insert_or_cvisit({3, 4}, [](value_type const&) {});
x.insert_and_visit({4, 5}, [](value_type&) {}, [](value_type&) {});
x.insert_and_cvisit({5, 6}, [](value_type&) {}, [](value_type const&) {});
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
@@ -825,7 +1225,8 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map_and_init_list)(node_map_and_init_list)(set_and_init_list)))
((map_and_init_list)(node_map_and_init_list)
(set_and_init_list)(node_set_and_init_list)))
UNORDERED_TEST(
insert,
@@ -840,6 +1241,16 @@ UNORDERED_TEST(
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map)(fancy_map)(node_map)(fancy_node_map)
(set)(fancy_set)(node_set)(fancy_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_insert_and_cvisit)(lvalue_insert_and_visit)
(rvalue_insert_and_cvisit)(rvalue_insert_and_visit)
(iterator_range_insert_and_cvisit)(iterator_range_insert_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map)(node_map))
((init_type_generator_factory))
+275
View File
@@ -177,6 +177,53 @@ namespace {
}
} lvalue_try_emplace_or_cvisit;
struct lvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_cvisit;
struct lvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -217,6 +264,53 @@ namespace {
}
} lvalue_try_emplace_or_visit;
struct lvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_visit;
struct rvalue_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -258,6 +352,54 @@ namespace {
}
} rvalue_try_emplace_or_cvisit;
struct rvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_cvisit;
struct rvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -298,6 +440,53 @@ namespace {
}
} rvalue_try_emplace_or_visit;
struct rvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_visit;
struct transp_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -326,6 +515,41 @@ namespace {
}
} transp_try_emplace_or_cvisit;
struct transp_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_cvisit;
struct transp_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -355,6 +579,42 @@ namespace {
}
} transp_try_emplace_or_visit;
struct transp_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_visit;
template <class X, class G, class F>
void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg)
{
@@ -417,6 +677,14 @@ UNORDERED_TEST(
(rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((map)(node_map))
((value_type_generator)(init_type_generator))
((lvalue_try_emplace_and_cvisit)(lvalue_try_emplace_and_visit)
(rvalue_try_emplace_and_cvisit)(rvalue_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map)(transp_node_map))
@@ -424,6 +692,13 @@ UNORDERED_TEST(
((transp_try_emplace)(norehash_transp_try_emplace)
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map)(transp_node_map))
((init_type_generator))
((transp_try_emplace_and_cvisit)(transp_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()