forked from boostorg/unordered
Optimize emplace()
for exactly a value_type
or init_type
argument (#227)
* Add structs to count special member functions * Add failing emplace tests, that will pass after making the optimization * Optimize emplace() to not allocate when we already have a value_type or init_type * Fix newly failing cfoa tests
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
|
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
|
||||||
#include <boost/unordered/detail/serialization_version.hpp>
|
#include <boost/unordered/detail/serialization_version.hpp>
|
||||||
#include <boost/unordered/detail/static_assert.hpp>
|
#include <boost/unordered/detail/static_assert.hpp>
|
||||||
|
#include <boost/unordered/detail/type_traits.hpp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@@ -682,6 +683,14 @@ public:
|
|||||||
return construct_and_emplace(std::forward<Args>(args)...);
|
return construct_and_emplace(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optimization for value_type and init_type, to avoid constructing twice */
|
||||||
|
template<typename Value>
|
||||||
|
BOOST_FORCEINLINE auto emplace(Value&& x)->typename std::enable_if<
|
||||||
|
detail::is_similar_to_any<Value,value_type,init_type>::value,bool>::type
|
||||||
|
{
|
||||||
|
return emplace_impl(std::forward<Value>(x));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FORCEINLINE bool
|
BOOST_FORCEINLINE bool
|
||||||
insert(const init_type& x){return emplace_impl(x);}
|
insert(const init_type& x){return emplace_impl(x);}
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
#include <boost/core/serialization.hpp>
|
#include <boost/core/serialization.hpp>
|
||||||
#include <boost/unordered/detail/foa/core.hpp>
|
#include <boost/unordered/detail/foa/core.hpp>
|
||||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||||
|
#include <boost/unordered/detail/type_traits.hpp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -405,6 +406,16 @@ public:
|
|||||||
return emplace_impl(type_policy::move(x.value()));
|
return emplace_impl(type_policy::move(x.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optimization for value_type and init_type, to avoid constructing twice */
|
||||||
|
template <typename T>
|
||||||
|
BOOST_FORCEINLINE typename std::enable_if<
|
||||||
|
detail::is_similar_to_any<T, value_type, init_type>::value,
|
||||||
|
std::pair<iterator, bool> >::type
|
||||||
|
emplace(T&& x)
|
||||||
|
{
|
||||||
|
return emplace_impl(std::forward<T>(x));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Key,typename... Args>
|
template<typename Key,typename... Args>
|
||||||
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
|
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
|
||||||
Key&& x,Args&&... args)
|
Key&& x,Args&&... args)
|
||||||
|
@@ -147,6 +147,23 @@ namespace boost {
|
|||||||
!std::is_convertible<Key, const_iterator>::value;
|
!std::is_convertible<Key, const_iterator>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
using remove_cvref_t =
|
||||||
|
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||||
|
|
||||||
|
template <class T, class U>
|
||||||
|
using is_similar = std::is_same<remove_cvref_t<T>, remove_cvref_t<U> >;
|
||||||
|
|
||||||
|
template <class, class...> struct is_similar_to_any : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <class T, class U, class... Us>
|
||||||
|
struct is_similar_to_any<T, U, Us...>
|
||||||
|
: std::conditional<is_similar<T, U>::value, is_similar<T, U>,
|
||||||
|
is_similar_to_any<T, Us...> >::type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||||
// https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
|
// https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
|
||||||
// https://eel.is/c++draft/container.requirements#unord.req.general-243
|
// https://eel.is/c++draft/container.requirements#unord.req.general-243
|
||||||
|
@@ -85,6 +85,7 @@ local FCA_TESTS =
|
|||||||
contains_tests
|
contains_tests
|
||||||
copy_tests
|
copy_tests
|
||||||
deduction_tests
|
deduction_tests
|
||||||
|
emplace_smf_tests
|
||||||
emplace_tests
|
emplace_tests
|
||||||
equality_tests
|
equality_tests
|
||||||
equivalent_keys_tests
|
equivalent_keys_tests
|
||||||
@@ -201,6 +202,7 @@ local FOA_TESTS =
|
|||||||
assign_tests
|
assign_tests
|
||||||
insert_tests
|
insert_tests
|
||||||
insert_hint_tests
|
insert_hint_tests
|
||||||
|
emplace_smf_tests
|
||||||
emplace_tests
|
emplace_tests
|
||||||
erase_tests
|
erase_tests
|
||||||
merge_tests
|
merge_tests
|
||||||
|
@@ -968,11 +968,9 @@ namespace {
|
|||||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||||
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * values.size());
|
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||||
raii::destructor, value_type_cardinality * values.size());
|
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
|
||||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
}
|
}
|
||||||
@@ -998,11 +996,9 @@ namespace {
|
|||||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||||
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * values.size());
|
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||||
raii::destructor, value_type_cardinality * values.size());
|
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
|
||||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
}
|
}
|
||||||
@@ -1030,14 +1026,11 @@ namespace {
|
|||||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||||
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * values.size());
|
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::destructor,
|
raii::destructor, value_type_cardinality * reference_cont.size());
|
||||||
value_type_cardinality * values.size() +
|
|
||||||
value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor,
|
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||||
2 * value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
}
|
}
|
||||||
@@ -1063,14 +1056,11 @@ namespace {
|
|||||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||||
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * values.size());
|
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::destructor,
|
raii::destructor, value_type_cardinality * reference_cont.size());
|
||||||
value_type_cardinality * values.size() +
|
|
||||||
value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor,
|
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||||
2 * value_type_cardinality * reference_cont.size());
|
|
||||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
}
|
}
|
||||||
|
@@ -712,9 +712,9 @@ namespace {
|
|||||||
|
|
||||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor, value_type_cardinality * 11u);
|
raii::move_constructor, 0u);
|
||||||
}
|
}
|
||||||
check_raii_counts();
|
check_raii_counts();
|
||||||
|
|
||||||
@@ -730,9 +730,9 @@ namespace {
|
|||||||
|
|
||||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor, value_type_cardinality * 11u);
|
raii::move_constructor, 0u);
|
||||||
}
|
}
|
||||||
check_raii_counts();
|
check_raii_counts();
|
||||||
|
|
||||||
@@ -748,9 +748,9 @@ namespace {
|
|||||||
|
|
||||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor, value_type_cardinality * 11u);
|
raii::move_constructor, 0u);
|
||||||
}
|
}
|
||||||
check_raii_counts();
|
check_raii_counts();
|
||||||
|
|
||||||
@@ -766,9 +766,9 @@ namespace {
|
|||||||
|
|
||||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||||
BOOST_TEST_EQ(
|
BOOST_TEST_EQ(
|
||||||
raii::move_constructor, value_type_cardinality * 11u);
|
raii::move_constructor, 0u);
|
||||||
}
|
}
|
||||||
check_raii_counts();
|
check_raii_counts();
|
||||||
}
|
}
|
||||||
@@ -867,6 +867,9 @@ namespace {
|
|||||||
template <class X, class GF>
|
template <class X, class GF>
|
||||||
void flat_constructor(X*, GF gen_factory, test::random_generator rg)
|
void flat_constructor(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;
|
using allocator_type = typename X::allocator_type;
|
||||||
|
|
||||||
auto gen = gen_factory.template get<X>();
|
auto gen = gen_factory.template get<X>();
|
||||||
@@ -886,8 +889,8 @@ namespace {
|
|||||||
auto const old_cc = +raii::copy_constructor;
|
auto const old_cc = +raii::copy_constructor;
|
||||||
|
|
||||||
BOOST_TEST_EQ(old_dc, 0u);
|
BOOST_TEST_EQ(old_dc, 0u);
|
||||||
BOOST_TEST_GT(old_mc, 0u);
|
BOOST_TEST_EQ(old_mc, 0u);
|
||||||
BOOST_TEST_GT(old_cc, 0u);
|
BOOST_TEST_EQ(old_cc, value_type_cardinality * flat.size());
|
||||||
|
|
||||||
X x(std::move(flat));
|
X x(std::move(flat));
|
||||||
|
|
||||||
@@ -931,8 +934,8 @@ namespace {
|
|||||||
auto const old_cc = +raii::copy_constructor;
|
auto const old_cc = +raii::copy_constructor;
|
||||||
|
|
||||||
BOOST_TEST_EQ(old_dc, 0u);
|
BOOST_TEST_EQ(old_dc, 0u);
|
||||||
BOOST_TEST_GT(old_mc, 0u);
|
BOOST_TEST_EQ(old_mc, 0u);
|
||||||
BOOST_TEST_GT(old_cc, 0u);
|
BOOST_TEST_EQ(old_cc, 2u * value_type_cardinality * x.size());
|
||||||
|
|
||||||
flat_container<X> flat(std::move(x));
|
flat_container<X> flat(std::move(x));
|
||||||
|
|
||||||
|
@@ -170,6 +170,74 @@ namespace {
|
|||||||
}
|
}
|
||||||
} lvalue_emplace_or_visit;
|
} lvalue_emplace_or_visit;
|
||||||
|
|
||||||
|
struct copy_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);
|
||||||
|
if (b) {
|
||||||
|
++num_inserts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BOOST_TEST_EQ(num_inserts, x.size());
|
||||||
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
|
||||||
|
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
|
}
|
||||||
|
} copy_emplacer;
|
||||||
|
|
||||||
|
struct move_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& r : s) {
|
||||||
|
bool b = x.emplace(std::move(r));
|
||||||
|
if (b) {
|
||||||
|
++num_inserts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BOOST_TEST_EQ(num_inserts, x.size());
|
||||||
|
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||||
|
|
||||||
|
#if defined(BOOST_MSVC)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4127) // conditional expression is constant
|
||||||
|
#endif
|
||||||
|
if (std::is_same<T, typename X::value_type>::value &&
|
||||||
|
!std::is_same<typename X::key_type,
|
||||||
|
typename X::value_type>::value) { // map value_type can only be
|
||||||
|
// copied, no move
|
||||||
|
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||||
|
} else {
|
||||||
|
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||||
|
}
|
||||||
|
#if defined(BOOST_MSVC)
|
||||||
|
#pragma warning(pop) // C4127
|
||||||
|
#endif
|
||||||
|
BOOST_TEST_GT(raii::move_constructor, value_type_cardinality * x.size());
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||||
|
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||||
|
}
|
||||||
|
} move_emplacer;
|
||||||
|
|
||||||
template <class X, class GF, class F>
|
template <class X, class GF, class F>
|
||||||
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
|
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
|
||||||
{
|
{
|
||||||
@@ -220,7 +288,7 @@ UNORDERED_TEST(
|
|||||||
((map)(set))
|
((map)(set))
|
||||||
((value_type_generator_factory)(init_type_generator_factory))
|
((value_type_generator_factory)(init_type_generator_factory))
|
||||||
((lvalue_emplacer)(norehash_lvalue_emplacer)
|
((lvalue_emplacer)(norehash_lvalue_emplacer)
|
||||||
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
|
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
|
||||||
((default_generator)(sequential)(limited_range)))
|
((default_generator)(sequential)(limited_range)))
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@@ -33,7 +33,7 @@ static std::size_t const num_threads =
|
|||||||
|
|
||||||
std::atomic_bool should_throw{false};
|
std::atomic_bool should_throw{false};
|
||||||
|
|
||||||
constexpr std::uint32_t throw_threshold = 2500;
|
constexpr std::uint32_t throw_threshold = 2300;
|
||||||
constexpr std::uint32_t alloc_throw_threshold = 10;
|
constexpr std::uint32_t alloc_throw_threshold = 10;
|
||||||
|
|
||||||
void enable_exceptions() { should_throw = true; }
|
void enable_exceptions() { should_throw = true; }
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD
|
#define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD
|
||||||
|
|
||||||
#include <boost/core/lightweight_test.hpp>
|
#include <boost/core/lightweight_test.hpp>
|
||||||
|
#include <boost/container_hash/hash.hpp>
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
struct object_count
|
struct object_count
|
||||||
@@ -84,6 +85,111 @@ namespace test {
|
|||||||
return global_object_count.constructions - constructions_;
|
return global_object_count.constructions - constructions_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
struct smf_count
|
||||||
|
{
|
||||||
|
int default_constructions = 0;
|
||||||
|
int copy_constructions = 0;
|
||||||
|
int move_constructions = 0;
|
||||||
|
int copy_assignments = 0;
|
||||||
|
int move_assignments = 0;
|
||||||
|
int destructions = 0;
|
||||||
|
|
||||||
|
#if (BOOST_CXX_VERSION < 201402L) || (defined(_MSC_VER) && _MSC_VER < 1910)
|
||||||
|
smf_count() = default;
|
||||||
|
|
||||||
|
smf_count(int default_constructions_, int copy_constructions_,
|
||||||
|
int move_constructions_, int copy_assignments_, int move_assignments_,
|
||||||
|
int destructions_)
|
||||||
|
: default_constructions(default_constructions_),
|
||||||
|
copy_constructions(copy_constructions_),
|
||||||
|
move_constructions(move_constructions_),
|
||||||
|
copy_assignments(copy_assignments_),
|
||||||
|
move_assignments(move_assignments_), destructions(destructions_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void reset() { *this = smf_count(); }
|
||||||
|
|
||||||
|
void default_construct() { ++default_constructions; }
|
||||||
|
void copy_construct() { ++copy_constructions; }
|
||||||
|
void move_construct() { ++move_constructions; }
|
||||||
|
void copy_assign() { ++copy_assignments; }
|
||||||
|
void move_assign() { ++move_assignments; }
|
||||||
|
void destruct() { ++destructions; }
|
||||||
|
|
||||||
|
friend bool operator==(smf_count const& lhs, smf_count const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.default_constructions == rhs.default_constructions &&
|
||||||
|
lhs.copy_constructions == rhs.copy_constructions &&
|
||||||
|
lhs.move_constructions == rhs.move_constructions &&
|
||||||
|
lhs.copy_assignments == rhs.copy_assignments &&
|
||||||
|
lhs.move_assignments == rhs.move_assignments &&
|
||||||
|
lhs.destructions == rhs.destructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& out, smf_count const& c)
|
||||||
|
{
|
||||||
|
out << "[default_constructions: " << c.default_constructions
|
||||||
|
<< ", copy_constructions: " << c.copy_constructions
|
||||||
|
<< ", move_constructions: " << c.move_constructions
|
||||||
|
<< ", copy_assignments: " << c.copy_assignments
|
||||||
|
<< ", move_assignments: " << c.move_assignments
|
||||||
|
<< ", destructions: " << c.destructions << "]";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Tag> class smf_counted_object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static smf_count count;
|
||||||
|
static void reset_count() { count.reset(); }
|
||||||
|
|
||||||
|
smf_counted_object() : index_(++running_index)
|
||||||
|
{
|
||||||
|
count.default_construct();
|
||||||
|
}
|
||||||
|
smf_counted_object(smf_counted_object const& rhs) : index_(rhs.index_)
|
||||||
|
{
|
||||||
|
count.copy_construct();
|
||||||
|
}
|
||||||
|
smf_counted_object(smf_counted_object&& rhs) noexcept : index_(rhs.index_)
|
||||||
|
{
|
||||||
|
count.move_construct();
|
||||||
|
}
|
||||||
|
smf_counted_object& operator=(smf_counted_object const& rhs)
|
||||||
|
{
|
||||||
|
count.copy_assign();
|
||||||
|
index_ = rhs.index_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
smf_counted_object& operator=(smf_counted_object&& rhs) noexcept
|
||||||
|
{
|
||||||
|
count.move_assign();
|
||||||
|
index_ = rhs.index_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~smf_counted_object() { count.destruct(); }
|
||||||
|
|
||||||
|
friend bool operator==(
|
||||||
|
smf_counted_object const& lhs, smf_counted_object const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.index_ == rhs.index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::size_t hash_value(smf_counted_object const& x)
|
||||||
|
{
|
||||||
|
return boost::hash<int>()(x.index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int running_index;
|
||||||
|
int index_;
|
||||||
|
};
|
||||||
|
template <class Tag> smf_count smf_counted_object<Tag>::count = {};
|
||||||
|
template <class Tag> int smf_counted_object<Tag>::running_index = 0;
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
175
test/unordered/emplace_smf_tests.cpp
Normal file
175
test/unordered/emplace_smf_tests.cpp
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 Braden Ganetsky.
|
||||||
|
// 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/unordered.hpp"
|
||||||
|
|
||||||
|
#include "../helpers/count.hpp"
|
||||||
|
#include "../helpers/test.hpp"
|
||||||
|
|
||||||
|
namespace emplace_smf_tests {
|
||||||
|
using test::smf_count;
|
||||||
|
using test::smf_counted_object;
|
||||||
|
|
||||||
|
using counted_key = smf_counted_object<struct key_tag_>;
|
||||||
|
using counted_value = smf_counted_object<struct value_tag_>;
|
||||||
|
void reset_counts()
|
||||||
|
{
|
||||||
|
counted_key::reset_count();
|
||||||
|
counted_value::reset_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||||
|
static boost::unordered_flat_map<counted_key, counted_value>* test_smf_map;
|
||||||
|
static boost::unordered_node_map<counted_key, counted_value>*
|
||||||
|
test_smf_node_map;
|
||||||
|
static boost::unordered_flat_set<counted_value>* test_smf_set;
|
||||||
|
static boost::unordered_node_set<counted_value>* test_smf_node_set;
|
||||||
|
#define EMPLACE_SMF_TESTS_MAP_ARGS ((test_smf_map)(test_smf_node_map))
|
||||||
|
#define EMPLACE_SMF_TESTS_SET_ARGS ((test_smf_set)(test_smf_node_set))
|
||||||
|
#else
|
||||||
|
static boost::unordered_map<counted_key, counted_value>* test_smf_map;
|
||||||
|
static boost::unordered_set<counted_value>* test_smf_set;
|
||||||
|
#define EMPLACE_SMF_TESTS_MAP_ARGS ((test_smf_map))
|
||||||
|
#define EMPLACE_SMF_TESTS_SET_ARGS ((test_smf_set))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class X> static void emplace_smf_value_type_map(X*)
|
||||||
|
{
|
||||||
|
using container = X;
|
||||||
|
using value_type = typename container::value_type;
|
||||||
|
|
||||||
|
container x;
|
||||||
|
|
||||||
|
{
|
||||||
|
value_type val{counted_key{}, counted_value{}};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(val);
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
value_type val{counted_key{}, counted_value{}};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(std::move(val));
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(value_type{counted_key{}, counted_value{}});
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{1, 1, 1, 0, 0, 2}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
counted_key key{};
|
||||||
|
counted_value value{};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(value_type{std::move(key), std::move(value)});
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 1, 0, 0, 1}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNORDERED_TEST(emplace_smf_value_type_map, EMPLACE_SMF_TESTS_MAP_ARGS)
|
||||||
|
|
||||||
|
template <class X> static void emplace_smf_init_type_map(X*)
|
||||||
|
{
|
||||||
|
using container = X;
|
||||||
|
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||||
|
using init_type = typename container::init_type;
|
||||||
|
#else
|
||||||
|
using raw_key =
|
||||||
|
typename std::remove_const<typename container::key_type>::type;
|
||||||
|
using init_type = std::pair<raw_key, typename container::mapped_type>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
container x;
|
||||||
|
|
||||||
|
{
|
||||||
|
init_type val{counted_key{}, counted_value{}};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(val);
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
init_type val{counted_key{}, counted_value{}};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(std::move(val));
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(init_type{counted_key{}, counted_value{}});
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
counted_key key{};
|
||||||
|
counted_value value{};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(init_type{std::move(key), std::move(value)});
|
||||||
|
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNORDERED_TEST(emplace_smf_init_type_map, EMPLACE_SMF_TESTS_MAP_ARGS)
|
||||||
|
|
||||||
|
template <class X> static void emplace_smf_value_type_set(X*)
|
||||||
|
{
|
||||||
|
using container = X;
|
||||||
|
using value_type = typename container::value_type;
|
||||||
|
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||||
|
BOOST_STATIC_ASSERT(
|
||||||
|
std::is_same<value_type, typename container::init_type>::value);
|
||||||
|
#endif
|
||||||
|
BOOST_STATIC_ASSERT(std::is_same<value_type, counted_value>::value);
|
||||||
|
|
||||||
|
container x;
|
||||||
|
|
||||||
|
{
|
||||||
|
counted_value val{};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(val);
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
counted_value val{};
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(std::move(val));
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
x.clear();
|
||||||
|
reset_counts();
|
||||||
|
x.emplace(counted_value{});
|
||||||
|
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 1, 0, 0, 1}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNORDERED_TEST(emplace_smf_value_type_set, EMPLACE_SMF_TESTS_SET_ARGS)
|
||||||
|
} // namespace emplace_smf_tests
|
||||||
|
|
||||||
|
RUN_TESTS()
|
Reference in New Issue
Block a user