Begin work on constructors

This commit is contained in:
Christian Mazakas 2023-04-12 12:22:59 -07:00
parent b771278813
commit d9b62d24dc
4 changed files with 388 additions and 25 deletions

View File

@ -39,26 +39,26 @@
#define BOOST_UNORDERED_COMMA ,
#define BOOST_UNORDERED_LAST_ARG(Arg, Args) \
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_COMMA Args>>
#define BOOST_UNORDERED_LAST_ARG(Arg, Args) \
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_COMMA Args> >
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_LAST_ARG(Arg, Args))
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable:
std::is_constructible<
std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type>
>
{};
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type> >
{
};
template <class Key, class T> struct concurrent_map_types
{
@ -142,7 +142,11 @@ namespace boost {
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
concurrent_flat_map() : concurrent_flat_map(0) {}
concurrent_flat_map()
: concurrent_flat_map(detail::foa::default_bucket_count)
{
}
explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
@ -150,6 +154,23 @@ namespace boost {
{
}
template <class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
this->insert(f, l);
}
concurrent_flat_map(concurrent_flat_map const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
/// Capacity
///
@ -276,15 +297,13 @@ namespace boost {
template <class M> bool insert_or_assign(key_type const& k, M&& obj)
{
return table_.try_emplace_or_visit(
k, std::forward<M>(obj),
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class M> bool insert_or_assign(key_type&& k, M&& obj)
{
return table_.try_emplace_or_visit(
std::move(k), std::forward<M>(obj),
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
@ -293,8 +312,8 @@ namespace boost {
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
insert_or_assign(K&& k, M&& obj)
{
return table_.try_emplace_or_visit(
std::forward<K>(k), std::forward<M>(obj),
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
@ -455,16 +474,16 @@ namespace boost {
bool try_emplace_or_visit(K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(
std::forward<K>(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
bool try_emplace_or_cvisit(K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(
std::forward<K>(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
return table_.try_emplace_or_cvisit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
size_type erase(key_type const& k) { return table_.erase(k); }
@ -508,6 +527,16 @@ namespace boost {
///
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
/// Observers
///
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
} // namespace unordered
} // namespace boost

View File

@ -175,7 +175,14 @@ alias foa_tests :
foa_merge_exception_tests
;
local CFOA_TESTS = insert_tests erase_tests try_emplace_tests emplace_tests visit_tests ;
local CFOA_TESTS =
insert_tests
erase_tests
try_emplace_tests
emplace_tests
visit_tests
constructor_tests
;
for local test in $(CFOA_TESTS)
{

View File

@ -0,0 +1,256 @@
// Copyright (C) 2023 Christian Mazakas
// 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>
test::seed_t initialize_seed(4122023);
using test::default_generator;
using test::limited_range;
using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = std::allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
UNORDERED_AUTO_TEST (default_constructor) {
boost::unordered::concurrent_flat_map<raii, raii> x;
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
}
UNORDERED_AUTO_TEST (bucket_count_with_hasher_key_equal_and_allocator) {
raii::reset_counts();
{
map_type x(0);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
}
{
map_type x(0, hasher(1));
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
}
{
map_type x(0, hasher(1), key_equal(2));
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
}
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
}
raii::reset_counts();
}
namespace {
template <class G> void from_iterator_range(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(values.begin(), values.end());
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
raii::reset_counts();
}
{
map_type x(values.begin(), values.end(), 0);
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
raii::reset_counts();
}
{
map_type x(values.begin(), values.end(), 0, hasher(1));
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
raii::reset_counts();
}
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2));
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
raii::reset_counts();
}
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{});
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
raii::reset_counts();
}
}
template <class G> void copy_constructor(G gen, test::random_generator rg)
{
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
map_type y(x);
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
}
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{});
thread_runner(
values, [&x, &reference_map](
boost::span<typename decltype(values)::value_type> s) {
(void)s;
map_type y(x);
test_matches_reference(x, reference_map);
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
});
}
}
template <class G>
void copy_constructor_with_insertion(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
auto f = [&x, &values] {
std::this_thread::sleep_for(std::chrono::milliseconds(75));
for (auto const& val : values) {
x.insert(val);
}
};
std::thread t1(f);
std::thread t2(f);
thread_runner(
values, [&x, &reference_map, &values, rg](
boost::span<typename decltype(values)::value_type> s) {
(void)s;
map_type y(x);
BOOST_TEST_GT(y.size(), 0u);
BOOST_TEST_LE(y.size(), values.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
x.visit_all([&reference_map, rg](
typename map_type::value_type const& val) {
BOOST_TEST(reference_map.contains(val.first));
if (rg == sequential) {
BOOST_TEST_EQ(val.second, reference_map.find(val.first)->second);
}
});
});
t1.join();
t2.join();
}
}
} // namespace
// clang-format off
UNORDERED_TEST(
from_iterator_range,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor_with_insertion,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()

View File

@ -36,6 +36,67 @@ struct transp_key_equal
}
};
struct stateful_hash
{
int x_ = -1;
stateful_hash() = default;
stateful_hash(stateful_hash const&) = default;
stateful_hash(stateful_hash&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_hash(int const x) : x_{x} {}
template <class T> std::size_t operator()(T const& t) const noexcept
{
std::size_t h = static_cast<std::size_t>(x_);
boost::hash_combine(h, t);
return h;
}
bool operator==(stateful_hash const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(std::ostream& os, stateful_hash const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
};
struct stateful_key_equal
{
int x_ = -1;
stateful_key_equal() = default;
stateful_key_equal(stateful_key_equal const&) = default;
stateful_key_equal(stateful_key_equal&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_key_equal(int const x) : x_{x} {}
template <class T, class U> bool operator()(T const& t, U const& u) const
{
return t == u;
}
bool operator==(stateful_key_equal const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(
std::ostream& os, stateful_key_equal const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
};
struct raii
{
static std::atomic<std::uint32_t> default_constructor;
@ -226,4 +287,14 @@ 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);
}));
}
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP