forked from boostorg/unordered
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1f8720dd2 | |||
| 67c5cdb3a6 | |||
| 75a94781d5 | |||
| 76185abe80 | |||
| 0d2751c5e1 | |||
| f493603f5c | |||
| 74ca1b0e74 | |||
| 7b16324869 | |||
| 6680e86b38 |
+12
-5
@@ -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(
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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})
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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