mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
tested boost::concurrent_flat_set
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
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<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
namespace {
|
||||
template <class G> void clear_tests(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
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_type>::value;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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<map_value_type> s) {
|
||||
thread_runner(values, [&x](boost::span<value_type> 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 <class G> void insert_and_clear(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
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<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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
|
||||
|
||||
|
137
test/cfoa/common_helpers.hpp
Normal file
137
test/cfoa/common_helpers.hpp
Normal file
@ -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 <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
template <typename K>
|
||||
struct value_cardinality
|
||||
{
|
||||
static constexpr std::size_t value=1;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct value_cardinality<std::pair<K, V> >
|
||||
{
|
||||
static constexpr std::size_t value=2;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct reference_container_impl;
|
||||
|
||||
template <class Container>
|
||||
using reference_container = typename reference_container_impl<Container>::type;
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_map<K, V>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K>;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct flat_container_impl;
|
||||
|
||||
template <class Container>
|
||||
using flat_container = typename flat_container_impl<Container>::type;
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_map<K, V, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename Container, template <typename> class Allocator>
|
||||
struct replace_allocator_impl;
|
||||
|
||||
template <typename Container, template <typename> class Allocator>
|
||||
using replace_allocator =
|
||||
typename replace_allocator_impl<Container, Allocator>::type;
|
||||
|
||||
template <
|
||||
typename K, typename V, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_flat_map<K, V, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_flat_map<K, V, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_flat_map<K, V, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_flat_set<K, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_flat_set<K, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_flat_set<K, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
K const& get_key(K const& x) { return x; }
|
||||
|
||||
template <typename K,typename V>
|
||||
K const& get_key(const std::pair<K, V>& x) { return x.first; }
|
||||
|
||||
template <typename K>
|
||||
K const& get_value(K const& x) { return x; }
|
||||
|
||||
template <typename K,typename V>
|
||||
V const& get_value(const std::pair<K, V>& x) { return x.second; }
|
||||
|
||||
template <typename K,typename V>
|
||||
V& get_value(std::pair<K, V>& x) { return x.second; }
|
||||
|
||||
template <class X, class Y>
|
||||
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 <class X, class Y>
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(335740237);
|
||||
|
||||
template <typename Container, typename Value>
|
||||
bool member_emplace(Container& x, Value const & v)
|
||||
{
|
||||
return x.emplace(v.x_);
|
||||
}
|
||||
|
||||
template <typename Container, typename Value>
|
||||
bool member_emplace(Container& x, Value& v)
|
||||
{
|
||||
return x.emplace(v.x_);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value>
|
||||
bool member_emplace(Container& x, std::pair<Key, Value> const & v)
|
||||
{
|
||||
return x.emplace(v.first.x_, v.second.x_);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value>
|
||||
bool member_emplace(Container& x, std::pair<Key, Value>& v)
|
||||
{
|
||||
return x.emplace(v.first.x_, v.second.x_);
|
||||
}
|
||||
|
||||
template <typename Container, typename Value, typename F>
|
||||
bool member_emplace_or_visit(Container& x, Value const & v, F f)
|
||||
{
|
||||
return x.emplace_or_visit(v.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Value, typename F>
|
||||
bool member_emplace_or_visit(Container& x, Value& v, F f)
|
||||
{
|
||||
return x.emplace_or_visit(v.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value, typename F>
|
||||
bool member_emplace_or_visit(
|
||||
Container& x, std::pair<Key, Value> const & v, F f)
|
||||
{
|
||||
return x.emplace_or_visit(v.first.x_, v.second.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value, typename F>
|
||||
bool member_emplace_or_visit(Container& x, std::pair<Key, Value>& v, F f)
|
||||
{
|
||||
return x.emplace_or_visit(v.first.x_, v.second.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Value, typename F>
|
||||
bool member_emplace_or_cvisit(Container& x, Value const & v, F f)
|
||||
{
|
||||
return x.emplace_or_cvisit(v.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Value, typename F>
|
||||
bool member_emplace_or_cvisit(Container& x, Value& v, F f)
|
||||
{
|
||||
return x.emplace_or_cvisit(v.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value, typename F>
|
||||
bool member_emplace_or_cvisit(
|
||||
Container& x, std::pair<Key, Value> const & v, F f)
|
||||
{
|
||||
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
|
||||
}
|
||||
|
||||
template <typename Container, typename Key, typename Value, typename F>
|
||||
bool member_emplace_or_cvisit(Container& x, std::pair<Key, Value>& v, F f)
|
||||
{
|
||||
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
|
||||
}
|
||||
|
||||
struct lvalue_emplacer_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;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> 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 <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;
|
||||
|
||||
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 <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;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> 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 <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};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> 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 <class X, class G, class F>
|
||||
void emplace(X*, G gen, F emplacer, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_set<raii>* 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)))
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
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<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
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 <class G> 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 <class X, class GF>
|
||||
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<X>();
|
||||
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
|
||||
boost::unordered_flat_map<raii, raii> reference_map(
|
||||
vals1.begin(), vals1.end());
|
||||
auto reference_cont = reference_container<X>(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
|
||||
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -15,6 +17,9 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
std::atomic<std::uint64_t> 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<T>) {
|
||||
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 <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;
|
||||
|
||||
std::atomic<std::uint64_t> 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<T> s) {
|
||||
for (auto const& k : s) {
|
||||
@ -92,6 +100,15 @@ namespace {
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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_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<T> 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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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_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<T> 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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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_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<T> /* 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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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_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<T> /* 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_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_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<T> 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 <class X, class G, class F>
|
||||
void erase(X*, G gen, F eraser, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void erase(X*, GF gen_factory, F eraser, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_map;
|
||||
boost::unordered::concurrent_flat_map<raii, transp_hash,
|
||||
transp_key_equal>* 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)))
|
||||
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> 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_type::value_type> 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 <class G> void copy_assign(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void copy_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
|
||||
{
|
||||
@ -31,12 +94,12 @@ namespace {
|
||||
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
|
||||
auto end = values.end();
|
||||
|
||||
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
|
||||
auto reference_cont = reference_container<X>(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 <class G> void move_assign(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void move_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
|
||||
{
|
||||
@ -72,7 +139,7 @@ namespace {
|
||||
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
|
||||
auto end = values.end();
|
||||
|
||||
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
|
||||
auto reference_cont = reference_container<X>(begin, mid);
|
||||
|
||||
BOOST_TEST(
|
||||
!boost::allocator_is_always_equal<allocator_type>::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 <class X, class IL>
|
||||
void intializer_list_assign(std::pair<X*, IL> p)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
std::initializer_list<value_type> 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()
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> 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_type::value_type> 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 <class X>
|
||||
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 <class G> void iterator_range(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void iterator_range(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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 <class G> void copy_constructor(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void copy_constructor(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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 <class G> void move_constructor(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
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<X>();
|
||||
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 <class X, class IL>
|
||||
void initializer_list_bucket_count(std::pair<X*, IL> p)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
std::initializer_list<value_type> 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()
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -15,6 +17,9 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
std::atomic<std::uint64_t> 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<T>) {
|
||||
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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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_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<T> 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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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;
|
||||
|
||||
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<T> /* 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<std::uint32_t> 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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<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;
|
||||
|
||||
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<T> /* 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<std::uint32_t> 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 <class X, class G, class F>
|
||||
void erase(X*, G gen, F eraser, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void erase(X*, GF gen_factory, F eraser, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
|
||||
boost::unordered::concurrent_flat_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* 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)))
|
||||
|
||||
|
@ -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 <boost/compat/latch.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@ -308,16 +314,54 @@ std::size_t hash_value(raii const& r) noexcept
|
||||
return hasher(r.x_);
|
||||
}
|
||||
|
||||
struct exception_value_type_generator_type
|
||||
template <typename K>
|
||||
struct exception_value_generator
|
||||
{
|
||||
std::pair<raii const, raii> 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 <typename K, typename V>
|
||||
struct exception_value_generator<std::pair<K, V> >
|
||||
{
|
||||
static constexpr bool const_key = std::is_const<K>::value;
|
||||
static constexpr bool const_mapped = std::is_const<V>::value;
|
||||
using value_type = std::pair<
|
||||
typename std::conditional<const_key, raii const, raii>::type,
|
||||
typename std::conditional<const_mapped, raii const, raii>::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 <typename Container>
|
||||
exception_value_generator<typename Container::value_type> get()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
} exception_value_type_generator_factory;
|
||||
|
||||
struct exception_init_type_generator_factory_type
|
||||
{
|
||||
template <typename Container>
|
||||
exception_value_generator<typename Container::init_type> get()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
} exception_init_type_generator_factory;
|
||||
|
||||
struct exception_init_type_generator_type
|
||||
{
|
||||
@ -388,29 +432,6 @@ template <class T, class F> void thread_runner(std::vector<T>& values, F f)
|
||||
}
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
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 <class X, class Y>
|
||||
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 <class T> 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<decltype(f())>
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -84,6 +86,9 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
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<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
if (std::is_same<typename X::key_type,
|
||||
typename X::value_type>::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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
// 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};
|
||||
|
||||
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 <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
// 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};
|
||||
|
||||
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 <class X, class G, class F>
|
||||
void insert(X*, G gen, F inserter, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
|
||||
{
|
||||
disable_exceptions();
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
|
||||
boost::unordered::concurrent_flat_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* 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)))
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(223333016);
|
||||
|
||||
template <class G> void merge(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void merge(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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
|
||||
|
@ -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 <boost/config/workaround.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <limits>
|
||||
|
||||
test::seed_t initialize_seed{32304628};
|
||||
@ -34,37 +36,89 @@ bool unequal_call(boost::unordered::concurrent_flat_map<T, T>& x1,
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<int, int>;
|
||||
|
||||
#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 <class T>
|
||||
void swap_call(boost::unordered::concurrent_flat_set<T>& x1,
|
||||
boost::unordered::concurrent_flat_set<T>& x2)
|
||||
{
|
||||
swap(x1, x2);
|
||||
}
|
||||
|
||||
#endif
|
||||
template <class T>
|
||||
bool equal_call(boost::unordered::concurrent_flat_set<T>& x1,
|
||||
boost::unordered::concurrent_flat_set<T>& x2)
|
||||
{
|
||||
return x1 == x2;
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (fwd_equal_call) {
|
||||
map_type x1, x2;
|
||||
template <class T>
|
||||
bool unequal_call(boost::unordered::concurrent_flat_set<T>& x1,
|
||||
boost::unordered::concurrent_flat_set<T>& x2)
|
||||
{
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<int, int>;
|
||||
using set_type = boost::unordered::concurrent_flat_map<int, int>;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
template <typename X>
|
||||
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 <typename X>
|
||||
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 <typename X>
|
||||
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 <typename X>
|
||||
void max_size(X*)
|
||||
{
|
||||
X x1;
|
||||
BOOST_TEST_EQ(
|
||||
x1.max_size(), std::numeric_limits<typename map_type::size_type>::max());
|
||||
x1.max_size(), std::numeric_limits<typename X::size_type>::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()
|
||||
|
@ -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 <boost/compat/latch.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@ -328,27 +333,48 @@ auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_type_generator_type
|
||||
template <typename K>
|
||||
struct value_generator
|
||||
{
|
||||
std::pair<raii const, raii> 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 <typename K, typename V>
|
||||
struct value_generator<std::pair<K, V> >
|
||||
{
|
||||
std::pair<raii, raii> operator()(test::random_generator rg)
|
||||
static constexpr bool const_key = std::is_const<K>::value;
|
||||
static constexpr bool const_mapped = std::is_const<V>::value;
|
||||
using value_type = std::pair<
|
||||
typename std::conditional<const_key, raii const, raii>::type,
|
||||
typename std::conditional<const_mapped, raii const, raii>::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 <typename Container>
|
||||
value_generator<typename Container::value_type> get() { return {}; }
|
||||
} value_type_generator_factory;
|
||||
|
||||
struct init_type_generator_factory_type
|
||||
{
|
||||
template <typename Container>
|
||||
value_generator<typename Container::init_type> get() { return {}; }
|
||||
} init_type_generator_factory;
|
||||
|
||||
template <class T>
|
||||
std::vector<boost::span<T> > split(
|
||||
@ -408,29 +434,6 @@ template <class T, class F> void thread_runner(std::vector<T>& values, F f)
|
||||
}
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
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 <class X, class Y>
|
||||
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 <class T> 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
|
||||
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
struct raii_convertible
|
||||
{
|
||||
int x, y;
|
||||
raii_convertible(int x_, int y_) : x{x_}, y{y_} {}
|
||||
int x = 0, y = 0 ;
|
||||
|
||||
template <typename T>
|
||||
raii_convertible(T const & t) : x{t.x_} {}
|
||||
|
||||
template <typename T, typename Q>
|
||||
raii_convertible(std::pair<T, Q> const & p) : x{p.first.x_}, y{p.second.x_}
|
||||
{}
|
||||
|
||||
operator raii() { return {x}; }
|
||||
operator std::pair<raii const, raii>() { return {x, y}; }
|
||||
};
|
||||
|
||||
@ -23,6 +32,9 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> 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 <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;
|
||||
|
||||
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<T, typename X::value_type>::value) {
|
||||
if (std::is_same<T, typename X::value_type>::value &&
|
||||
!std::is_same<typename X::key_type, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
@ -82,6 +100,9 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
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<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
if (std::is_same<typename X::key_type,
|
||||
typename X::value_type>::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 <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;
|
||||
|
||||
std::vector<raii_convertible> 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<raii_convertible> 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 <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;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> 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 <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};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> 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 <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;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
@ -337,11 +387,19 @@ namespace {
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
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, 2 * x.size());
|
||||
BOOST_TEST_GE(
|
||||
raii::move_constructor, value_type_cardinality * x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_insert_or_cvisit;
|
||||
@ -350,12 +408,22 @@ namespace {
|
||||
{
|
||||
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};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> 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<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
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, 2 * x.size());
|
||||
BOOST_TEST_GE(
|
||||
raii::move_constructor, value_type_cardinality * x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_insert_or_visit;
|
||||
@ -384,10 +460,13 @@ namespace {
|
||||
{
|
||||
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;
|
||||
|
||||
std::vector<raii_convertible> 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<std::uint64_t> 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 <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;
|
||||
|
||||
std::vector<raii_convertible> 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<std::uint64_t> 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 <class X, class G, class F>
|
||||
void insert(X*, G gen, F inserter, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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 <class X> void insert_initializer_list(X*)
|
||||
template <class X, class IL>
|
||||
void insert_initializer_list(std::pair<X*, IL> 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<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
std::initializer_list<value_type> 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<raii> dummy;
|
||||
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(
|
||||
init_list.begin(), init_list.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
@ -520,13 +586,13 @@ namespace {
|
||||
X x;
|
||||
|
||||
thread_runner(
|
||||
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
|
||||
dummy, [&x, &init_list](boost::span<raii>) { 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<raii>) {
|
||||
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
|
||||
thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span<raii>) {
|
||||
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<raii>, fancy_allocator<std::pair<raii const, raii> > >*
|
||||
fancy_map;
|
||||
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_flat_set<raii, boost::hash<raii>,
|
||||
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
|
||||
fancy_set;
|
||||
|
||||
std::initializer_list<std::pair<raii const, raii> > 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<raii> 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
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
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<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii,
|
||||
hasher, key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
|
||||
std::hash<raii>, std::equal_to<raii>,
|
||||
stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
using set2_type = boost::unordered::concurrent_flat_set<raii, std::hash<raii>,
|
||||
std::equal_to<raii>, stateful_allocator<raii> >;
|
||||
|
||||
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 <class F, class G>
|
||||
void merge_tests(F merger, G gen, test::random_generator rg)
|
||||
template <typename X, typename Y, class F, class GF>
|
||||
void merge_tests(
|
||||
std::pair<X*, Y*>, 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_type>::value;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto ref_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 8, [&] { return gen(rg); });
|
||||
auto reference_cont = reference_container<X>(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<unsigned long long> num_merged{0};
|
||||
|
||||
thread_runner(values, [&x, &expected_copies, &num_merged, merger](
|
||||
boost::span<map_value_type> s) {
|
||||
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
|
||||
std::hash<raii>, std::equal_to<raii>, allocator_type>;
|
||||
|
||||
map2_type y(s.size(), allocator_type(3));
|
||||
boost::span<value_type> 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 <class G>
|
||||
void insert_and_merge_tests(G gen, test::random_generator rg)
|
||||
template <typename X, typename Y, class GF>
|
||||
void insert_and_merge_tests(
|
||||
std::pair<X*, Y*>, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
|
||||
std::hash<raii>, std::equal_to<raii>, allocator_type>;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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<raii, raii>();
|
||||
ref_map.insert(vals1.begin(), vals1.end());
|
||||
ref_map.insert(vals2.begin(), vals2.end());
|
||||
auto reference_cont = reference_container<X>();
|
||||
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
|
||||
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
using test::default_generator;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii>;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii>;
|
||||
|
||||
map_type* test_map;
|
||||
set_type* test_set;
|
||||
|
||||
template<typename F>
|
||||
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<int, int>;
|
||||
using value_type = typename map::value_type;
|
||||
namespace {
|
||||
template <class X, class GF>
|
||||
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<typename X::key_type, typename X::value_type>::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<X>();
|
||||
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()
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
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<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
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 <typename X>
|
||||
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 <typename X>
|
||||
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<size_type>(std::ceil(c / x.max_load_factor()));
|
||||
@ -59,9 +71,13 @@ namespace {
|
||||
BOOST_TEST_EQ(x.bucket_count(), f(0.0));
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void insert_and_erase_with_rehash(G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
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<X>();
|
||||
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
|
||||
|
||||
auto erase_indices = std::vector<std::size_t>(vals1.size());
|
||||
@ -70,13 +86,13 @@ namespace {
|
||||
}
|
||||
shuffle_values(erase_indices);
|
||||
|
||||
auto ref_map = boost::unordered_flat_map<raii, raii>();
|
||||
ref_map.insert(vals1.begin(), vals1.end());
|
||||
auto reference_cont = reference_container<X>();
|
||||
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
|
||||
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{996130204};
|
||||
|
||||
@ -56,17 +58,12 @@ template <class T> struct pocs_allocator
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
|
||||
using pocs_allocator_type = pocs_allocator<std::pair<const raii, raii> >;
|
||||
|
||||
using pocs_map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, pocs_allocator_type>;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
template <class T> struct is_nothrow_member_swappable
|
||||
{
|
||||
@ -75,13 +72,21 @@ template <class T> struct is_nothrow_member_swappable
|
||||
};
|
||||
|
||||
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
|
||||
boost::unordered::concurrent_flat_map<int, int, std::hash<int>,
|
||||
std::equal_to<int>, std::allocator<std::pair<int const, int> > > >::value);
|
||||
replace_allocator<map_type, std::allocator> >::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<pocs_map_type>::value);
|
||||
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
|
||||
replace_allocator<map_type, pocs_allocator> >::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(!is_nothrow_member_swappable<map_type>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
|
||||
replace_allocator<set_type, std::allocator> >::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
|
||||
replace_allocator<set_type, pocs_allocator> >::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(!is_nothrow_member_swappable<set_type>::value);
|
||||
|
||||
namespace {
|
||||
struct
|
||||
{
|
||||
@ -97,31 +102,31 @@ namespace {
|
||||
}
|
||||
} free_fn_swap;
|
||||
|
||||
template <class X, class F, class G>
|
||||
void swap_tests(X*, F swapper, G gen, test::random_generator rg)
|
||||
template <class X, class F, class GF>
|
||||
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<allocator>::type::value;
|
||||
boost::allocator_propagate_on_container_swap<
|
||||
allocator_type>::type::value;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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<raii, raii>(vals1.begin(), vals1.end());
|
||||
|
||||
auto ref_map2 =
|
||||
boost::unordered_flat_map<raii, raii>(vals2.begin(), vals2.end());
|
||||
auto reference_cont1 = reference_container<X>(vals1.begin(), vals1.end());
|
||||
auto reference_cont2 = reference_container<X>(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<map_value_type> s) {
|
||||
thread_runner(vals1, [&x1, &x2, swapper](boost::span<value_type> 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 <class F, class G>
|
||||
void insert_and_swap(F swapper, G gen, test::random_generator rg)
|
||||
template <class X, class F, class GF>
|
||||
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<X>();
|
||||
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<map_type, pocs_allocator>* pocs_map;
|
||||
|
||||
set_type* set;
|
||||
replace_allocator<set_type, pocs_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
|
||||
|
||||
|
@ -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 <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(335740237);
|
||||
|
||||
auto non_present_keys = []
|
||||
{
|
||||
std::array<raii,128> a;
|
||||
for(std::size_t i = 0; i < a.size(); ++i) {
|
||||
a[i].x_ = -((int)i + 1);
|
||||
}
|
||||
return a;
|
||||
}();
|
||||
|
||||
template<typename T>
|
||||
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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<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_visits{0};
|
||||
std::atomic<std::uint64_t> 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<T> 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<T> 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<T> 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<T> 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<T> 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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<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_visits{0};
|
||||
std::atomic<std::uint64_t> 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<T> 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<T> 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<T> 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<T> 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<T> 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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
std::atomic<std::uint64_t> total_count{0};
|
||||
|
||||
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
auto mut_truthy_visitor = [&reference_cont](
|
||||
std::atomic<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<uint64_t>& 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<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
auto mut_visitor = [&reference_cont](std::atomic<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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 <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
void operator()(std::vector<T>& 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<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
auto mut_truthy_visitor = [&reference_cont](
|
||||
std::atomic<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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<uint64_t>& 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 <class X, class G, class F>
|
||||
void visit(X*, G gen, F visitor, test::random_generator rg)
|
||||
template <class X, class GF, class F>
|
||||
void visit(X*, GF gen_factory, F visitor, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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<raii, raii>(values.begin(), values.end());
|
||||
auto reference_cont = reference_container<X>(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 <class X, class G>
|
||||
void empty_visit(X*, G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
void empty_visit(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
auto gen = gen_factory.template get<X>();
|
||||
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 <class X, class G>
|
||||
void insert_and_visit(X*, G gen, test::random_generator rg)
|
||||
template <class X, class GF>
|
||||
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<X>();
|
||||
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<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_flat_set<raii, transp_hash,
|
||||
transp_key_equal>* 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))
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user