Compare commits

..

9 Commits

Author SHA1 Message Date
Peter Dimov c1f8720dd2 Add GCC 5 on 18.04 to Drone and GHA 2024-01-31 18:32:47 +02:00
Braden Ganetsky 67c5cdb3a6 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
2023-12-31 18:56:14 +01:00
joaquintides 75a94781d5 Fix/b2 5.0.0 (#228)
updated CI after b2 5.0.0 new requirements on the compiler used for self-build
2023-12-30 22:11:11 +01:00
joaquintides 76185abe80 extended workaround to GCC 4.9 2023-12-30 19:41:13 +01:00
Braden Ganetsky 0d2751c5e1 Extract at()'s throw_exception calls into noinline function (#223)
* Create function `detail::throw_out_of_range()` to make `at()` more inlineable

* Replace `boost::throw_exception()` with `detail::throw_out_of_range()`
2023-12-19 17:08:08 +01:00
joaquintides f493603f5c [skip ci] editorial 2023-12-01 12:22:48 +01:00
Peter Dimov 74ca1b0e74 Update test/CMakeLists.txt 2023-11-30 17:54:53 +02:00
Peter Dimov 7b16324869 Regenerate CMakeLists.txt 2023-11-30 17:40:41 +02:00
joaquintides 6680e86b38 [skip ci] typo 2023-11-25 09:49:24 +01:00
21 changed files with 506 additions and 82 deletions
+12 -5
View File
@@ -102,16 +102,16 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
[
linux_pipeline(
"Linux 14.04 GCC 4.8* 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11', ADDRMD: '32,64' },
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.8', CXXSTD: '11', ADDRMD: '32,64' },
"g++-4.8-multilib",
),
linux_pipeline(
"Linux 14.04 GCC 4.9 32/64",
"cppalliance/droneubuntu1404:1",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '11', ADDRMD: '32,64' },
"g++-4.9-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
@@ -120,6 +120,13 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 5 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-5', CXXSTD: '11,14', ADDRMD: '32,64' },
"g++-5-multilib",
),
linux_pipeline(
"Linux 18.04 GCC 6 32/64",
"cppalliance/droneubuntu1804:1",
@@ -468,7 +475,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
windows_pipeline(
"Windows VS2015 msvc-14.0",
"cppalliance/dronevs2015",
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' },
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest', 'B2_DONT_EMBED_MANIFEST': 1 },
),
windows_pipeline(
+2 -1
View File
@@ -52,6 +52,7 @@ jobs:
matrix:
include:
# Linux, gcc
- { compiler: gcc-5, cxxstd: '11,14', os: 'ubuntu-18.04', install: 'g++-5' }
- { compiler: gcc-7, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
@@ -174,7 +175,7 @@ jobs:
SOURCE_KEYS=(${{join(matrix.source_keys, ' ')}})
SOURCES=(${{join(matrix.sources, ' ')}})
# Add this by default
SOURCES+=(ppa:ubuntu-toolchain-r/test)
# SOURCES+=(ppa:ubuntu-toolchain-r/test)
for key in "${SOURCE_KEYS[@]}"; do
for i in {1..$NET_RETRY_COUNT}; do
wget -O - "$key" | sudo apt-key add - && break || sleep 10
+3 -6
View File
@@ -3,7 +3,7 @@
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.5...3.20)
cmake_minimum_required(VERSION 3.8...3.20)
project(boost_unordered VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
@@ -18,16 +18,13 @@ target_link_libraries(boost_unordered
Boost::config
Boost::container_hash
Boost::core
Boost::move
Boost::mp11
Boost::predef
Boost::preprocessor
Boost::static_assert
Boost::throw_exception
Boost::tuple
Boost::type_traits
)
target_compile_features(boost_unordered INTERFACE cxx_std_11)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
+1 -1
View File
@@ -38,7 +38,7 @@ Users can then declare a hash function `Hash` as avalanching either by embedding
into the definition of `Hash`, or directly by specializing `hash_is_avalanching<Hash>` to a class with
an embedded compile-time constant `value` set to `true`.
xref:unordered_flat_set[`boost::unordered_flat_set`] and xref:unordered_flat_map[`boost::unordered_flat_map`]
Open-addressing and concurrent containers
use the provided hash function `Hash` as-is if `hash_is_avalanching<Hash>::value` is `true`; otherwise, they
implement a bit-mixing post-processing stage to increase the quality of hashing at the expense of
extra computational cost.
+1 -1
View File
@@ -152,7 +152,7 @@ performing container-wide operations such as swapping or assignment.
By using atomic operations to access the group metadata, lookup is (group-level)
lock-free up to the point where an actual comparison needs to be done with an element
that has been previously SIMD-matched: only then it's the group's spinlock used.
that has been previously SIMD-matched: only then is the group's spinlock used.
Insertion uses the following _optimistic algorithm_:
@@ -28,6 +28,7 @@
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
#include <boost/unordered/detail/serialization_version.hpp>
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <cstddef>
#include <functional>
#include <initializer_list>
@@ -682,6 +683,14 @@ public:
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
insert(const init_type& x){return emplace_impl(x);}
@@ -18,6 +18,7 @@
#include <boost/core/serialization.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -405,6 +406,16 @@ public:
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>
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
Key&& x,Args&&... args)
@@ -0,0 +1,30 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP
#define BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/throw_exception.hpp>
#include <stdexcept>
namespace boost {
namespace unordered {
namespace detail {
BOOST_NOINLINE BOOST_NORETURN inline void throw_out_of_range(
char const* message)
{
boost::throw_exception(std::out_of_range(message));
}
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP
@@ -147,6 +147,23 @@ namespace boost {
!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
// https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
// https://eel.is/c++draft/container.requirements#unord.req.general-243
@@ -14,12 +14,12 @@
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/throw_exception.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
@@ -459,8 +459,8 @@ namespace boost {
// TODO: someday refactor this to conditionally serialize the key and
// include it in the error message
//
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_flat_map");
}
mapped_type const& at(key_type const& key) const
@@ -469,8 +469,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_flat_map");
}
template <class K>
@@ -483,8 +483,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_flat_map");
}
template <class K>
@@ -497,8 +497,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_flat_map");
}
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
+9 -8
View File
@@ -16,6 +16,7 @@
#include <boost/unordered/detail/map.hpp>
#include <boost/unordered/detail/serialize_fca_container.hpp>
#include <boost/unordered/detail/throw_exception.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/container_hash/hash.hpp>
@@ -1586,8 +1587,8 @@ namespace boost {
return p->value().second;
}
boost::throw_exception(
std::out_of_range("Unable to find key in unordered_map."));
boost::unordered::detail::throw_out_of_range(
"Unable to find key in unordered_map.");
}
template <class K, class T, class H, class P, class A>
@@ -1602,8 +1603,8 @@ namespace boost {
return p->value().second;
}
boost::throw_exception(
std::out_of_range("Unable to find key in unordered_map."));
boost::unordered::detail::throw_out_of_range(
"Unable to find key in unordered_map.");
}
template <class K, class T, class H, class P, class A>
@@ -1620,8 +1621,8 @@ namespace boost {
return p->value().second;
}
boost::throw_exception(
std::out_of_range("Unable to find key in unordered_map."));
boost::unordered::detail::throw_out_of_range(
"Unable to find key in unordered_map.");
}
template <class K, class T, class H, class P, class A>
@@ -1638,8 +1639,8 @@ namespace boost {
return p->value().second;
}
boost::throw_exception(
std::out_of_range("Unable to find key in unordered_map."));
boost::unordered::detail::throw_out_of_range(
"Unable to find key in unordered_map.");
}
template <class K, class T, class H, class P, class A>
@@ -15,12 +15,12 @@
#include <boost/unordered/detail/foa/node_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/throw_exception.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_map_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
@@ -554,8 +554,8 @@ namespace boost {
// TODO: someday refactor this to conditionally serialize the key and
// include it in the error message
//
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_node_map");
}
mapped_type const& at(key_type const& key) const
@@ -564,8 +564,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_node_map");
}
template <class K>
@@ -578,8 +578,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_node_map");
}
template <class K>
@@ -592,8 +592,8 @@ namespace boost {
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
boost::unordered::detail::throw_out_of_range(
"key was not found in unordered_node_map");
}
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
+1 -1
View File
@@ -9,7 +9,7 @@ if(HAVE_BOOST_TEST)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(BOOST_TEST_LINK_LIBRARIES Boost::unordered Boost::core Boost::concept_check)
set(BOOST_TEST_LINK_LIBRARIES Boost::unordered Boost::core Boost::concept_check Boost::tuple)
function(fca_tests)
boost_test(PREFIX boost_unordered ${ARGN})
+2
View File
@@ -85,6 +85,7 @@ local FCA_TESTS =
contains_tests
copy_tests
deduction_tests
emplace_smf_tests
emplace_tests
equality_tests
equivalent_keys_tests
@@ -201,6 +202,7 @@ local FOA_TESTS =
assign_tests
insert_tests
insert_hint_tests
emplace_smf_tests
emplace_tests
erase_tests
merge_tests
+12 -22
View File
@@ -968,11 +968,9 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(
raii::destructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * reference_cont.size());
raii::copy_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(raii::destructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::copy_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(
raii::copy_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(
raii::destructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * reference_cont.size());
raii::copy_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(raii::destructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::copy_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(
raii::copy_constructor, value_type_cardinality * values.size());
raii::copy_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(
raii::destructor,
value_type_cardinality * values.size() +
value_type_cardinality * reference_cont.size());
raii::destructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(
raii::move_constructor,
2 * value_type_cardinality * reference_cont.size());
raii::move_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(raii::copy_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(
raii::copy_constructor, value_type_cardinality * values.size());
raii::copy_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(
raii::destructor,
value_type_cardinality * values.size() +
value_type_cardinality * reference_cont.size());
raii::destructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(
raii::move_constructor,
2 * value_type_cardinality * reference_cont.size());
raii::move_constructor, value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
+15 -12
View File
@@ -712,9 +712,9 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
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(
raii::move_constructor, value_type_cardinality * 11u);
raii::move_constructor, 0u);
}
check_raii_counts();
@@ -730,9 +730,9 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
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(
raii::move_constructor, value_type_cardinality * 11u);
raii::move_constructor, 0u);
}
check_raii_counts();
@@ -748,9 +748,9 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
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(
raii::move_constructor, value_type_cardinality * 11u);
raii::move_constructor, 0u);
}
check_raii_counts();
@@ -766,9 +766,9 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
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(
raii::move_constructor, value_type_cardinality * 11u);
raii::move_constructor, 0u);
}
check_raii_counts();
}
@@ -867,6 +867,9 @@ namespace {
template <class X, class GF>
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;
auto gen = gen_factory.template get<X>();
@@ -886,8 +889,8 @@ namespace {
auto const old_cc = +raii::copy_constructor;
BOOST_TEST_EQ(old_dc, 0u);
BOOST_TEST_GT(old_mc, 0u);
BOOST_TEST_GT(old_cc, 0u);
BOOST_TEST_EQ(old_mc, 0u);
BOOST_TEST_EQ(old_cc, value_type_cardinality * flat.size());
X x(std::move(flat));
@@ -931,8 +934,8 @@ namespace {
auto const old_cc = +raii::copy_constructor;
BOOST_TEST_EQ(old_dc, 0u);
BOOST_TEST_GT(old_mc, 0u);
BOOST_TEST_GT(old_cc, 0u);
BOOST_TEST_EQ(old_mc, 0u);
BOOST_TEST_EQ(old_cc, 2u * value_type_cardinality * x.size());
flat_container<X> flat(std::move(x));
+69 -1
View File
@@ -170,6 +170,74 @@ namespace {
}
} 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>
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
{
@@ -220,7 +288,7 @@ UNORDERED_TEST(
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((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)))
// clang-format on
+1 -1
View File
@@ -33,7 +33,7 @@ static std::size_t const num_threads =
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;
void enable_exceptions() { should_throw = true; }
+11 -4
View File
@@ -156,6 +156,9 @@ namespace {
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// some versions of old gcc have trouble eliding copies here
// https://godbolt.org/z/Ebo6TbvaG
#elif BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000)
// seemingly same problem, though the snippet above does not reveal it
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
@@ -488,8 +491,10 @@ namespace {
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -525,8 +530,10 @@ namespace {
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
+107 -1
View File
@@ -7,6 +7,7 @@
#define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD
#include <boost/core/lightweight_test.hpp>
#include <boost/container_hash/hash.hpp>
namespace test {
struct object_count
@@ -84,6 +85,111 @@ namespace test {
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
+175
View 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()