Compare commits

..

63 Commits

Author SHA1 Message Date
Peter Dimov 57546ed7e3 Switch all Cygwin64 jobs to Debug, because Release times out 2024-09-11 16:52:49 +03:00
Peter Dimov 6067d9e515 Disable tests using Boost.Process under MinGW as well 2024-09-11 12:15:37 +03:00
Peter Dimov ad4f00a825 Update test/Jamfile.v2 2024-09-11 02:16:05 +03:00
Peter Dimov d1c166558a Update test/Jamfile.v2 2024-09-10 22:05:00 +03:00
Peter Dimov d8a75accff Update build.jam 2024-09-10 22:03:05 +03:00
Rene Rivera cdb31c8309 Sync from upstream. 2024-09-09 15:23:31 -05:00
Braden Ganetsky f48ae7f7f4 Merge pull request #281 from k3DW/instantiate-classes
Put instantiate_[fca] classes into their respective test file
2024-09-08 12:45:08 -05:00
Rene Rivera 843d6df58c Sync from upstream. 2024-09-06 08:42:51 -05:00
joaquintides 95f36f334d updated README.md 2024-09-06 10:00:28 +02:00
Braden Ganetsky 3f4d5fc567 Put instantiate_[fca] classes into their respective test file, instead of in the headers 2024-09-03 21:17:36 -05:00
Rene Rivera 9a2483dd2c Sync from upstream. 2024-09-03 20:09:40 -05:00
joaquintides cd9a592f00 Fixed std::initializer_list assignment issues for open-addressing containers (#277)
* fixed #276

* used range insert, stylistic this->

* assigned non-empty std::initializer_lists
2024-09-02 18:56:13 +02:00
Rene Rivera aa67315922 Sync from upstream. 2024-08-27 09:24:49 -05:00
Braden Ganetsky 21937249c4 Merge pull request #275 from k3DW/cygwin
Fix compiling on Cygwin for tests using Interprocess
2024-08-26 22:05:04 -05:00
Braden Ganetsky 5dd2068b62 Fix tests using Interprocess not compiling on Cygwin 2024-08-26 13:43:33 -05:00
Rene Rivera 85098f0506 Sync from upstream. 2024-08-26 07:13:46 -05:00
Braden Ganetsky 1b0037135c Add concurrent_node containers to the GDB pretty-printer script 2024-08-25 13:01:49 -05:00
joaquintides f734e399e3 Feature/concurrent node containers (#271)
* added concurrent node containers

* removed spurious typename

* added missing includes

* avoided unused param warning

* worked around Clang bug

* s/{}/() to work around GCC4.8 problems with aggregate initialization

* used /bigobj for cfoa/visit_tests.cpp

* suppressed localized maybe-uninitialized warnings

* fixed comments

* added /bigobj to cfoa/insert_tests.cpp

* instrumented double exact comparison to spot a spurious error

* fixed pedantic error

* refactored byte_span machinery

* compromised on sub-epsilon equality for doubles that should be identical

* documented boost::concurrent_node_(map|set)

* added concurrent_node_set

* added missing AlternativeType

* tested empty node insertion

* tested node_handle allocator management

* added nonassignable_allocator and node_handle_allocator_swap_tests

* fixed warning disabling

* silenced spurious GCC warning

* broadened scope of previous pragma

* broadened even more

* worked around spurious constexpr-related msvc-14.0 bug
https://godbolt.org/z/v78545Ebf

* added workaround back

* replaced previous workaround with built-in one

* added workaround back on top of built-in solution (which doesn't work 100% of the time)
2024-08-25 18:34:58 +02:00
Rene Rivera 2afef71107 Sync from upstream. 2024-08-23 11:12:09 -05:00
Braden Ganetsky 35bdabf259 Fix typo in docs 2024-08-23 00:03:49 -05:00
Braden Ganetsky 1ebe692957 Merge pull request #274 from k3DW/gdb
Write GDB pretty-printers for all containers and iterators
2024-08-22 23:45:23 -05:00
Braden Ganetsky 111a503195 Generate inline assembly header file for automatic GDB pretty-printer script inclusion 2024-08-22 18:56:24 -05:00
Braden Ganetsky 5206ac46a3 Output the open-addressing containers' stats through a GDB xmethod 2024-08-17 14:37:06 -05:00
Braden Ganetsky 0bca47c690 Write docs for GDB pretty-printers 2024-08-17 14:37:05 -05:00
Braden Ganetsky 7f9aa8505f Fix containers of reference type 2024-08-17 14:37:05 -05:00
Braden Ganetsky ae364ea311 Add fancy pointer support to GDB pretty-printers 2024-08-17 14:37:05 -05:00
Braden Ganetsky f415d17e58 Write GDB pretty-printers for FOA containers, without fancy pointers 2024-08-17 14:37:05 -05:00
Braden Ganetsky 10fd6c8647 Write GDB pretty-printers for FCA containers, without fancy pointers 2024-08-17 14:37:05 -05:00
Braden Ganetsky 45e50f6cd2 Use references in test file, so we can check the debugger display without dereferencing the container 2024-08-17 14:37:05 -05:00
Braden Ganetsky 600dd0a8e2 Rename 'natvis_tests.cpp' to 'debuggability/visualization_tests.cpp', to be used with other kinds of visualizations 2024-08-17 14:37:05 -05:00
Rene Rivera a6a0cd0a13 Sync from upstream. 2024-08-08 22:00:05 -05:00
Braden Ganetsky a39cf60e93 Merge pull request #269 from k3DW/191
static_assert on the constructibility of the containers' types
2024-08-08 11:57:22 -05:00
Braden Ganetsky a14b159665 Add missing calls to as_const(), otherwise we may call a 'T(T&)' constructor instead of the intended 'T(T const&)' 2024-08-07 16:15:36 -05:00
Braden Ganetsky 793fad5620 Run clang-format on the 'types' files 2024-08-07 16:09:15 -05:00
Braden Ganetsky c117f4448f static_assert on the constructibility of the containers' types 2024-08-07 16:09:15 -05:00
Braden Ganetsky 65f3ea60dd Use as_const to remove any possibility of use-after-move (#272) 2024-08-06 15:48:13 +02:00
Rene Rivera 7645e4f20d Sync from upstream. 2024-07-25 17:18:43 -05:00
Braden Ganetsky cd4c1f3713 Fix Boost.Config includes in natvis_tests.cpp (#268) 2024-07-24 09:05:21 +02:00
Rene Rivera 32ce058ebe Move inter-lib dependencies to a project variable and into the build targets. 2024-07-23 22:34:23 -05:00
Rene Rivera 4c6d0211fb Update copyright dates. 2024-07-20 22:52:04 -05:00
Rene Rivera 1edbe3fdbe Change all <source> references to <library>. 2024-07-20 21:27:30 -05:00
Rene Rivera 42579dee10 Add uuid test dependency. 2024-07-19 16:00:30 -05:00
Rene Rivera 0928b3d016 Add process test dependency. 2024-07-19 12:58:21 -05:00
Rene Rivera 5d7786b47e Add interprocess test dependency. 2024-07-19 08:11:35 -05:00
Rene Rivera 228fede498 Sync from upstream. 2024-07-18 09:28:57 -05:00
joaquintides 1d1f0d306d Feature/nonconst set visit (#265)
* Made some boost::concurrent_flat_set operations exclusive-locked

* unnamed unused args

* fixed PR number

* replaced homemade mechanism with boost::compat::latch
2024-07-16 19:50:08 +02:00
Braden Ganetsky 7ddd562532 Implement natvis for fancy pointers (#262)
* Add file for manual natvis testing

* Equip natvis file to allow fancy pointers

* Update docs

* [skip ci] add links to natvis docs
2024-07-15 19:42:28 +02:00
Dmitry 3ad8ac4c34 fix asan and tsan jobs on Drone (#264) 2024-07-15 12:38:32 +02:00
joaquintides ac1a2b4d23 fixed syntax error 2024-07-14 16:43:04 +02:00
joaquintides e354fb432b configured ASAN/TSAN jobs as privileged 2024-07-14 16:41:21 +02:00
Rene Rivera 88535dca1f Sync from upstream. 2024-07-12 08:54:23 -05:00
Rene Rivera 3d9b24019d Bump B2 require to 5.2 2024-06-14 11:33:56 -05:00
Rene Rivera 0387791dc1 Sync from upstream. 2024-06-11 08:46:53 -05:00
Rene Rivera 4126a56528 Sync from upstream. 2024-06-10 21:40:32 -05:00
Rene Rivera 38acf9c619 Sync from upstream. 2024-06-02 13:50:31 -05:00
Rene Rivera e5d89f1a15 Update dependencies. 2024-05-13 21:48:23 -05:00
Rene Rivera 8630a588bd Sync from upstream. 2024-05-10 20:47:21 -05:00
Rene Rivera 390efc275f Add requires-b2 check to top-level build file. 2024-05-05 09:00:01 -05:00
Rene Rivera 3868496ac9 Sync from upstream. 2024-05-02 22:47:22 -05:00
Rene Rivera a1f9097f2b Sync from upstream. 2024-04-10 07:57:16 -05:00
Rene Rivera f177f437b9 Switch to library requirements instead of source. As source puts extra source in install targets. 2024-03-29 21:16:00 -05:00
Rene Rivera a3873dc19b Sync from upstream. 2024-03-16 09:01:13 -05:00
Rene Rivera 89515ee604 Make the library modular usable. 2024-03-11 08:38:17 -05:00
86 changed files with 9013 additions and 475 deletions
+4 -4
View File
@@ -107,14 +107,14 @@ environment:
B2_TOOLSET: gcc
B2_VARIANT: release
- FLAVOR: Cygwin (64 bit, C++11, Release)
- FLAVOR: Cygwin (64 bit, C++11, Debug)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 11
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
B2_VARIANT: debug
- FLAVOR: Cygwin (64 bit, C++14, Debug)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
@@ -125,14 +125,14 @@ environment:
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: debug
- FLAVOR: Cygwin (64 bit, C++17, Release)
- FLAVOR: Cygwin (64 bit, C++17, Debug)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 17
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
B2_VARIANT: debug
- FLAVOR: Cygwin (64 bit, C++2a, Debug)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
+1
View File
@@ -30,6 +30,7 @@ local linux_pipeline(name, image, environment, packages = "", sources = [], arch
name: "everything",
image: image,
environment: environment,
privileged: true,
commands:
[
'set -e',
+5
View File
@@ -14,6 +14,11 @@ DRONE_BUILD_DIR=$(pwd)
BOOST_BRANCH=develop
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
if [[ $(uname) == "Linux" && ( "$TSAN" == 1 || "$ASAN" == 1 ) ]]; then
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
sudo sysctl vm.mmap_rnd_bits=28
fi
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
+10 -4
View File
@@ -25,6 +25,10 @@ deviate from the standard in exchange for top performance.</ul>
<ul>High performance for multithreaded scenarios. Introducing a new non-standard, iterator-free API.</ul>
**`boost::concurrent_node_set` `boost::concurrent_node_map`**
<ul>Variations of <code>boost::concurrent_flat_(set|map)</code> providing pointer stability.</ul>
## Learn about Boost.Unordered
* [Online documentation](https://boost.org/libs/unordered)
@@ -34,16 +38,18 @@ deviate from the standard in exchange for top performance.</ul>
* [Inside `boost::unordered_flat_map`](https://bannalia.blogspot.com/2022/11/inside-boostunorderedflatmap.html)
* [Inside `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/07/inside-boostconcurrentflatmap.html)
* [Bulk visitation in `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/10/bulk-visitation-in-boostconcurrentflatm.html)
## Get the library
* Debugging visualizers for Boost.Unordered:
* [Natvis for boost::unordered_map, and how to use &lt;Intrinsic&gt; elements](https://blog.ganets.ky/NatvisForUnordered/)
* [Natvis for boost::concurrent_flat_map, and why fancy pointers are hard](https://blog.ganets.ky/NatvisForUnordered2/)
* [Visualizing boost::unordered_map in GDB, with pretty-printer customization points](https://blog.ganets.ky/PrettyPrinter/)
Boost.Unordered can be installed in a number of ways:
* [Download Boost](https://www.boost.org/users/download/) and you're ready to go (this is a header-only library requiring no building).
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.83):
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.86):
```
[requires]
boost/[>=1.83.0]
boost/[>=1.86.0]
```
<ul>If you're not using any compiled Boost library, the following will skip building altogether:</ul>
+26
View File
@@ -0,0 +1,26 @@
# Copyright 2023-2024 René Ferdinand Rivera Morell
# Copyright 2024 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
require-b2 5.2 ;
constant boost_dependencies :
/boost/assert//boost_assert
/boost/config//boost_config
/boost/container_hash//boost_container_hash
/boost/core//boost_core
/boost/mp11//boost_mp11
/boost/predef//boost_predef
/boost/throw_exception//boost_throw_exception
;
project /boost/unordered ;
explicit
[ alias boost_unordered : : : : <include>include <library>$(boost_dependencies) ]
[ alias all : boost_unordered test ]
;
call-if : boost-library unordered
;
+1 -1
View File
@@ -126,7 +126,7 @@ h|*Method* h|*Description*
|`float max_load_factor(float z)`
|Changes the container's maximum load factor, using `z` as a hint. +
**Open-addressing containers:** this function does nothing: users are not allowed to change the maximum load factor.
**Open-addressing and concurrent containers:** this function does nothing: users are not allowed to change the maximum load factor.
|`void rehash(size_type n)`
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
+12
View File
@@ -6,6 +6,18 @@
:github-pr-url: https://github.com/boostorg/unordered/pull
:cpp: C++
== Release 1.87.0 - Major update
* Added concurrent, node-based containers `boost::concurrent_node_map` and `boost::concurrent_node_set`.
* Made visitation exclusive-locked within certain
`boost::concurrent_flat_set` operations to allow for safe mutable modification of elements
({github-pr-url}/265[PR#265^]).
* In Visual Studio Natvis, supported any container with an allocator that uses fancy pointers. This applies to any fancy pointer type, as long as the proper Natvis customization point "Intrinsic" functions are written for the fancy pointer type.
* Added GDB pretty-printers for all containers and iterators. For a container with an allocator that uses fancy pointers, these only work if the proper pretty-printer is written for the fancy pointer type itself.
* Fixed `std::initializer_list` assignment issues for open-addressing containers
({github-pr-url}/277[PR#277^]).
== Release 1.86.0
* Added container `pmr` aliases when header `<memory_resource>` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type.
+5 -3
View File
@@ -89,7 +89,8 @@ The main differences with C++ unordered associative containers are:
== Concurrent Containers
There is currently no specification in the C++ standard for this or any other type of concurrent
data structure. The APIs of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`
data structure. The APIs of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
`boost::concurrent_flat_map`/`boost::concurrent_node_map`
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
with the crucial difference that iterators are not provided
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
@@ -105,7 +106,7 @@ In a non-concurrent unordered container, iterators serve two main purposes:
* Access to an element previously located via lookup.
* Container traversal.
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
In place of iterators, Boost.Unordered concurrent containers use _internal visitation_
facilities as a thread-safe substitute. Classical operations returning an iterator to an
element already existing in the container, like for instance:
@@ -141,7 +142,8 @@ respectively, here visitation is granted mutable or const access depending on
the constness of the member function used (there are also `*cvisit` overloads for
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
One notable operation not provided by `boost::concurrent_flat_map`/`boost::concurrent_node_map`
is `operator[]`/`at`, which can be
replaced, if in a more convoluted manner, by
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
+29 -10
View File
@@ -3,7 +3,8 @@
:idprefix: concurrent_
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
Boost.Unordered provides `boost::concurrent_node_set`, `boost::concurrent_node_map`,
`boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
hash tables that allow concurrent write/read access from
different threads without having to implement any synchronzation mechanism on the user's side.
@@ -43,7 +44,7 @@ logical cores in the CPU).
== Visitation-based API
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
The first thing a new user of Boost.Unordered concurrent containers
will notice is that these classes _do not provide iterators_ (which makes them technically
not https://en.cppreference.com/w/cpp/named_req/Container[Containers^]
in the C++ standard sense). The reason for this is that iterators are inherently
@@ -62,7 +63,7 @@ thread issues an `m.erase(k)` operation between A and B. There are designs that
can remedy this by making iterators lock the element they point to, but this
approach lends itself to high contention and can easily produce deadlocks in a program.
`operator[]` has similar concurrency issues, and is not provided by
`boost::concurrent_flat_map` either. Instead, element access is done through
`boost::concurrent_flat_map`/`boost::concurrent_node_map` either. Instead, element access is done through
so-called _visitation functions_:
[source,c++]
@@ -112,7 +113,7 @@ if (found) {
}
----
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
Visitation is prominent in the API provided by concurrent containers, and
many classical operations have visitation-enabled variations:
[source,c++]
@@ -125,13 +126,15 @@ m.insert_or_visit(x, [](auto& y) {
----
Note that in this last example the visitation function could actually _modify_
the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
the element: as a general rule, operations on a concurrent map `m`
will grant visitation functions const/non-const access to the element depending on whether
`m` is const/non-const. Const access can be always be explicitly requested
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
in higher parallelization. For `boost::concurrent_flat_set`, on the other hand,
in higher parallelization. For concurrent sets, on the other hand,
visitation is always const access.
Consult the references of
xref:#concurrent_node_set[`boost::concurrent_node_set`],
xref:#concurrent_flat_map[`boost::concurrent_node_map`],
xref:#concurrent_flat_set[`boost::concurrent_flat_set`] and
xref:#concurrent_flat_map[`boost::concurrent_flat_map`]
for the complete list of visitation-enabled operations.
@@ -245,7 +248,7 @@ may yield worse performance.
== Blocking Operations
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
Concurrent containers can be copied, assigned, cleared and merged just like any other
Boost.Unordered container. Unlike most other operations, these are _blocking_,
that is, all other threads are prevented from accesing the tables involved while a copy, assignment,
clear or merge operation is in progress. Blocking is taken care of automatically by the library
@@ -258,9 +261,25 @@ reserving space in advance of bulk insertions will generally speed up the proces
== Interoperability with non-concurrent containers
As open-addressing and concurrent containers are based on the same internal data structure,
`boost::unordered_flat_set` and `boost::unordered_flat_map` can
be efficiently move-constructed from `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
respectively, and vice versa.
they can be efficiently move-constructed from their non-concurrent counterpart, and vice versa.
[caption=, title='Table {counter:table-counter}. Concurrent/non-concurrent interoperatibility']
[cols="1,1", frame=all, grid=all]
|===
^|`boost::concurrent_node_set`
^|`boost::unordered_node_set`
^|`boost::concurrent_node_map`
^|`boost::unordered_node_map`
^|`boost::concurrent_flat_set`
^|`boost::unordered_flat_set`
^|`boost::concurrent_flat_map`
^|`boost::unordered_flat_map`
|===
This interoperability comes handy in multistage scenarios where parts of the data processing happen
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
we want to construct a histogram from a huge input vector of words:
+7 -3
View File
@@ -389,9 +389,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
only when an internal rehashing is issued.
Access or modification of an element of a `boost::concurrent_flat_map` passed by reference to a
user-provided visitation function do not introduce data races when the visitation function
is executed internally by the `boost::concurrent_flat_map`.
When executed internally by a `boost::concurrent_flat_map`, the following operations by a
user-provided visitation function on the element passed do not introduce data races:
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`.
+25 -3
View File
@@ -98,25 +98,35 @@ namespace boost {
// visitation
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f);
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f) const;
template<class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const key_type& k, F f) const;
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f);
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const;
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const;
template<class FwdIterator, class F>
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f);
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const;
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const;
template<class ExecutionPolicy, class F>
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F>
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F>
void xref:#concurrent_flat_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f);
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f) const;
template<class F> bool xref:#concurrent_flat_set_cvisit_while[cvisit_while](F f) const;
template<class ExecutionPolicy, class F>
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F>
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F>
@@ -340,9 +350,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
only when an internal rehashing is issued.
Access or modification of an element of a `boost::concurrent_flat_set` passed by reference to a
user-provided visitation function do not introduce data races when the visitation function
is executed internally by the `boost::concurrent_flat_set`.
When executed internally by a `boost::concurrent_flat_set`, the following operations by a
user-provided visitation function on the element passed do not introduce data races:
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`.
@@ -708,8 +722,10 @@ Concurrency:;; Blocking on `*this`.
==== [c]visit
```c++
template<class F> size_t visit(const key_type& k, F f);
template<class F> size_t visit(const key_type& k, F f) const;
template<class F> size_t cvisit(const key_type& k, F f) const;
template<class K, class F> size_t visit(const K& k, F f);
template<class K, class F> size_t visit(const K& k, F f) const;
template<class K, class F> size_t cvisit(const K& k, F f) const;
```
@@ -725,6 +741,8 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
==== Bulk visit
```c++
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
@@ -758,6 +776,7 @@ Returns:;; The number of elements visited.
==== [c]visit_all
```c++
template<class F> size_t visit_all(F f);
template<class F> size_t visit_all(F f) const;
template<class F> size_t cvisit_all(F f) const;
```
@@ -772,6 +791,7 @@ Returns:;; The number of elements visited.
==== Parallel [c]visit_all
```c++
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
```
@@ -792,6 +812,7 @@ Unsequenced execution policies are not allowed.
==== [c]visit_while
```c++
template<class F> bool visit_while(F f);
template<class F> bool visit_while(F f) const;
template<class F> bool cvisit_while(F f) const;
```
@@ -807,6 +828,7 @@ Returns:;; `false` iff `f` ever returns `false`.
==== Parallel [c]visit_while
```c++
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
```
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+76 -2
View File
@@ -5,7 +5,13 @@
== Visual Studio Natvis
All containers and iterators have custom visualizations in the Natvis framework, as long as their allocator uses regular raw pointers. Any container or iterator with an allocator using fancy pointers does not have a custom visualization right now.
All containers and iterators have custom visualizations in the Natvis framework.
=== Using in your project
To visualize Boost.Unordered containers in the Natvis framework in your project, simply add the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis] to your Visual Studio project as an "Existing Item".
=== Visualization structure
The visualizations mirror those for the standard unordered containers. A container has a maximum of 100 elements displayed at once. Each set element has its item name listed as `[i]`, where `i` is the index in the display, starting at `0`. Each map element has its item name listed as `[\{key-display}]` by default. For example, if the first element is the pair `("abc", 1)`, the item name will be `["abc"]`. This behaviour can be overridden by using the view "ShowElementsByIndex", which switches the map display behaviour to name the elements by index. This same view name is used in the standard unordered containers.
@@ -13,4 +19,72 @@ By default, the closed-addressing containers will show the `[hash_function]` and
By default, the open-addressing containers will show the `[hash_function]`, `[key_eq]`, `[allocator]`, and the elements. Using the view "simple" shows only the elements, with no other items present. Both the SIMD and the non-SIMD implementations are viewable through the Natvis framework.
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `\{ end iterator }`.
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `{ end iterator }`.
=== Fancy pointers
The container visualizations also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has natvis customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis].
== GDB Pretty-Printers
All containers and iterators have a custom GDB pretty-printer.
=== Using in your project
Always, when using pretty-printers, you must enable pretty-printing like below. This is typically a one-time setup.
```plaintext
(gdb) set print pretty on
```
By default, if you compile into an ELF binary format, your binary will contain the Boost.Unordered pretty-printers. To use the embedded pretty-printers, ensure you allow auto-loading like below. This must be done every time you load GDB, or add it to a ".gdbinit" file.
```plaintext
(gdb) add-auto-load-safe-path [/path/to/executable]
```
You can choose to compile your binary _without_ embedding the pretty-printers by defining `BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS`, which disables the embedded GDB pretty-printers for all Boost libraries that have this feature.
You can load the pretty-printers externally from the non-embedded Python script. Add the script, link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py], using the `source` command as shown below.
```plaintext
(gdb) source [/path/to/boost]/libs/unordered/extra/boost_unordered_printers.py
```
=== Visualization structure
The visualizations mirror the standard unordered containers. The map containers display an association from key to mapped value. The set containers display an association from index to value. An iterator is either displayed with its item, or as an end iterator. Here is what may be shown for an example `boost::unordered_map`, an example `boost::unordered_set`, and their respective begin and end iterators.
```plaintext
(gdb) print example_unordered_map
$1 = boost::unordered_map with 3 elements = {["C"] = "c", ["B"] = "b", ["A"] = "a"}
(gdb) print example_unordered_map_begin
$2 = iterator = { {first = "C", second = "c"} }
(gdb) print example_unordered_map_end
$3 = iterator = { end iterator }
(gdb) print example_unordered_set
$4 = boost::unordered_set with 3 elements = {[0] = "c", [1] = "b", [2] = "a"}
(gdb) print example_unordered_set_begin
$5 = iterator = { "c" }
(gdb) print example_unordered_set_end
$6 = iterator = { end iterator }
```
The other containers are identical other than replacing "`boost::unordered_{map|set}`" with the appropriate template name when displaying the container itself. Note that each sub-element (i.e. the key, the mapped value, or the value) is displayed based on its own printing settings which may include its own pretty-printer.
Both the SIMD and the non-SIMD implementations are viewable through the GDB pretty-printers.
For open-addressing containers where xref:#hash_quality_container_statistics[container statistics] are enabled, you can obtain these statistics by calling `get_stats()` on the container, from within GDB. This is overridden in GDB as an link:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Xmethod-API.html[xmethod], so it will not invoke any C++ synchronization code. See the following printout as an example for the expected format.
```plaintext
(gdb) print example_flat_map.get_stats()
$1 = [stats] = {[insertion] = {[count] = 5, [probe_length] = {avg = 1.0, var = 0.0, dev = 0.0}},
[successful_lookup] = {[count] = 0, [probe_length] = {avg = 0.0, var = 0.0, dev = 0.0},
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}, [unsuccessful_lookup] = {[count] = 5,
[probe_length] = {avg = 1.0, var = 0.0, dev = 0.0},
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}}
```
=== Fancy pointers
The pretty-printers also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has GDB pretty-printer customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py].
+7 -5
View File
@@ -43,7 +43,8 @@ boost::unordered_node_map
boost::unordered_flat_map
^.^h|*Concurrent*
^|
^| `boost::concurrent_node_set` +
`boost::concurrent_node_map`
^| `boost::concurrent_flat_set` +
`boost::concurrent_flat_map`
@@ -59,6 +60,7 @@ There are two variants: **flat** (the fastest) and **node-based**, which
provide pointer stability under rehashing at the expense of being slower.
* Finally, **concurrent containers** are designed and implemented to be used in high-performance
multithreaded scenarios. Their interface is radically different from that of regular C++ containers.
Flat and node-based variants are provided.
All sets and maps in Boost.Unordered are instantiatied similarly as
`std::unordered_set` and `std::unordered_map`, respectively:
@@ -72,8 +74,8 @@ namespace boost {
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<Key> >
class unordered_set;
// same for unordered_multiset, unordered_flat_set, unordered_node_set
// and concurrent_flat_set
// same for unordered_multiset, unordered_flat_set, unordered_node_set,
// concurrent_flat_set and concurrent_node_set
template <
class Key, class Mapped,
@@ -81,8 +83,8 @@ namespace boost {
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
class unordered_map;
// same for unordered_multimap, unordered_flat_map, unordered_node_map
// and concurrent_flat_map
// same for unordered_multimap, unordered_flat_map, unordered_node_map,
// concurrent_flat_map and concurrent_node_map
}
----
+3 -2
View File
@@ -121,7 +121,8 @@ for Visual Studio on an x64-mode Intel CPU with SSE2 and for GCC on an IBM s390x
== Concurrent Containers
The same data structure used by Boost.Unordered open-addressing containers has been chosen
also as the foundation of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`:
also as the foundation of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
`boost::concurrent_flat_map`/`boost::concurrent_node_map`:
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
concurrent scenarios.
@@ -130,7 +131,7 @@ with minimal locking. In particular, the metadata array can be used for implemen
lookup that are lock-free up to the last step of actual element comparison.
* Layout compatibility with Boost.Unordered flat containers allows for
xref:#concurrent_interoperability_with_non_concurrent_containers[fast transfer]
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`,
of all elements between a concurrent container and its non-concurrent counterpart,
and vice versa.
=== Hash Function and Platform Interoperability
+2
View File
@@ -13,3 +13,5 @@ include::unordered_node_map.adoc[]
include::unordered_node_set.adoc[]
include::concurrent_flat_map.adoc[]
include::concurrent_flat_set.adoc[]
include::concurrent_node_map.adoc[]
include::concurrent_node_set.adoc[]
+2 -1
View File
@@ -129,7 +129,8 @@ xref:#rationale_open_addresing_containers[corresponding section].
== Concurrent Containers
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
`boost::concurrent_flat_set`/`boost::concurrent_node_set` and
`boost::concurrent_flat_map`/`boost::concurrent_node_map` use the basic
xref:#structures_open_addressing_containers[open-addressing layout] described above
augmented with synchronization mechanisms.
+22 -3
View File
@@ -78,6 +78,7 @@ namespace boost {
explicit xref:#unordered_node_map_allocator_constructor[unordered_node_map](const Allocator& a);
xref:#unordered_node_map_copy_constructor_with_allocator[unordered_node_map](const unordered_node_map& other, const Allocator& a);
xref:#unordered_node_map_move_constructor_with_allocator[unordered_node_map](unordered_node_map&& other, const Allocator& a);
xref:#unordered_node_map_move_constructor_from_concurrent_node_map[unordered_node_map](concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
xref:#unordered_node_map_initializer_list_constructor[unordered_node_map](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -537,6 +538,24 @@ and always calls `other.reset_stats()`.
---
==== Move Constructor from concurrent_node_map
```c++
unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
```
Move construction from a xref:#concurrent_node_map[`concurrent_node_map`].
The internal bucket array of `other` is transferred directly to the new container.
The hash function, predicate and allocator are moved-constructed from `other`.
If statistics are xref:unordered_node_map_boost_unordered_enable_stats[enabled],
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
[horizontal]
Complexity:;; Constant time.
Concurrency:;; Blocking on `other`.
---
==== Initializer List Constructor
[source,c++,subs="+quotes"]
----
@@ -1219,8 +1238,8 @@ Throws:;; Nothing.
==== Extract by Key
```c++
node_type erase(const key_type& k);
template<class K> node_type erase(K&& k);
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
```
Extracts the element with key equivalent to `k`, if it exists.
@@ -1228,7 +1247,7 @@ Extracts the element with key equivalent to `k`, if it exists.
[horizontal]
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
---
+22 -3
View File
@@ -73,6 +73,7 @@ namespace boost {
explicit xref:#unordered_node_set_allocator_constructor[unordered_node_set](const Allocator& a);
xref:#unordered_node_set_copy_constructor_with_allocator[unordered_node_set](const unordered_node_set& other, const Allocator& a);
xref:#unordered_node_set_move_constructor_with_allocator[unordered_node_set](unordered_node_set&& other, const Allocator& a);
xref:#unordered_node_set_move_constructor_from_concurrent_node_set[unordered_node_set](concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
xref:#unordered_node_set_initializer_list_constructor[unordered_node_set](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -489,6 +490,24 @@ and always calls `other.reset_stats()`.
---
==== Move Constructor from concurrent_node_set
```c++
unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
```
Move construction from a xref:#concurrent_node_set[`concurrent_node_set`].
The internal bucket array of `other` is transferred directly to the new container.
The hash function, predicate and allocator are moved-constructed from `other`.
If statistics are xref:unordered_node_set_boost_unordered_enable_stats[enabled],
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
[horizontal]
Complexity:;; Constant time.
Concurrency:;; Blocking on `other`.
---
==== Initializer List Constructor
[source,c++,subs="+quotes"]
----
@@ -1028,8 +1047,8 @@ Throws:;; Nothing.
==== Extract by Key
```c++
node_type erase(const key_type& k);
template<class K> node_type erase(K&& k);
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
```
Extracts the element with key equivalent to `k`, if it exists.
@@ -1037,7 +1056,7 @@ Extracts the element with key equivalent to `k`, if it exists.
[horizontal]
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
---
+175 -46
View File
@@ -30,18 +30,50 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</Type>
<Type Name="boost::unordered::detail::grouped_bucket_array&lt;*&gt;" Inheritable="false">
<!--
The expression `&**p` is used so the Intrinsic fails to parse for a fancy pointer type.
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
For a raw pointer, this expression is exactly equivalent to `*p`.
For a fancy pointer, this expression will try to call a user-defined `operator*()`, which is not allowed in Natvis, and it will fail.
-->
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<!--
The casting expression `(xyz_pointer)p` is used so the Intrinsic fails to parse for a fancy pointer type.
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
In this case, `(xyz_pointer)p` is either a no-op for a raw pointer type, or it's an invalid expression.
-->
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer)p) + n">
<Parameter Name="p" Type="bucket_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="bucket_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Expand>
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Variable Name="current_bucket" InitialValue="to_address(&amp;buckets)" />
<Variable Name="node" InitialValue="to_address(&amp;current_bucket-&gt;next)" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Exec>current_bucket = next(to_address(&amp;buckets), bucket_index)</Exec>
<Exec>node = to_address(&amp;current_bucket-&gt;next)</Exec>
<Loop Condition="node != nullptr">
<Item>node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
<Exec>node = to_address(&amp;node-&gt;next)</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
@@ -54,14 +86,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Variable Name="current_bucket" InitialValue="to_address(&amp;buckets)" />
<Variable Name="node" InitialValue="to_address(&amp;current_bucket-&gt;next)" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Exec>current_bucket = next(to_address(&amp;buckets), bucket_index)</Exec>
<Exec>node = to_address(&amp;current_bucket-&gt;next)</Exec>
<Loop Condition="node != nullptr">
<Item Name="[{node-&gt;buf.t_.first}]">node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
<Exec>node = to_address(&amp;node-&gt;next)</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
@@ -99,29 +131,46 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Type Name="boost::unordered::detail::iterator_detail::iterator&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator&lt;*&gt;" />
<Intrinsic Name="valid" Expression="p != nullptr &amp;&amp; itb.p != nullptr &amp;&amp; itb.pbg != nullptr" />
<DisplayString Condition="valid()">{p-&gt;buf.t_}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="node_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
</Intrinsic>
<Intrinsic Name="valid" Expression="to_address(&amp;p) &amp;&amp; to_address(&amp;itb.p) &amp;&amp; to_address(&amp;itb.pbg)" />
<DisplayString Condition="valid()">{to_address(&amp;p)-&gt;buf.t_}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">p-&gt;buf.t_</ExpandedItem>
<ExpandedItem Condition="valid()">to_address(&amp;p)-&gt;buf.t_</ExpandedItem>
</Expand>
</Type>
<!-- FOA and CFOA helpers -->
<Type Name="boost::unordered::detail::foa::element_type&lt;*&gt;" Priority="Medium" Inheritable="false">
<DisplayString>{*p}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="pointer*" />
</Intrinsic>
<Intrinsic Name="get" Expression="to_address(&amp;p)" />
<DisplayString>{*get()}</DisplayString>
<Expand>
<ExpandedItem>*p</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::unordered::detail::foa::element_type&lt;std::pair&lt;*&gt;,*&gt;" Priority="MediumHigh" Inheritable="false">
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
<DisplayString>({p-&gt;first}, {p-&gt;second})</DisplayString>
<Expand>
<Item Name="first">p-&gt;first</Item>
<Item Name="second">p-&gt;second</Item>
<ExpandedItem>*get()</ExpandedItem>
</Expand>
</Type>
@@ -227,10 +276,39 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Type Name="boost::unordered::detail::foa::table&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table&lt;*&gt;" />
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer)p) + n">
<Parameter Name="p" Type="arrays_type::value_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer)p) + n">
<Parameter Name="p" Type="unsigned char*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="arrays_type::value_type*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer*)nullptr)->boost_next(p, n)">
<Parameter Name="p" Type="unsigned char*" />
<Parameter Name="n" Type="ptrdiff_t" />
</Intrinsic>
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
<Parameter Name="e" Type="value_type*" />
</Intrinsic>
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e-&gt;p">
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e-&gt;get()">
<Parameter Name="e" Type="element_type*" />
</Intrinsic>
@@ -254,8 +332,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(to_address(&amp;arrays.groups_))" />
<Variable Name="p_" InitialValue="to_address(&amp;arrays.elements_)" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
@@ -264,18 +342,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<If Condition="!first_time || !(p_ &amp;&amp; !(to_address(&amp;arrays.groups_)[0].match_occupied() &amp; 0x1))">
<Item>*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
<Exec>p_ = next(p_, group_type::N)</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
@@ -284,9 +362,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
</Else>
</Loop>
@@ -299,8 +376,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(to_address(&amp;arrays.groups_))" />
<Variable Name="p_" InitialValue="to_address(&amp;arrays.elements_)" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
@@ -309,18 +386,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<If Condition="!first_time || !(p_ &amp;&amp; !(to_address(&amp;arrays.groups_)[0].match_occupied() &amp; 0x1))">
<Item Name="[{get_value(p_)-&gt;first}]">*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
<Exec>p_ = next(p_, group_type::N)</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
@@ -329,9 +406,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
</Else>
</Loop>
@@ -345,6 +421,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<AlternativeType Name="boost::unordered::unordered_node_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_set&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::hash_base*&gt;(&amp;table_))</Item>
@@ -357,6 +435,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<Type Name="boost::unordered::unordered_flat_map&lt;*&gt;" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
<AlternativeType Name="boost::unordered::unordered_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_map&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::hash_base*&gt;(&amp;table_))</Item>
@@ -369,12 +448,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
<!-- FOA iterators -->
<Type Name="boost::unordered::detail::foa::table_iterator&lt;*&gt;" Inheritable="false">
<Intrinsic Name="valid" Expression="p_ != nullptr &amp;&amp; pc_ != nullptr" />
<DisplayString Condition="valid()">{*p_}</DisplayString>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="table_element_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="table_element_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="&amp;**p">
<Parameter Name="p" Type="char_pointer*" />
</Intrinsic>
<Intrinsic Name="to_address" Optional="true" Expression="p-&gt;boost_to_address()">
<Parameter Name="p" Type="char_pointer*" />
</Intrinsic>
<Intrinsic Name="valid" Expression="to_address(&amp;p_) != nullptr &amp;&amp; to_address(&amp;pc_) != nullptr" />
<DisplayString Condition="valid()">{*to_address(&amp;p_)}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
<ExpandedItem Condition="valid()">*to_address(&amp;p_)</ExpandedItem>
</Expand>
</Type>
<!-- Fancy pointer support -->
<!--
To allow your own fancy pointer type to interact with Boost.Unordered Natvis,
add the following intrinsics to your type, with the following conditions.
(Note, this is assuming the presence of a type alias `pointer` for the underlying
raw pointer type, and a type alias `difference_type` for your fancy pointer
difference type. Substitute whichever names are applicable in your case.)
`boost_to_address`
* Takes no parameters
* Returns the raw pointer equivalent to your fancy pointer
`boost_next`
* Parameter 1, an underlying raw pointer of type `pointer`
* Parameter 2, an offset of type `difference_type`
* Returns the raw pointer equivalent to your fancy pointer, as if you did the following operations
1. Convert the incoming raw pointer to your fancy pointer
2. Use operator+= to add the offset to the fancy pointer
3. Convert back to the raw pointer
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above.
Example
```
<Type Name="my_fancy_ptr&lt;*&gt;">
...
<Intrinsic Name="boost_to_address" ReturnType="pointer" Expression="..." />
<Intrinsic Name="boost_next" ReturnType="pointer" Expression="...">
<Parameter Name="ptr" Type="pointer" />
<Parameter Name="offset" Type="difference_type" />
</Intrinsic>
...
</Type>
```
-->
</AutoVisualizer>
+391
View File
@@ -0,0 +1,391 @@
# Copyright 2024 Braden Ganetsky
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
import gdb.printing
import gdb.xmethod
import re
import math
class BoostUnorderedHelpers:
def maybe_unwrap_atomic(n):
if f"{n.type.strip_typedefs()}".startswith("std::atomic<"):
underlying_type = n.type.template_argument(0)
return n.cast(underlying_type)
else:
return n
def maybe_unwrap_foa_element(e):
if f"{e.type.strip_typedefs()}".startswith("boost::unordered::detail::foa::element_type<"):
return e["p"]
else:
return e
def maybe_unwrap_reference(value):
if value.type.code == gdb.TYPE_CODE_REF:
return value.referenced_value()
else:
return value
def countr_zero(n):
for i in range(32):
if (n & (1 << i)) != 0:
return i
return 32
class BoostUnorderedPointerCustomizationPoint:
def __init__(self, any_ptr):
vis = gdb.default_visualizer(any_ptr)
if vis is None:
self.to_address = lambda ptr: ptr
self.next = lambda ptr, offset: ptr + offset
else:
self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)
self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)
class BoostUnorderedFcaPrinter:
def __init__(self, val):
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
self.name = self.name.replace("boost::unordered::", "boost::")
self.is_map = self.name.endswith("map")
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["buckets_"]["buckets"])
def to_string(self):
size = self.val["table_"]["size_"]
return f"{self.name} with {size} elements"
def display_hint(self):
return "map"
def children(self):
def generator():
grouped_buckets = self.val["table_"]["buckets_"]
size = grouped_buckets["size_"]
buckets = grouped_buckets["buckets"]
bucket_index = 0
count = 0
while bucket_index != size:
current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)
node = self.cpo.to_address(current_bucket.dereference()["next"])
while node != 0:
value = node.dereference()["buf"]["t_"]
if self.is_map:
first = value["first"]
second = value["second"]
yield "", first
yield "", second
else:
yield "", count
yield "", value
count += 1
node = self.cpo.to_address(node.dereference()["next"])
bucket_index += 1
return generator()
class BoostUnorderedFcaIteratorPrinter:
def __init__(self, val):
self.val = val
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p"])
def to_string(self):
if self.valid():
value = self.cpo.to_address(self.val["p"]).dereference()["buf"]["t_"]
return f"iterator = {{ {value} }}"
else:
return "iterator = { end iterator }"
def valid(self):
return (self.cpo.to_address(self.val["p"]) != 0) and (self.cpo.to_address(self.val["itb"]["p"]) != 0)
class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return "[stats]"
def display_hint(self):
return "map"
def children(self):
def generator():
members = ["insertion", "successful_lookup", "unsuccessful_lookup"]
for member in members:
yield "", member
yield "", self.val[member]
return generator()
class BoostUnorderedFoaCumulativeStatsPrinter:
def __init__(self, val):
self.val = val
self.n = self.val["n"]
self.N = self.val.type.template_argument(0)
def display_hint(self):
return "map"
def children(self):
def generator():
yield "", "count"
yield "", self.n
sequence_stats_data = gdb.lookup_type("boost::unordered::detail::foa::sequence_stats_data")
data = self.val["data"]
arr = data.address.reinterpret_cast(sequence_stats_data.pointer())
def build_string(idx):
entry = arr[idx]
avg = float(entry["m"])
var = float(entry["s"] / self.n) if (self.n != 0) else 0.0
dev = math.sqrt(var)
return f"{{avg = {avg}, var = {var}, dev = {dev}}}"
if self.N > 0:
yield "", "probe_length"
yield "", build_string(0)
if self.N > 1:
yield "", "num_comparisons"
yield "", build_string(1)
return generator()
class BoostUnorderedFoaPrinter:
def __init__(self, val):
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
self.name = self.name.replace("boost::unordered::", "boost::")
self.is_map = self.name.endswith("map")
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["arrays"]["groups_"])
def to_string(self):
size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val["table_"]["size_ctrl"]["size"])
return f"{self.name} with {size} elements"
def display_hint(self):
return "map"
def is_regular_layout(self, group):
typename = group["m"].type.strip_typedefs()
array_size = typename.sizeof // typename.target().sizeof
if array_size == 16:
return True
elif array_size == 2:
return False
def match_occupied(self, group):
m = group["m"]
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
if self.is_regular_layout(group):
bits = [1 << b for b in range(16) if at(b) == 0]
return 0x7FFF & ~sum(bits)
else:
xx = at(0) | at(1)
yy = xx | (xx >> 32)
return 0x7FFF & (yy | (yy >> 16))
def is_sentinel(self, group, pos):
m = group["m"]
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
N = group["N"]
sentinel_ = group["sentinel_"]
if self.is_regular_layout(group):
return pos == N-1 and at(N-1) == sentinel_
else:
return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0
def children(self):
def generator():
table = self.val["table_"]
groups = self.cpo.to_address(table["arrays"]["groups_"])
elements = self.cpo.to_address(table["arrays"]["elements_"])
pc_ = groups.cast(gdb.lookup_type("unsigned char").pointer())
p_ = elements
first_time = True
mask = 0
n0 = 0
n = 0
count = 0
while p_ != 0:
# This if block mirrors the condition in the begin() call
if (not first_time) or (self.match_occupied(groups.dereference()) & 1):
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)
value = self.cpo.to_address(pointer).dereference()
if self.is_map:
first = value["first"]
second = value["second"]
yield "", first
yield "", second
else:
yield "", count
yield "", value
count += 1
first_time = False
n0 = pc_.cast(gdb.lookup_type("uintptr_t")) % groups.dereference().type.sizeof
pc_ = self.cpo.next(pc_, -n0)
mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)
while mask == 0:
pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)
p_ = self.cpo.next(p_, groups.dereference()["N"])
mask = self.match_occupied(pc_.cast(groups.type).dereference())
n = BoostUnorderedHelpers.countr_zero(mask)
if self.is_sentinel(pc_.cast(groups.type).dereference(), n):
p_ = 0
else:
pc_ = self.cpo.next(pc_, n)
p_ = self.cpo.next(p_, n - n0)
return generator()
class BoostUnorderedFoaIteratorPrinter:
def __init__(self, val):
self.val = val
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p_"])
def to_string(self):
if self.valid():
element = self.cpo.to_address(self.val["p_"])
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)
value = self.cpo.to_address(pointer).dereference()
return f"iterator = {{ {value} }}"
else:
return "iterator = { end iterator }"
def valid(self):
return (self.cpo.to_address(self.val["p_"]) != 0) and (self.cpo.to_address(self.val["pc_"]) != 0)
def boost_unordered_build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter("boost_unordered")
add_template_printer = lambda name, printer: pp.add_printer(name, f"^{name}<.*>$", printer)
add_concrete_printer = lambda name, printer: pp.add_printer(name, f"^{name}$", printer)
add_template_printer("boost::unordered::unordered_map", BoostUnorderedFcaPrinter)
add_template_printer("boost::unordered::unordered_multimap", BoostUnorderedFcaPrinter)
add_template_printer("boost::unordered::unordered_set", BoostUnorderedFcaPrinter)
add_template_printer("boost::unordered::unordered_multiset", BoostUnorderedFcaPrinter)
add_template_printer("boost::unordered::detail::iterator_detail::iterator", BoostUnorderedFcaIteratorPrinter)
add_template_printer("boost::unordered::detail::iterator_detail::c_iterator", BoostUnorderedFcaIteratorPrinter)
add_template_printer("boost::unordered::unordered_flat_map", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::unordered_flat_set", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::unordered_node_map", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::unordered_node_set", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::concurrent_flat_map", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::concurrent_flat_set", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::concurrent_node_map", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::concurrent_node_set", BoostUnorderedFoaPrinter)
add_template_printer("boost::unordered::detail::foa::table_iterator", BoostUnorderedFoaIteratorPrinter)
add_concrete_printer("boost::unordered::detail::foa::table_core_cumulative_stats", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)
add_template_printer("boost::unordered::detail::foa::cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
add_template_printer("boost::unordered::detail::foa::concurrent_cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
return pp
gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())
# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html
class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):
def __init__(self):
gdb.xmethod.XMethod.__init__(self, "get_stats")
def get_worker(self, method_name):
if method_name == "get_stats":
return BoostUnorderedFoaGetStatsWorker()
class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):
def get_arg_types(self):
return None
def get_result_type(self, obj):
return gdb.lookup_type("boost::unordered::detail::foa::table_core_cumulative_stats")
def __call__(self, obj):
try:
return obj["table_"]["cstats"]
except gdb.error:
print("Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.")
return
class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):
def __init__(self):
gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')
self.methods = [BoostUnorderedFoaGetStatsMethod()]
def match(self, class_type, method_name):
template_name = f"{class_type.strip_typedefs()}".split("<")[0]
regex = "^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$"
if not re.match(regex, template_name):
return None
workers = []
for method in self.methods:
if method.enabled:
worker = method.get_worker(method_name)
if worker:
workers.append(worker)
return workers
gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())
""" Fancy pointer support """
"""
To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,
create a pretty-printer for your own type with the following additional methods.
(Note, this is assuming the presence of a type alias `pointer` for the underlying
raw pointer type, Substitute whichever name is applicable in your case.)
`boost_to_address(fancy_ptr)`
* A static method, but `@staticmethod` is not required
* Parameter `fancy_ptr` of type `gdb.Value`
* Its `.type` will be your fancy pointer type
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer
* This method should be equivalent to calling `operator->()` on your fancy pointer in C++
`boost_next(raw_ptr, offset)`
* Parameter `raw_ptr` of type `gdb.Value`
* Its `.type` will be `pointer`
* Parameter `offset`
* Either has integer type, or is of type `gdb.Value` with an underlying integer
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations
1. Convert the incoming raw pointer to your fancy pointer
2. Use operator+= to add the offset to the fancy pointer
3. Convert back to the raw pointer
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above
* Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers
Example
```
class MyFancyPtrPrinter:
...
# Equivalent to `operator->()`
def boost_to_address(fancy_ptr):
...
return ...
# Equivalent to `operator+()`
def boost_next(raw_ptr, offset):
...
return ...
...
```
"""
@@ -227,6 +227,13 @@ namespace boost {
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
{
@@ -241,6 +248,15 @@ namespace boost {
return table_.visit(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
@@ -259,6 +275,15 @@ namespace boost {
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
@@ -277,6 +302,12 @@ namespace boost {
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
@@ -290,6 +321,16 @@ namespace boost {
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
@@ -311,6 +352,12 @@ namespace boost {
}
#endif
template <class F> bool visit_while(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool visit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
@@ -324,6 +371,16 @@ namespace boost {
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
@@ -384,14 +441,14 @@ namespace boost {
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(obj, f);
return table_.insert_or_visit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::move(obj), f);
return table_.insert_or_visit(std::move(obj), f);
}
template <class K, class F>
@@ -401,7 +458,7 @@ namespace boost {
insert_or_visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
return table_.try_emplace_or_visit(std::forward<K>(k), f);
}
template <class InputIterator, class F>
@@ -409,7 +466,7 @@ namespace boost {
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
table_.emplace_or_visit(*first, f);
}
}
@@ -417,7 +474,7 @@ namespace boost {
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class F>
@@ -469,7 +526,7 @@ namespace boost {
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_cvisit(
return table_.emplace_or_visit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
@@ -0,0 +1,975 @@
/* Fast open-addressing, node-based concurrent hashmap.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
#include <boost/unordered/concurrent_node_map_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_map_handle.hpp>
#include <boost/unordered/detail/foa/node_map_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_map_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <type_traits>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash, class Pred, class Allocator>
class concurrent_node_map
{
private:
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_node_map;
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class unordered_node_map;
using type_policy = detail::foa::node_map_types<Key, T,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(concurrent_node_map<K, V, H, KE, A> const& lhs,
concurrent_node_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Predicate>
friend typename concurrent_node_map<K, V, H, KE, A>::size_type erase_if(
concurrent_node_map<K, V, H, KE, A>& set, Predicate pred);
template<class Archive, class K, class V, class H, class KE, class A>
friend void serialize(
Archive& ar, concurrent_node_map<K, V, H, KE, A>& c,
unsigned int version);
public:
using key_type = Key;
using mapped_type = T;
using value_type = typename type_policy::value_type;
using init_type = typename type_policy::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using node_type = detail::foa::node_map_handle<type_policy,
typename boost::allocator_rebind<Allocator,
typename type_policy::value_type>::type>;
using insert_return_type =
detail::foa::iteratorless_insert_return_type<node_type>;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
#if defined(BOOST_UNORDERED_ENABLE_STATS)
using stats = typename table_type::stats;
#endif
concurrent_node_map()
: concurrent_node_map(detail::foa::default_bucket_count)
{
}
explicit concurrent_node_map(size_type n, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
}
template <class InputIterator>
concurrent_node_map(InputIterator f, InputIterator l,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
this->insert(f, l);
}
concurrent_node_map(concurrent_node_map const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
concurrent_node_map(concurrent_node_map&& rhs)
: table_(std::move(rhs.table_))
{
}
template <class InputIterator>
concurrent_node_map(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_node_map(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_node_map(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_node_map(
concurrent_node_map const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_node_map(concurrent_node_map&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_node_map(std::initializer_list<value_type> il,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: concurrent_node_map(n, hf, eql, a)
{
this->insert(il.begin(), il.end());
}
concurrent_node_map(size_type n, const allocator_type& a)
: concurrent_node_map(n, hasher(), key_equal(), a)
{
}
concurrent_node_map(
size_type n, const hasher& hf, const allocator_type& a)
: concurrent_node_map(n, hf, key_equal(), a)
{
}
template <typename InputIterator>
concurrent_node_map(
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: concurrent_node_map(f, l, n, hasher(), key_equal(), a)
{
}
template <typename InputIterator>
concurrent_node_map(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_node_map(f, l, n, hf, key_equal(), a)
{
}
concurrent_node_map(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_node_map(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_node_map(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_node_map(il, n, hasher(), key_equal(), a)
{
}
concurrent_node_map(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_node_map(il, n, hf, key_equal(), a)
{
}
concurrent_node_map(
unordered_node_map<Key, T, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_node_map() = default;
concurrent_node_map& operator=(concurrent_node_map const& rhs)
{
table_ = rhs.table_;
return *this;
}
concurrent_node_map& operator=(concurrent_node_map&& rhs) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(rhs.table_);
return *this;
}
concurrent_node_map& operator=(std::initializer_list<value_type> ilist)
{
table_ = ilist;
return *this;
}
/// Capacity
///
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
cvisit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type cvisit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_all(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
cvisit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.cvisit_all(p, f);
}
#endif
template <class F> bool visit_while(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool visit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool cvisit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_while(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
cvisit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.cvisit_while(p, f);
}
#endif
/// Modifiers
///
template <class Ty>
BOOST_FORCEINLINE auto insert(Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)))
{
return table_.insert(std::forward<Ty>(value));
}
BOOST_FORCEINLINE bool insert(init_type&& obj)
{
return table_.insert(std::move(obj));
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
insert_return_type insert(node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert(std::move(access::element(nh)))) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
{
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class K, class M>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
insert_or_assign(K&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class Ty, class F>
BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
-> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(std::forward<Ty>(value), f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(std::move(obj), f);
}
template <class InputIterator, class F>
void insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_visit(*first, f);
}
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class F>
insert_return_type insert_or_visit(node_type&& nh, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class Ty, class F>
BOOST_FORCEINLINE auto insert_or_cvisit(Ty&& value, F f)
-> decltype(table_.insert_or_cvisit(std::forward<Ty>(value), f))
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::forward<Ty>(value), f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::move(obj), f);
}
template <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
}
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class F>
insert_return_type insert_or_cvisit(node_type&& nh, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.emplace_or_visit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_cvisit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
return table_.try_emplace(k, std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
{
return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
try_emplace(K&& k, Args&&... args)
{
return table_.try_emplace(
std::forward<K>(k), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
key_type const& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
key_type const& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
key_type&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
key_type&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
erase(K&& k)
{
return table_.erase(std::forward<K>(k));
}
template <class F>
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
{
return table_.erase_if(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value &&
!detail::is_execution_policy<K>::value,
size_type>::type
erase_if(K&& k, F f)
{
return table_.erase_if(std::forward<K>(k), f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
erase_if(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.erase_if(p, f);
}
#endif
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
void swap(concurrent_node_map& other) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
return table_.swap(other.table_);
}
node_type extract(key_type const& key)
{
node_type nh;
table_.extract(key, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
extract(K const& key)
{
node_type nh;
table_.extract(key, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class F>
node_type extract_if(key_type const& key, F f)
{
node_type nh;
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class K, class F>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
extract_if(K const& key, F f)
{
node_type nh;
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
return nh;
}
void clear() noexcept { table_.clear(); }
template <typename H2, typename P2>
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>& x)
{
BOOST_ASSERT(get_allocator() == x.get_allocator());
return table_.merge(x.table_);
}
template <typename H2, typename P2>
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>&& x)
{
return merge(x);
}
BOOST_FORCEINLINE size_type count(key_type const& k) const
{
return table_.count(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& k)
{
return table_.count(k);
}
BOOST_FORCEINLINE bool contains(key_type const& k) const
{
return table_.contains(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
contains(K const& k) const
{
return table_.contains(k);
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
#if defined(BOOST_UNORDERED_ENABLE_STATS)
/// Stats
///
stats get_stats() const { return table_.get_stats(); }
void reset_stats() noexcept { table_.reset_stats(); }
#endif
/// Observers
///
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}
template <class K, class T, class H, class P, class A, class Predicate>
typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
concurrent_node_map<K, T, H, P, A>& c, Predicate pred)
{
return c.table_.erase_if(pred);
}
template<class Archive, class K, class V, class H, class KE, class A>
void serialize(
Archive& ar, concurrent_node_map<K, V, H, KE, A>& c, unsigned int)
{
ar & core::make_nvp("table",c.table_);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
class Pred =
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_node_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
Allocator>;
template <class Key, class T,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_node_map<std::remove_const_t<Key>, T, Hash, Pred,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_node_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(InputIterator, InputIterator, Allocator)
-> concurrent_node_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_node_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> concurrent_node_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
@@ -0,0 +1,67 @@
/* Fast open-addressing, node-based concurrent hashmap.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2024 Braden Ganetsky.
* Copyright 2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
#include <boost/config.hpp>
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
#include <memory_resource>
#endif
namespace boost {
namespace unordered {
template <class Key, class T, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class concurrent_node_map;
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
template <class K, class T, class H, class P, class A, class Predicate>
typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
concurrent_node_map<K, T, H, P, A>& c, Predicate pred);
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
namespace pmr {
template <class Key, class T, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key> >
using concurrent_node_map = boost::unordered::concurrent_node_map<Key, T,
Hash, Pred, std::pmr::polymorphic_allocator<std::pair<Key const, T> > >;
} // namespace pmr
#endif
} // namespace unordered
using boost::unordered::concurrent_node_map;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
@@ -0,0 +1,888 @@
/* Fast open-addressing, node-based concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
#include <boost/unordered/concurrent_node_set_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_set_handle.hpp>
#include <boost/unordered/detail/foa/node_set_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_set_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <utility>
namespace boost {
namespace unordered {
template <class Key, class Hash, class Pred, class Allocator>
class concurrent_node_set
{
private:
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class concurrent_node_set;
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class unordered_node_set;
using type_policy = detail::foa::node_set_types<Key,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(concurrent_node_set<K, H, KE, A> const& lhs,
concurrent_node_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Predicate>
friend typename concurrent_node_set<K, H, KE, A>::size_type erase_if(
concurrent_node_set<K, H, KE, A>& set, Predicate pred);
template<class Archive, class K, class H, class KE, class A>
friend void serialize(
Archive& ar, concurrent_node_set<K, H, KE, A>& c,
unsigned int version);
public:
using key_type = Key;
using value_type = typename type_policy::value_type;
using init_type = typename type_policy::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using node_type = detail::foa::node_set_handle<type_policy,
typename boost::allocator_rebind<Allocator,
typename type_policy::value_type>::type>;
using insert_return_type =
detail::foa::iteratorless_insert_return_type<node_type>;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
#if defined(BOOST_UNORDERED_ENABLE_STATS)
using stats = typename table_type::stats;
#endif
concurrent_node_set()
: concurrent_node_set(detail::foa::default_bucket_count)
{
}
explicit concurrent_node_set(size_type n, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
}
template <class InputIterator>
concurrent_node_set(InputIterator f, InputIterator l,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
this->insert(f, l);
}
concurrent_node_set(concurrent_node_set const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
concurrent_node_set(concurrent_node_set&& rhs)
: table_(std::move(rhs.table_))
{
}
template <class InputIterator>
concurrent_node_set(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_node_set(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_node_set(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_node_set(
concurrent_node_set const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_node_set(concurrent_node_set&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_node_set(std::initializer_list<value_type> il,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: concurrent_node_set(n, hf, eql, a)
{
this->insert(il.begin(), il.end());
}
concurrent_node_set(size_type n, const allocator_type& a)
: concurrent_node_set(n, hasher(), key_equal(), a)
{
}
concurrent_node_set(
size_type n, const hasher& hf, const allocator_type& a)
: concurrent_node_set(n, hf, key_equal(), a)
{
}
template <typename InputIterator>
concurrent_node_set(
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: concurrent_node_set(f, l, n, hasher(), key_equal(), a)
{
}
template <typename InputIterator>
concurrent_node_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_node_set(f, l, n, hf, key_equal(), a)
{
}
concurrent_node_set(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_node_set(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_node_set(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_node_set(il, n, hasher(), key_equal(), a)
{
}
concurrent_node_set(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_node_set(il, n, hf, key_equal(), a)
{
}
concurrent_node_set(
unordered_node_set<Key, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_node_set() = default;
concurrent_node_set& operator=(concurrent_node_set const& rhs)
{
table_ = rhs.table_;
return *this;
}
concurrent_node_set& operator=(concurrent_node_set&& rhs)
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value)
{
table_ = std::move(rhs.table_);
return *this;
}
concurrent_node_set& operator=(std::initializer_list<value_type> ilist)
{
table_ = ilist;
return *this;
}
/// Capacity
///
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
cvisit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type cvisit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_all(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
cvisit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.cvisit_all(p, f);
}
#endif
template <class F> bool visit_while(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool visit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool cvisit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_while(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
cvisit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.cvisit_while(p, f);
}
#endif
/// Modifiers
///
BOOST_FORCEINLINE bool insert(value_type const& obj)
{
return table_.insert(obj);
}
BOOST_FORCEINLINE bool insert(value_type&& obj)
{
return table_.insert(std::move(obj));
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert(K&& k)
{
return table_.try_emplace(std::forward<K>(k));
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
insert_return_type insert(node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert(std::move(access::element(nh)))) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_visit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_visit(std::move(obj), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_or_visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.try_emplace_or_visit(std::forward<K>(k), f);
}
template <class InputIterator, class F>
void insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_visit(*first, f);
}
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class F>
insert_return_type insert_or_visit(node_type&& nh, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class F>
BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::move(obj), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_or_cvisit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
}
template <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
}
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class F>
insert_return_type insert_or_cvisit(node_type&& nh, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_visit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_cvisit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
erase(K&& k)
{
return table_.erase(std::forward<K>(k));
}
template <class F>
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
{
return table_.erase_if(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value &&
!detail::is_execution_policy<K>::value,
size_type>::type
erase_if(K&& k, F f)
{
return table_.erase_if(std::forward<K>(k), f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
erase_if(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.erase_if(p, f);
}
#endif
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
void swap(concurrent_node_set& other) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
return table_.swap(other.table_);
}
node_type extract(key_type const& key)
{
node_type nh;
table_.extract(key, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
extract(K const& key)
{
node_type nh;
table_.extract(key, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class F>
node_type extract_if(key_type const& key, F f)
{
node_type nh;
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
return nh;
}
template <class K, class F>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
extract_if(K const& key, F f)
{
node_type nh;
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
return nh;
}
void clear() noexcept { table_.clear(); }
template <typename H2, typename P2>
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>& x)
{
BOOST_ASSERT(get_allocator() == x.get_allocator());
return table_.merge(x.table_);
}
template <typename H2, typename P2>
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>&& x)
{
return merge(x);
}
BOOST_FORCEINLINE size_type count(key_type const& k) const
{
return table_.count(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& k)
{
return table_.count(k);
}
BOOST_FORCEINLINE bool contains(key_type const& k) const
{
return table_.contains(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
contains(K const& k) const
{
return table_.contains(k);
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
#if defined(BOOST_UNORDERED_ENABLE_STATS)
/// Stats
///
stats get_stats() const { return table_.get_stats(); }
void reset_stats() noexcept { table_.reset_stats(); }
#endif
/// Observers
///
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}
template <class K, class H, class P, class A, class Predicate>
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
concurrent_node_set<K, H, P, A>& c, Predicate pred)
{
return c.table_.erase_if(pred);
}
template<class Archive, class K, class H, class KE, class A>
void serialize(
Archive& ar, concurrent_node_set<K, H, KE, A>& c, unsigned int)
{
ar & core::make_nvp("table",c.table_);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
class Pred =
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
class Allocator = std::allocator<
typename std::iterator_traits<InputIterator>::value_type>,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
Allocator>;
template <class T, class Hash = boost::hash<T>,
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_node_set< T, Hash, Pred, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(InputIterator, InputIterator, Allocator)
-> concurrent_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(std::initializer_list<T>, std::size_t, Allocator)
-> concurrent_node_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(std::initializer_list<T >, Allocator)
-> concurrent_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
template <class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_node_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
-> concurrent_node_set<T, Hash, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
@@ -0,0 +1,67 @@
/* Fast open-addressing, node-based concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Copyright 2024 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
#include <boost/config.hpp>
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
#include <memory_resource>
#endif
namespace boost {
namespace unordered {
template <class Key, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<Key> >
class concurrent_node_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
template <class K, class H, class P, class A, class Predicate>
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
concurrent_node_set<K, H, P, A>& c, Predicate pred);
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
namespace pmr {
template <class Key, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key> >
using concurrent_node_set = boost::unordered::concurrent_node_set<Key,
Hash, Pred, std::pmr::polymorphic_allocator<Key> >;
} // namespace pmr
#endif
} // namespace unordered
using boost::unordered::concurrent_node_set;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
@@ -397,10 +397,10 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
* on C++17 stdlib parallel algorithms.
*
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
* Heterogeneous lookup is suported by default, that is, without checking for
* any ::is_transparent typedefs --this checking is done by the wrapping
* containers.
* Consult boost::concurrent_(flat|node)_(map|set) docs for the full API
* reference. Heterogeneous lookup is suported by default, that is, without
* checking for any ::is_transparent typedefs --this checking is done by the
* wrapping containers.
*
* Thread-safe concurrency is implemented using a two-level lock system:
*
@@ -724,6 +724,14 @@ public:
BOOST_FORCEINLINE bool
insert(value_type&& x){return emplace_impl(std::move(x));}
template<typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert(element_type&& x){return emplace_impl(std::move(x));}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace(Key&& x,Args&&... args)
{
@@ -819,6 +827,30 @@ public:
group_shared{},std::forward<F>(f),std::move(x));
}
template<typename F,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_or_visit(element_type&& x, F&& f)
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
}
template<typename F,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_or_cvisit(element_type&& x, F&& f)
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
}
template<typename Key>
BOOST_FORCEINLINE std::size_t erase(const Key& x)
{
@@ -889,6 +921,29 @@ public:
super::clear();
}
template<typename Key,typename Extractor>
BOOST_FORCEINLINE void extract(const Key& x,Extractor&& ext)
{
extract_if(
x,[](const value_type&){return true;},std::forward<Extractor>(ext));
}
template<typename Key,typename F,typename Extractor>
BOOST_FORCEINLINE void extract_if(const Key& x,F&& f,Extractor&& ext)
{
auto lck=shared_access();
auto hash=this->hash_for(x);
unprotected_internal_visit(
group_exclusive{},x,this->position_for(hash),hash,
[&,this](group_type* pg,unsigned int n,element_type* p)
{
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
ext(std::move(*p),this->al());
super::erase(pg,n,p);
}
});
}
// TODO: should we accept different allocator too?
template<typename Hash2,typename Pred2>
size_type merge(concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& x)
@@ -1733,7 +1788,8 @@ private:
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
ar.reset_object_address(
std::addressof(type_policy::value_from(*loc.p)),std::addressof(x));
}
}
@@ -1742,7 +1798,7 @@ private:
{
using raw_key_type=typename std::remove_const<key_type>::type;
using raw_mapped_type=typename std::remove_const<
typename TypePolicy::mapped_type>::type;
typename type_policy::mapped_type>::type;
auto lck=exclusive_access();
std::size_t s;
@@ -1766,8 +1822,12 @@ private:
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
ar.reset_object_address(
std::addressof(type_policy::value_from(*loc.p).first),
std::addressof(k));
ar.reset_object_address(
std::addressof(type_policy::value_from(*loc.p).second),
std::addressof(m));
}
}
@@ -29,6 +29,7 @@
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/hash_traits.hpp>
#include <boost/unordered/unordered_printers.hpp>
#include <climits>
#include <cmath>
#include <cstddef>
@@ -1017,6 +1018,11 @@ struct table_arrays
rebind<group_type>;
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
// For natvis purposes
using char_pointer=
typename boost::pointer_traits<value_type_pointer>::template
rebind<unsigned char>;
table_arrays(
std::size_t gsi,std::size_t gsm,
group_type_pointer pg,value_type_pointer pe):
@@ -1453,6 +1459,11 @@ public:
using stats=table_core_stats;
#endif
#if defined(BOOST_GCC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
table_core(
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
@@ -1461,6 +1472,10 @@ public:
size_ctrl{initial_max_load(),0}
{}
#if defined(BOOST_GCC)
#pragma GCC diagnostic pop
#endif
/* genericize on an ArraysFn so that we can do things like delay an
* allocation for the group_access data required by cfoa after the move
* constructors of Hash, Pred have been invoked
@@ -2075,6 +2090,11 @@ private:
using pred_base=empty_value<Pred,1>;
using allocator_base=empty_value<Allocator,2>;
#if defined(BOOST_GCC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
/* used by allocator-extended move ctor */
table_core(Hash&& h_,Pred&& pred_,const Allocator& al_):
@@ -2085,6 +2105,10 @@ private:
{
}
#if defined(BOOST_GCC)
#pragma GCC diagnostic pop
#endif
arrays_type new_arrays(std::size_t n)const
{
return arrays_type::new_(typename arrays_type::allocator_type(al()),n);
@@ -6,6 +6,8 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#include <boost/unordered/detail/foa/types_constructibility.hpp>
#include <boost/core/allocator_access.hpp>
namespace boost {
@@ -25,6 +27,9 @@ namespace boost {
using element_type = value_type;
using types = flat_map_types<Key, T>;
using constructibility_checker = map_types_constructibility<types>;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
@@ -48,18 +53,21 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, key_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
@@ -79,8 +87,8 @@ namespace boost {
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
@@ -5,6 +5,8 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#include <boost/unordered/detail/foa/types_constructibility.hpp>
#include <boost/core/allocator_access.hpp>
namespace boost {
@@ -21,6 +23,9 @@ namespace boost {
using element_type = value_type;
using types = flat_set_types<Key>;
using constructibility_checker = set_types_constructibility<types>;
static Key& value_from(element_type& x) { return x; }
static element_type&& move(element_type& x) { return std::move(x); }
@@ -28,6 +33,7 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
@@ -37,8 +43,8 @@ namespace boost {
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
@@ -1,4 +1,5 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -11,8 +12,11 @@
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/allocator_access.hpp>
#include <type_traits>
namespace boost{
namespace unordered{
@@ -27,6 +31,13 @@ struct insert_return_type
NodeType node;
};
template <class NodeType>
struct iteratorless_insert_return_type
{
bool inserted;
NodeType node;
};
template <class TypePolicy,class Allocator>
struct node_handle_base
{
@@ -42,7 +53,27 @@ struct node_handle_base
element_type p_;
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
protected:
friend struct node_handle_access;
template<bool B>
void move_assign_allocator_if(node_handle_base&& nh)noexcept
{
move_assign_allocator_if(
std::integral_constant<bool,B>{}, std::move(nh));
}
void move_assign_allocator_if(
std::true_type, node_handle_base&& nh)noexcept
{
al()=std::move(nh.al());
}
void move_assign_allocator_if(
std::false_type, node_handle_base&&)noexcept
{
}
protected:
node_value_type& data()noexcept
{
return *(p_.p);
@@ -126,9 +157,7 @@ struct node_handle_base
BOOST_ASSERT(pocma||al()==nh.al());
type_policy::destroy(al(),&p_);
if(pocma){
al()=std::move(nh.al());
}
move_assign_allocator_if<pocma>(std::move(nh));
p_=std::move(nh.p_);
nh.reset();
@@ -153,7 +182,17 @@ struct node_handle_base
}
}
allocator_type get_allocator()const noexcept{return al();}
allocator_type get_allocator()const
{
#if defined(BOOST_GCC)
/* GCC lifetime analysis incorrectly warns about uninitialized
* allocator object under some circumstances.
*/
if(empty())__builtin_unreachable();
#endif
return al();
}
explicit operator bool()const noexcept{ return !empty();}
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
@@ -196,6 +235,82 @@ struct node_handle_base
}
};
// Internal usage of node_handle_base protected API
struct node_handle_access
{
template <class TypePolicy, class Allocator>
using node_type = node_handle_base<TypePolicy, Allocator>;
#if BOOST_WORKAROUND(BOOST_CLANG_VERSION,<190000)
// https://github.com/llvm/llvm-project/issues/25708
template <class TypePolicy, class Allocator>
struct element_type_impl
{
using type = typename node_type<TypePolicy, Allocator>::element_type;
};
template <class TypePolicy, class Allocator>
using element_type = typename element_type_impl<TypePolicy, Allocator>::type;
#else
template <class TypePolicy, class Allocator>
using element_type = typename node_type<TypePolicy, Allocator>::element_type;
#endif
template <class TypePolicy, class Allocator>
static element_type<TypePolicy, Allocator>&
element(node_type<TypePolicy, Allocator>& nh)noexcept
{
return nh.element();
}
template <class TypePolicy, class Allocator>
static element_type<TypePolicy, Allocator>
const& element(node_type<TypePolicy, Allocator> const& nh)noexcept
{
return nh.element();
}
template <class TypePolicy, class Allocator>
static void emplace(
node_type<TypePolicy, Allocator>& nh,
element_type<TypePolicy, Allocator>&& x, Allocator a)
{
nh.emplace(std::move(x), a);
}
template <class TypePolicy,class Allocator>
static void reset(node_type<TypePolicy, Allocator>& nh)
{
nh.reset();
}
};
template <class TypePolicy, class Allocator>
class node_handle_emplacer_class
{
using access = node_handle_access;
using node_type = access::node_type<TypePolicy, Allocator>;
using element_type = access::element_type<TypePolicy, Allocator>;
node_type & nh;
public:
node_handle_emplacer_class(node_type& nh_): nh(nh_) {}
void operator()(element_type&& x,Allocator a)
{
access::emplace(nh, std::move(x), a);
}
};
template <class TypePolicy, class Allocator>
node_handle_emplacer_class<TypePolicy, Allocator>
node_handle_emplacer(node_handle_base<TypePolicy, Allocator>& nh)
{
return {nh};
}
}
}
}
@@ -0,0 +1,56 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
#include <boost/unordered/detail/foa/node_handle.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template <class TypePolicy, class Allocator>
struct node_map_handle
: public node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
public:
using key_type = typename TypePolicy::key_type;
using mapped_type = typename TypePolicy::mapped_type;
constexpr node_map_handle() noexcept = default;
node_map_handle(node_map_handle&& nh) noexcept = default;
node_map_handle& operator=(node_map_handle&&) noexcept = default;
key_type& key() const
{
BOOST_ASSERT(!this->empty());
return const_cast<key_type&>(this->data().first);
}
mapped_type& mapped() const
{
BOOST_ASSERT(!this->empty());
return const_cast<mapped_type&>(this->data().second);
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
@@ -7,6 +7,8 @@
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/types_constructibility.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
@@ -29,6 +31,9 @@ namespace boost {
using element_type = foa::element_type<value_type, VoidPtr>;
using types = node_map_types<Key, T, VoidPtr>;
using constructibility_checker = map_types_constructibility<types>;
static value_type& value_from(element_type const& x)
{
return *(x.p);
@@ -68,24 +73,27 @@ namespace boost {
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
construct(al, p, detail::as_const(*copy.p));
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, key_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
@@ -95,8 +103,11 @@ namespace boost {
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
auto address = boost::to_address(p->p);
constructibility_checker::check(
al, address, std::forward<Args>(args)...);
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
al, address, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
@@ -132,8 +143,8 @@ namespace boost {
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
@@ -0,0 +1,48 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
#include <boost/unordered/detail/foa/node_handle.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template <class TypePolicy, class Allocator>
struct node_set_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
public:
using value_type = typename TypePolicy::value_type;
constexpr node_set_handle() noexcept = default;
node_set_handle(node_set_handle&& nh) noexcept = default;
node_set_handle& operator=(node_set_handle&&) noexcept = default;
value_type& value() const
{
BOOST_ASSERT(!this->empty());
return const_cast<value_type&>(this->data());
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
@@ -6,6 +6,8 @@
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/types_constructibility.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
@@ -26,6 +28,9 @@ namespace boost {
using element_type = foa::element_type<value_type, VoidPtr>;
using types = node_set_types<Key, VoidPtr>;
using constructibility_checker = set_types_constructibility<types>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
static element_type&& move(element_type& x) { return std::move(x); }
@@ -35,7 +40,7 @@ namespace boost {
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
construct(al, p, detail::as_const(*copy.p));
}
template <typename Allocator>
@@ -49,6 +54,7 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
constructibility_checker::check(al, p, std::forward<Args>(args)...);
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
@@ -58,8 +64,11 @@ namespace boost {
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
auto address = boost::to_address(p->p);
constructibility_checker::check(
al, address, std::forward<Args>(args)...);
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
al, address, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
@@ -85,8 +94,8 @@ namespace boost {
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
@@ -0,0 +1,172 @@
// Copyright (C) 2024 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_FOA_TYPES_CONSTRUCTIBILITY_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class... Args> struct check_key_type_t
{
static_assert(std::is_constructible<Key, Args...>::value,
"key_type must be constructible from Args");
};
template <class Key> struct check_key_type_t<Key>
{
static_assert(std::is_constructible<Key>::value,
"key_type must be default constructible");
};
template <class Key> struct check_key_type_t<Key, const Key&>
{
static_assert(std::is_constructible<Key, const Key&>::value,
"key_type must be copy constructible");
};
template <class Key> struct check_key_type_t<Key, Key&&>
{
static_assert(std::is_constructible<Key, Key&&>::value,
"key_type must be move constructible");
};
template <class Mapped, class... Args> struct check_mapped_type_t
{
static_assert(std::is_constructible<Mapped, Args...>::value,
"mapped_type must be constructible from Args");
};
template <class Mapped> struct check_mapped_type_t<Mapped>
{
static_assert(std::is_constructible<Mapped>::value,
"mapped_type must be default constructible");
};
template <class Mapped>
struct check_mapped_type_t<Mapped, const Mapped&>
{
static_assert(std::is_constructible<Mapped, const Mapped&>::value,
"mapped_type must be copy constructible");
};
template <class Mapped> struct check_mapped_type_t<Mapped, Mapped&&>
{
static_assert(std::is_constructible<Mapped, Mapped&&>::value,
"mapped_type must be move constructible");
};
template <class TypePolicy> struct map_types_constructibility
{
using key_type = typename TypePolicy::key_type;
using mapped_type = typename TypePolicy::mapped_type;
using init_type = typename TypePolicy::init_type;
using value_type = typename TypePolicy::value_type;
template <class A, class X, class... Args>
static void check(A&, X*, Args&&...)
{
// Pass through, as we cannot say anything about a general allocator
}
template <class... Args> static void check_key_type()
{
(void)check_key_type_t<key_type, Args...>{};
}
template <class... Args> static void check_mapped_type()
{
(void)check_mapped_type_t<mapped_type, Args...>{};
}
template <class Arg>
static void check(std::allocator<value_type>&, key_type*, Arg&&)
{
check_key_type<Arg&&>();
}
template <class Arg1, class Arg2>
static void check(
std::allocator<value_type>&, value_type*, Arg1&&, Arg2&&)
{
check_key_type<Arg1&&>();
check_mapped_type<Arg2&&>();
}
template <class Arg1, class Arg2>
static void check(std::allocator<value_type>&, value_type*,
const std::pair<Arg1, Arg2>&)
{
check_key_type<const Arg1&>();
check_mapped_type<const Arg2&>();
}
template <class Arg1, class Arg2>
static void check(
std::allocator<value_type>&, value_type*, std::pair<Arg1, Arg2>&&)
{
check_key_type<Arg1&&>();
check_mapped_type<Arg2&&>();
}
template <class... Args1, class... Args2>
static void check(std::allocator<value_type>&, value_type*,
std::piecewise_construct_t, std::tuple<Args1...>&&,
std::tuple<Args2...>&&)
{
check_key_type<Args1&&...>();
check_mapped_type<Args2&&...>();
}
template <class Arg1, class Arg2>
static void check(
std::allocator<value_type>&, init_type*, Arg1&&, Arg2&&)
{
check_key_type<Arg1&&>();
check_mapped_type<Arg2&&>();
}
template <class Arg1, class Arg2>
static void check(std::allocator<value_type>&, init_type*,
const std::pair<Arg1, Arg2>&)
{
check_key_type<const Arg1&>();
check_mapped_type<const Arg2&>();
}
template <class Arg1, class Arg2>
static void check(
std::allocator<value_type>&, init_type*, std::pair<Arg1, Arg2>&&)
{
check_key_type<Arg1&&>();
check_mapped_type<Arg2&&>();
}
template <class... Args1, class... Args2>
static void check(std::allocator<value_type>&, init_type*,
std::piecewise_construct_t, std::tuple<Args1...>&&,
std::tuple<Args2...>&&)
{
check_key_type<Args1&&...>();
check_mapped_type<Args2&&...>();
}
};
template <class TypePolicy> struct set_types_constructibility
{
using key_type = typename TypePolicy::key_type;
using value_type = typename TypePolicy::value_type;
static_assert(std::is_same<key_type, value_type>::value, "");
template <class A, class X, class... Args>
static void check(A&, X*, Args&&...)
{
// Pass through, as we cannot say anything about a general allocator
}
template <class... Args>
static void check(std::allocator<value_type>&, key_type*, Args&&...)
{
(void)check_key_type_t<key_type, Args&&...>{};
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
-17
View File
@@ -41,23 +41,6 @@ namespace boost {
typedef typename table::iterator iterator;
typedef boost::unordered::insert_return_type_map<iterator, node_type> insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_map
{
typedef boost::unordered_map<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_multimap
{
typedef boost::unordered_multimap<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
};
}
}
}
-17
View File
@@ -39,23 +39,6 @@ namespace boost {
typedef boost::unordered::insert_return_type_set<iterator, node_type>
insert_return_type;
};
template <typename T, typename H, typename P, typename A>
class instantiate_set
{
typedef boost::unordered_set<T, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
template <typename T, typename H, typename P, typename A>
class instantiate_multiset
{
typedef boost::unordered_multiset<T, H, P, A> container;
container x;
typename container::node_type node_type;
};
}
}
}
@@ -219,6 +219,17 @@ namespace boost {
using iter_to_alloc_t =
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
#endif
#if BOOST_CXX_VERSION < 201703L
template <class T>
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
{
return t;
}
template <class T> void as_const(const T&&) = delete;
#else
using std::as_const;
#endif
} // namespace detail
} // namespace unordered
} // namespace boost
@@ -1,4 +1,5 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -202,6 +203,13 @@ namespace boost {
return *this;
}
unordered_flat_map& operator=(std::initializer_list<value_type> il)
{
this->clear();
this->insert(il.begin(), il.end());
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
@@ -1,4 +1,5 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -198,6 +199,13 @@ namespace boost {
return *this;
}
unordered_flat_set& operator=(std::initializer_list<value_type> il)
{
this->clear();
this->insert(il.begin(), il.end());
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
+2 -2
View File
@@ -198,7 +198,7 @@ namespace boost {
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace_unique(
table::extractor::extract(std::forward<Args>(args)...),
table::extractor::extract(detail::as_const(args)...),
std::forward<Args>(args)...);
}
@@ -206,7 +206,7 @@ namespace boost {
iterator emplace_hint(const_iterator hint, Args&&... args)
{
return table_.emplace_hint_unique(hint,
table::extractor::extract(std::forward<Args>(args)...),
table::extractor::extract(detail::as_const(args)...),
std::forward<Args>(args)...);
}
+31 -44
View File
@@ -1,4 +1,5 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,8 +11,8 @@
#pragma once
#endif
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/concurrent_node_map_fwd.hpp>
#include <boost/unordered/detail/foa/node_map_handle.hpp>
#include <boost/unordered/detail/foa/node_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
@@ -36,45 +37,13 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class TypePolicy, class Allocator>
struct node_map_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
template <class Key, class T, class Hash, class Pred, class Alloc>
friend class boost::unordered::unordered_node_map;
public:
using key_type = typename TypePolicy::key_type;
using mapped_type = typename TypePolicy::mapped_type;
constexpr node_map_handle() noexcept = default;
node_map_handle(node_map_handle&& nh) noexcept = default;
node_map_handle& operator=(node_map_handle&&) noexcept = default;
key_type& key() const
{
BOOST_ASSERT(!this->empty());
return const_cast<key_type&>(this->data().first);
}
mapped_type& mapped() const
{
BOOST_ASSERT(!this->empty());
return const_cast<mapped_type&>(this->data().second);
}
};
} // namespace detail
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_node_map
{
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_node_map;
using map_types = detail::foa::node_map_types<Key, T,
typename boost::allocator_void_pointer<Allocator>::type>;
@@ -109,7 +78,7 @@ namespace boost {
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
using node_type = detail::node_map_handle<map_types,
using node_type = detail::foa::node_map_handle<map_types,
typename boost::allocator_rebind<Allocator,
typename map_types::value_type>::type>;
using insert_return_type =
@@ -220,6 +189,12 @@ namespace boost {
{
}
unordered_node_map(
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_node_map() = default;
unordered_node_map& operator=(unordered_node_map const& other)
@@ -235,6 +210,13 @@ namespace boost {
return *this;
}
unordered_node_map& operator=(std::initializer_list<value_type> il)
{
this->clear();
this->insert(il.begin(), il.end());
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
@@ -307,15 +289,17 @@ namespace boost {
insert_return_type insert(node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {end(), false, node_type{}};
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
auto itp = table_.insert(std::move(access::element(nh)));
if (itp.second) {
nh.reset();
access::reset(nh);
return {itp.first, true, node_type{}};
} else {
return {itp.first, false, std::move(nh)};
@@ -324,15 +308,17 @@ namespace boost {
iterator insert(const_iterator, node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return end();
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
auto itp = table_.insert(std::move(access::element(nh)));
if (itp.second) {
nh.reset();
access::reset(nh);
return itp.first;
} else {
return itp.first;
@@ -507,7 +493,8 @@ namespace boost {
BOOST_ASSERT(pos != end());
node_type nh;
auto elem = table_.extract(pos);
nh.emplace(std::move(elem), get_allocator());
detail::foa::node_handle_emplacer(nh)(
std::move(elem), get_allocator());
return nh;
}
+30 -35
View File
@@ -1,4 +1,5 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,8 +11,9 @@
#pragma once
#endif
#include <boost/unordered/concurrent_node_set_fwd.hpp>
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/node_set_handle.hpp>
#include <boost/unordered/detail/foa/node_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
@@ -35,37 +37,12 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class TypePolicy, class Allocator>
struct node_set_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
template <class Key, class Hash, class Pred, class Alloc>
friend class boost::unordered::unordered_node_set;
public:
using value_type = typename TypePolicy::value_type;
constexpr node_set_handle() noexcept = default;
node_set_handle(node_set_handle&& nh) noexcept = default;
node_set_handle& operator=(node_set_handle&&) noexcept = default;
value_type& value() const
{
BOOST_ASSERT(!this->empty());
return const_cast<value_type&>(this->data());
}
};
} // namespace detail
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class concurrent_node_set;
using set_types = detail::foa::node_set_types<Key,
typename boost::allocator_void_pointer<Allocator>::type>;
@@ -99,7 +76,7 @@ namespace boost {
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
using node_type = detail::node_set_handle<set_types,
using node_type = detail::foa::node_set_handle<set_types,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
using insert_return_type =
@@ -210,6 +187,12 @@ namespace boost {
{
}
unordered_node_set(
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_node_set() = default;
unordered_node_set& operator=(unordered_node_set const& other)
@@ -225,6 +208,13 @@ namespace boost {
return *this;
}
unordered_node_set& operator=(std::initializer_list<value_type> il)
{
this->clear();
this->insert(il.begin(), il.end());
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
@@ -312,15 +302,17 @@ namespace boost {
insert_return_type insert(node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {end(), false, node_type{}};
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
auto itp = table_.insert(std::move(access::element(nh)));
if (itp.second) {
nh.reset();
access::reset(nh);
return {itp.first, true, node_type{}};
} else {
return {itp.first, false, std::move(nh)};
@@ -329,15 +321,17 @@ namespace boost {
iterator insert(const_iterator, node_type&& nh)
{
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return end();
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
auto itp = table_.insert(std::move(access::element(nh)));
if (itp.second) {
nh.reset();
access::reset(nh);
return itp.first;
} else {
return itp.first;
@@ -395,7 +389,8 @@ namespace boost {
BOOST_ASSERT(pos != end());
node_type nh;
auto elem = table_.extract(pos);
nh.emplace(std::move(elem), get_allocator());
detail::foa::node_handle_emplacer(nh)(
std::move(elem), get_allocator());
return nh;
}
@@ -0,0 +1,414 @@
// Copyright 2024 Braden Ganetsky
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// Generated on 2024-08-25T17:48:54
#ifndef BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
#define BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS
#if defined(__ELF__)
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverlength-strings"
#endif
__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
".ascii \"\\4gdb.inlined-script.BOOST_UNORDERED_UNORDERED_PRINTERS_HPP\\n\"\n"
".ascii \"import gdb.printing\\n\"\n"
".ascii \"import gdb.xmethod\\n\"\n"
".ascii \"import re\\n\"\n"
".ascii \"import math\\n\"\n"
".ascii \"class BoostUnorderedHelpers:\\n\"\n"
".ascii \" def maybe_unwrap_atomic(n):\\n\"\n"
".ascii \" if f\\\"{n.type.strip_typedefs()}\\\".startswith(\\\"std::atomic<\\\"):\\n\"\n"
".ascii \" underlying_type = n.type.template_argument(0)\\n\"\n"
".ascii \" return n.cast(underlying_type)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return n\\n\"\n"
".ascii \" def maybe_unwrap_foa_element(e):\\n\"\n"
".ascii \" if f\\\"{e.type.strip_typedefs()}\\\".startswith(\\\"boost::unordered::detail::foa::element_type<\\\"):\\n\"\n"
".ascii \" return e[\\\"p\\\"]\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return e\\n\"\n"
".ascii \" def maybe_unwrap_reference(value):\\n\"\n"
".ascii \" if value.type.code == gdb.TYPE_CODE_REF:\\n\"\n"
".ascii \" return value.referenced_value()\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return value\\n\"\n"
".ascii \" def countr_zero(n):\\n\"\n"
".ascii \" for i in range(32):\\n\"\n"
".ascii \" if (n & (1 << i)) != 0:\\n\"\n"
".ascii \" return i\\n\"\n"
".ascii \" return 32\\n\"\n"
".ascii \"class BoostUnorderedPointerCustomizationPoint:\\n\"\n"
".ascii \" def __init__(self, any_ptr):\\n\"\n"
".ascii \" vis = gdb.default_visualizer(any_ptr)\\n\"\n"
".ascii \" if vis is None:\\n\"\n"
".ascii \" self.to_address = lambda ptr: ptr\\n\"\n"
".ascii \" self.next = lambda ptr, offset: ptr + offset\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)\\n\"\n"
".ascii \" self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)\\n\"\n"
".ascii \"class BoostUnorderedFcaPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"buckets_\\\"][\\\"buckets\\\"])\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" size = self.val[\\\"table_\\\"][\\\"size_\\\"]\\n\"\n"
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
".ascii \" def display_hint(self):\\n\"\n"
".ascii \" return \\\"map\\\"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" def generator():\\n\"\n"
".ascii \" grouped_buckets = self.val[\\\"table_\\\"][\\\"buckets_\\\"]\\n\"\n"
".ascii \" size = grouped_buckets[\\\"size_\\\"]\\n\"\n"
".ascii \" buckets = grouped_buckets[\\\"buckets\\\"]\\n\"\n"
".ascii \" bucket_index = 0\\n\"\n"
".ascii \" count = 0\\n\"\n"
".ascii \" while bucket_index != size:\\n\"\n"
".ascii \" current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)\\n\"\n"
".ascii \" node = self.cpo.to_address(current_bucket.dereference()[\\\"next\\\"])\\n\"\n"
".ascii \" while node != 0:\\n\"\n"
".ascii \" value = node.dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
".ascii \" if self.is_map:\\n\"\n"
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
".ascii \" yield \\\"\\\", first\\n\"\n"
".ascii \" yield \\\"\\\", second\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield \\\"\\\", count\\n\"\n"
".ascii \" yield \\\"\\\", value\\n\"\n"
".ascii \" count += 1\\n\"\n"
".ascii \" node = self.cpo.to_address(node.dereference()[\\\"next\\\"])\\n\"\n"
".ascii \" bucket_index += 1\\n\"\n"
".ascii \" return generator()\\n\"\n"
".ascii \"class BoostUnorderedFcaIteratorPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p\\\"])\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" if self.valid():\\n\"\n"
".ascii \" value = self.cpo.to_address(self.val[\\\"p\\\"]).dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
".ascii \" def valid(self):\\n\"\n"
".ascii \" return (self.cpo.to_address(self.val[\\\"p\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"itb\\\"][\\\"p\\\"]) != 0)\\n\"\n"
".ascii \"class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return \\\"[stats]\\\"\\n\"\n"
".ascii \" def display_hint(self):\\n\"\n"
".ascii \" return \\\"map\\\"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" def generator():\\n\"\n"
".ascii \" members = [\\\"insertion\\\", \\\"successful_lookup\\\", \\\"unsuccessful_lookup\\\"]\\n\"\n"
".ascii \" for member in members:\\n\"\n"
".ascii \" yield \\\"\\\", member\\n\"\n"
".ascii \" yield \\\"\\\", self.val[member]\\n\"\n"
".ascii \" return generator()\\n\"\n"
".ascii \"class BoostUnorderedFoaCumulativeStatsPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" self.n = self.val[\\\"n\\\"]\\n\"\n"
".ascii \" self.N = self.val.type.template_argument(0)\\n\"\n"
".ascii \" def display_hint(self):\\n\"\n"
".ascii \" return \\\"map\\\"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" def generator():\\n\"\n"
".ascii \" yield \\\"\\\", \\\"count\\\"\\n\"\n"
".ascii \" yield \\\"\\\", self.n\\n\"\n"
".ascii \" sequence_stats_data = gdb.lookup_type(\\\"boost::unordered::detail::foa::sequence_stats_data\\\")\\n\"\n"
".ascii \" data = self.val[\\\"data\\\"]\\n\"\n"
".ascii \" arr = data.address.reinterpret_cast(sequence_stats_data.pointer())\\n\"\n"
".ascii \" def build_string(idx):\\n\"\n"
".ascii \" entry = arr[idx]\\n\"\n"
".ascii \" avg = float(entry[\\\"m\\\"])\\n\"\n"
".ascii \" var = float(entry[\\\"s\\\"] / self.n) if (self.n != 0) else 0.0\\n\"\n"
".ascii \" dev = math.sqrt(var)\\n\"\n"
".ascii \" return f\\\"{{avg = {avg}, var = {var}, dev = {dev}}}\\\"\\n\"\n"
".ascii \" if self.N > 0:\\n\"\n"
".ascii \" yield \\\"\\\", \\\"probe_length\\\"\\n\"\n"
".ascii \" yield \\\"\\\", build_string(0)\\n\"\n"
".ascii \" if self.N > 1:\\n\"\n"
".ascii \" yield \\\"\\\", \\\"num_comparisons\\\"\\n\"\n"
".ascii \" yield \\\"\\\", build_string(1)\\n\"\n"
".ascii \" return generator()\\n\"\n"
".ascii \"class BoostUnorderedFoaPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val[\\\"table_\\\"][\\\"size_ctrl\\\"][\\\"size\\\"])\\n\"\n"
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
".ascii \" def display_hint(self):\\n\"\n"
".ascii \" return \\\"map\\\"\\n\"\n"
".ascii \" def is_regular_layout(self, group):\\n\"\n"
".ascii \" typename = group[\\\"m\\\"].type.strip_typedefs()\\n\"\n"
".ascii \" array_size = typename.sizeof // typename.target().sizeof\\n\"\n"
".ascii \" if array_size == 16:\\n\"\n"
".ascii \" return True\\n\"\n"
".ascii \" elif array_size == 2:\\n\"\n"
".ascii \" return False\\n\"\n"
".ascii \" def match_occupied(self, group):\\n\"\n"
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
".ascii \" if self.is_regular_layout(group):\\n\"\n"
".ascii \" bits = [1 << b for b in range(16) if at(b) == 0]\\n\"\n"
".ascii \" return 0x7FFF & ~sum(bits)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" xx = at(0) | at(1)\\n\"\n"
".ascii \" yy = xx | (xx >> 32)\\n\"\n"
".ascii \" return 0x7FFF & (yy | (yy >> 16))\\n\"\n"
".ascii \" def is_sentinel(self, group, pos):\\n\"\n"
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
".ascii \" N = group[\\\"N\\\"]\\n\"\n"
".ascii \" sentinel_ = group[\\\"sentinel_\\\"]\\n\"\n"
".ascii \" if self.is_regular_layout(group):\\n\"\n"
".ascii \" return pos == N-1 and at(N-1) == sentinel_\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" def generator():\\n\"\n"
".ascii \" table = self.val[\\\"table_\\\"]\\n\"\n"
".ascii \" groups = self.cpo.to_address(table[\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
".ascii \" elements = self.cpo.to_address(table[\\\"arrays\\\"][\\\"elements_\\\"])\\n\"\n"
".ascii \" pc_ = groups.cast(gdb.lookup_type(\\\"unsigned char\\\").pointer())\\n\"\n"
".ascii \" p_ = elements\\n\"\n"
".ascii \" first_time = True\\n\"\n"
".ascii \" mask = 0\\n\"\n"
".ascii \" n0 = 0\\n\"\n"
".ascii \" n = 0\\n\"\n"
".ascii \" count = 0\\n\"\n"
".ascii \" while p_ != 0:\\n\"\n"
".ascii \" # This if block mirrors the condition in the begin() call\\n\"\n"
".ascii \" if (not first_time) or (self.match_occupied(groups.dereference()) & 1):\\n\"\n"
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)\\n\"\n"
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
".ascii \" if self.is_map:\\n\"\n"
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
".ascii \" yield \\\"\\\", first\\n\"\n"
".ascii \" yield \\\"\\\", second\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield \\\"\\\", count\\n\"\n"
".ascii \" yield \\\"\\\", value\\n\"\n"
".ascii \" count += 1\\n\"\n"
".ascii \" first_time = False\\n\"\n"
".ascii \" n0 = pc_.cast(gdb.lookup_type(\\\"uintptr_t\\\")) % groups.dereference().type.sizeof\\n\"\n"
".ascii \" pc_ = self.cpo.next(pc_, -n0)\\n\"\n"
".ascii \" mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)\\n\"\n"
".ascii \" while mask == 0:\\n\"\n"
".ascii \" pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)\\n\"\n"
".ascii \" p_ = self.cpo.next(p_, groups.dereference()[\\\"N\\\"])\\n\"\n"
".ascii \" mask = self.match_occupied(pc_.cast(groups.type).dereference())\\n\"\n"
".ascii \" n = BoostUnorderedHelpers.countr_zero(mask)\\n\"\n"
".ascii \" if self.is_sentinel(pc_.cast(groups.type).dereference(), n):\\n\"\n"
".ascii \" p_ = 0\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" pc_ = self.cpo.next(pc_, n)\\n\"\n"
".ascii \" p_ = self.cpo.next(p_, n - n0)\\n\"\n"
".ascii \" return generator()\\n\"\n"
".ascii \"class BoostUnorderedFoaIteratorPrinter:\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p_\\\"])\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" if self.valid():\\n\"\n"
".ascii \" element = self.cpo.to_address(self.val[\\\"p_\\\"])\\n\"\n"
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)\\n\"\n"
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
".ascii \" def valid(self):\\n\"\n"
".ascii \" return (self.cpo.to_address(self.val[\\\"p_\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"pc_\\\"]) != 0)\\n\"\n"
".ascii \"def boost_unordered_build_pretty_printer():\\n\"\n"
".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"boost_unordered\\\")\\n\"\n"
".ascii \" add_template_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}<.*>$\\\", printer)\\n\"\n"
".ascii \" add_concrete_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}$\\\", printer)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_map\\\", BoostUnorderedFcaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multimap\\\", BoostUnorderedFcaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_set\\\", BoostUnorderedFcaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multiset\\\", BoostUnorderedFcaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::c_iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::table_iterator\\\", BoostUnorderedFoaIteratorPrinter)\\n\"\n"
".ascii \" add_concrete_printer(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::concurrent_cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
".ascii \" return pp\\n\"\n"
".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())\\n\"\n"
".ascii \"# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html\\n\"\n"
".ascii \"class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):\\n\"\n"
".ascii \" def __init__(self):\\n\"\n"
".ascii \" gdb.xmethod.XMethod.__init__(self, \\\"get_stats\\\")\\n\"\n"
".ascii \" def get_worker(self, method_name):\\n\"\n"
".ascii \" if method_name == \\\"get_stats\\\":\\n\"\n"
".ascii \" return BoostUnorderedFoaGetStatsWorker()\\n\"\n"
".ascii \"class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):\\n\"\n"
".ascii \" def get_arg_types(self):\\n\"\n"
".ascii \" return None\\n\"\n"
".ascii \" def get_result_type(self, obj):\\n\"\n"
".ascii \" return gdb.lookup_type(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\")\\n\"\n"
".ascii \" def __call__(self, obj):\\n\"\n"
".ascii \" try:\\n\"\n"
".ascii \" return obj[\\\"table_\\\"][\\\"cstats\\\"]\\n\"\n"
".ascii \" except gdb.error:\\n\"\n"
".ascii \" print(\\\"Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.\\\")\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):\\n\"\n"
".ascii \" def __init__(self):\\n\"\n"
".ascii \" gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')\\n\"\n"
".ascii \" self.methods = [BoostUnorderedFoaGetStatsMethod()]\\n\"\n"
".ascii \" def match(self, class_type, method_name):\\n\"\n"
".ascii \" template_name = f\\\"{class_type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
".ascii \" regex = \\\"^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$\\\"\\n\"\n"
".ascii \" if not re.match(regex, template_name):\\n\"\n"
".ascii \" return None\\n\"\n"
".ascii \" workers = []\\n\"\n"
".ascii \" for method in self.methods:\\n\"\n"
".ascii \" if method.enabled:\\n\"\n"
".ascii \" worker = method.get_worker(method_name)\\n\"\n"
".ascii \" if worker:\\n\"\n"
".ascii \" workers.append(worker)\\n\"\n"
".ascii \" return workers\\n\"\n"
".ascii \"gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())\\n\"\n"
".ascii \"\\\"\\\"\\\" Fancy pointer support \\\"\\\"\\\"\\n\"\n"
".ascii \"\\\"\\\"\\\"\\n\"\n"
".ascii \"To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,\\n\"\n"
".ascii \"create a pretty-printer for your own type with the following additional methods.\\n\"\n"
".ascii \"(Note, this is assuming the presence of a type alias `pointer` for the underlying\\n\"\n"
".ascii \"raw pointer type, Substitute whichever name is applicable in your case.)\\n\"\n"
".ascii \"`boost_to_address(fancy_ptr)`\\n\"\n"
".ascii \" * A static method, but `@staticmethod` is not required\\n\"\n"
".ascii \" * Parameter `fancy_ptr` of type `gdb.Value`\\n\"\n"
".ascii \" * Its `.type` will be your fancy pointer type\\n\"\n"
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer\\n\"\n"
".ascii \" * This method should be equivalent to calling `operator->()` on your fancy pointer in C++\\n\"\n"
".ascii \"`boost_next(raw_ptr, offset)`\\n\"\n"
".ascii \" * Parameter `raw_ptr` of type `gdb.Value`\\n\"\n"
".ascii \" * Its `.type` will be `pointer`\\n\"\n"
".ascii \" * Parameter `offset`\\n\"\n"
".ascii \" * Either has integer type, or is of type `gdb.Value` with an underlying integer\\n\"\n"
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations\\n\"\n"
".ascii \" 1. Convert the incoming raw pointer to your fancy pointer\\n\"\n"
".ascii \" 2. Use operator+= to add the offset to the fancy pointer\\n\"\n"
".ascii \" 3. Convert back to the raw pointer\\n\"\n"
".ascii \" * Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above\\n\"\n"
".ascii \" * Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers\\n\"\n"
".ascii \"Example\\n\"\n"
".ascii \"```\\n\"\n"
".ascii \"class MyFancyPtrPrinter:\\n\"\n"
".ascii \" ...\\n\"\n"
".ascii \" # Equivalent to `operator->()`\\n\"\n"
".ascii \" def boost_to_address(fancy_ptr):\\n\"\n"
".ascii \" ...\\n\"\n"
".ascii \" return ...\\n\"\n"
".ascii \" # Equivalent to `operator+()`\\n\"\n"
".ascii \" def boost_next(raw_ptr, offset):\\n\"\n"
".ascii \" ...\\n\"\n"
".ascii \" return ...\\n\"\n"
".ascii \" ...\\n\"\n"
".ascii \"```\\n\"\n"
".ascii \"\\\"\\\"\\\"\\n\"\n"
".byte 0\n"
".popsection\n");
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // defined(__ELF__)
#endif // !defined(BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS)
#endif // !defined(BOOST_UNORDERED_UNORDERED_PRINTERS_HPP)
+2 -2
View File
@@ -194,7 +194,7 @@ namespace boost {
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace_unique(
table::extractor::extract(std::forward<Args>(args)...),
table::extractor::extract(detail::as_const(args)...),
std::forward<Args>(args)...);
}
@@ -202,7 +202,7 @@ namespace boost {
iterator emplace_hint(const_iterator hint, Args&&... args)
{
return table_.emplace_hint_unique(hint,
table::extractor::extract(std::forward<Args>(args)...),
table::extractor::extract(detail::as_const(args)...),
std::forward<Args>(args)...);
}
+1 -1
View File
@@ -135,7 +135,7 @@ foa_tests(SOURCES exception/merge_exception_tests.cpp)
# CFOA tests
cfoa_tests(SOURCES cfoa/insert_tests.cpp)
cfoa_tests(SOURCES cfoa/insert_tests.cpp COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj>)
cfoa_tests(SOURCES cfoa/erase_tests.cpp)
cfoa_tests(SOURCES cfoa/try_emplace_tests.cpp)
cfoa_tests(SOURCES cfoa/emplace_tests.cpp)
+56 -8
View File
@@ -8,7 +8,7 @@
import path ;
import regex ;
import testing ;
import ../../config/checks/config : requires ;
import config : requires ;
path-constant TOP : . ;
@@ -21,6 +21,9 @@ local msvc-flags = /wd4494 ;
project
: requirements
<library>/boost/concept_check//boost_concept_check
<library>/boost/compat//boost_compat
<library>/boost/iterator//boost_iterator
<warnings>pedantic
<toolset>intel:<warnings>on
@@ -42,6 +45,10 @@ path-constant BOOST_UNORDERED_TEST_DIR : . ;
run quick.cpp ;
compile debuggability/visualization_tests.cpp
: <target-os>cygwin:<define>_XOPEN_SOURCE=600
;
compile unordered/self_include_tests_obj.cpp
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
: tl_unordered_map_hpp ;
@@ -108,6 +115,7 @@ local FCA_TESTS =
move_tests
narrow_cast_tests
node_handle_tests
node_handle_allocator_tests
noexcept_tests
post_move_tests
prime_fmod_tests
@@ -148,7 +156,7 @@ run unordered/serialization_tests.cpp
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off ;
<library>/boost/serialization//boost_serialization/<warnings>off ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
@@ -230,6 +238,7 @@ local FOA_TESTS =
fancy_pointer_noleak
pmr_allocator_tests
stats_tests
node_handle_allocator_tests
;
for local test in $(FOA_TESTS)
@@ -251,7 +260,7 @@ run unordered/serialization_tests.cpp
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off
<library>/boost/serialization//boost_serialization/<warnings>off
: foa_serialization_tests ;
local FOA_EXCEPTION_TESTS =
@@ -286,11 +295,19 @@ local MMAP_CONTAINERS =
for local container in $(MMAP_CONTAINERS)
{
run unordered/mmap_tests.cpp /boost/filesystem//boost_filesystem : :
run unordered/mmap_tests.cpp
/boost/filesystem//boost_filesystem
/boost/interprocess//boost_interprocess
/boost/process//boost_process
/boost/uuid//boost_uuid
: :
: <define>BOOST_UNORDERED_FOA_MMAP_MAP_TYPE="boost::$(container)"
<warnings>off
<link>static
<toolset>gcc-4.8:<build>no # Boost.Process does not compile
<toolset>gcc-4.9:<build>no # ditto
<target-os>cygwin:<build>no
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
: foa_mmap_$(container)_tests ;
}
@@ -306,11 +323,10 @@ alias foa_tests :
;
local CFOA_TESTS =
insert_tests
erase_tests
try_emplace_tests
emplace_tests
visit_tests
extract_insert_tests
constructor_tests
assign_tests
clear_tests
@@ -336,6 +352,7 @@ local CFOA_TESTS =
explicit_alloc_ctor_tests
pmr_allocator_tests
stats_tests
node_handle_allocator_tests
;
for local test in $(CFOA_TESTS)
@@ -346,6 +363,28 @@ for local test in $(CFOA_TESTS)
;
}
run cfoa/insert_tests.cpp
:
:
: $(CPP11) <threading>multi
<toolset>msvc:<cxxflags>/bigobj
<toolset>gcc:<inlining>on
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
: cfoa_insert_tests ;
run cfoa/visit_tests.cpp
:
:
: $(CPP11) <threading>multi
<toolset>msvc:<cxxflags>/bigobj
<toolset>gcc:<inlining>on
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
: cfoa_visit_tests ;
run cfoa/serialization_tests.cpp
:
:
@@ -357,12 +396,12 @@ run cfoa/serialization_tests.cpp
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off
<library>/boost/serialization//boost_serialization/<warnings>off
: cfoa_serialization_tests ;
rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
{
run cfoa/interprocess_concurrency_tests.cpp /boost/filesystem//boost_filesystem : :
run cfoa/interprocess_concurrency_tests.cpp : :
: <define>$(defines)
<warnings>off
<link>static
@@ -370,7 +409,14 @@ rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
<toolset>clang-3.6:<build>no # idem
<toolset>clang-3.7:<build>no # idem
<toolset>clang-3.8:<build>no # idem
<toolset>gcc-4.8:<build>no # idem
<toolset>gcc-4.9:<build>no # idem
<target-os>cygwin:<build>no
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
<library>/boost/filesystem//boost_filesystem
<library>/boost/interprocess//boost_interprocess
<library>/boost/process//boost_process
<library>/boost/uuid//boost_uuid
: $(name) ;
}
@@ -381,6 +427,8 @@ make_cfoa_interprocess_concurrency_tests cfoa_interproc_conc_tests_stats
alias cfoa_tests :
cfoa_$(CFOA_TESTS)
cfoa_insert_tests
cfoa_visit_tests
cfoa_serialization_tests
cfoa_interproc_conc_tests
cfoa_interproc_conc_tests_stats ;
+87 -28
View File
@@ -1,12 +1,19 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "helpers.hpp"
#include "../helpers/replace_allocator.hpp"
#include "../objects/non_default_ctble_allocator.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <vector>
#if defined(__clang__) && defined(__has_warning)
@@ -37,19 +44,35 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using fancy_map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
using fancy_node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
using fancy_set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator2<raii> >;
using fancy_node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator2<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
fancy_map_type* fancy_test_map;
fancy_node_map_type* fancy_test_node_map;
fancy_set_type* fancy_test_set;
fancy_node_set_type* fancy_test_node_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
@@ -102,7 +125,9 @@ std::initializer_list<set_type::value_type> set_init_list{
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
template <class T,bool POCCA, bool POCMA>
struct poca_allocator: fancy_allocator<T>
@@ -862,6 +887,28 @@ namespace {
check_raii_counts();
}
template <class X, class GF>
void initializer_list_assign_gh276(
X*, GF gen_factory, test::random_generator rg)
{
// https://github.com/boostorg/unordered/issues/276
using replaced_allocator_container = test::replace_allocator<
X, test::non_default_ctble_allocator<int> >;
using replaced_allocator_type =
typename replaced_allocator_container::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(4, [&] { return gen(rg); });
replaced_allocator_container
x(replaced_allocator_type(0)),
y(values.begin(), values.end(), replaced_allocator_type(0));
x = {values[0], values[1], values[2], values[3]};
BOOST_TEST(x == y);
}
template <class X, class GF>
void insert_and_assign(X*, GF gen_factory, test::random_generator rg)
{
@@ -928,7 +975,7 @@ namespace {
}
template <class X, class GF>
void flat_move_assign(X*, GF gen_factory, test::random_generator rg)
void nonconcurrent_move_assign(X*, GF gen_factory, test::random_generator rg)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
@@ -950,16 +997,17 @@ namespace {
{
raii::reset_counts();
flat_container<X> flat(values.begin(), values.end(), values.size(),
nonconcurrent_container<X> nonc(
values.begin(), values.end(), values.size(),
hasher(1), key_equal(2), allocator_type(3));
X x(0, hasher(2), key_equal(1), allocator_type(3));
BOOST_TEST(flat.get_allocator() == x.get_allocator());
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
x = std::move(flat);
x = std::move(nonc);
BOOST_TEST(flat.empty());
BOOST_TEST(nonc.empty());
BOOST_TEST_EQ(x.size(), reference_cont.size());
test_fuzzy_matches_reference(x, reference_cont, rg);
@@ -983,17 +1031,18 @@ namespace {
X x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(3));
nonconcurrent_container<X> nonc(
0, hasher(2), key_equal(1), allocator_type(3));
BOOST_TEST(flat.get_allocator() == x.get_allocator());
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
flat = std::move(x);
nonc = std::move(x);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(flat.size(), reference_cont.size());
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * reference_cont.size());
@@ -1008,16 +1057,17 @@ namespace {
{
raii::reset_counts();
flat_container<X> flat(values.begin(), values.end(), values.size(),
nonconcurrent_container<X> nonc(
values.begin(), values.end(), values.size(),
hasher(1), key_equal(2), allocator_type(3));
X x(0, hasher(2), key_equal(1), allocator_type(4));
BOOST_TEST(flat.get_allocator() != x.get_allocator());
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
x = std::move(flat);
x = std::move(nonc);
BOOST_TEST(flat.empty());
BOOST_TEST(nonc.empty());
BOOST_TEST_EQ(x.size(), reference_cont.size());
test_fuzzy_matches_reference(x, reference_cont, rg);
@@ -1043,17 +1093,18 @@ namespace {
X x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(4));
nonconcurrent_container<X> nonc(
0, hasher(2), key_equal(1), allocator_type(4));
BOOST_TEST(flat.get_allocator() != x.get_allocator());
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
flat = std::move(x);
nonc = std::move(x);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(flat.size(), reference_cont.size());
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * reference_cont.size());
@@ -1073,29 +1124,37 @@ namespace {
// clang-format off
UNORDERED_TEST(
copy_assign,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_assign,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
initializer_list_assign,
((test_map_and_init_list)(test_set_and_init_list)))
((test_map_and_init_list)(test_node_map_and_init_list)
(test_set_and_init_list)(test_node_set_and_init_list)))
UNORDERED_TEST(
initializer_list_assign_gh276,
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)))
UNORDERED_TEST(
insert_and_assign,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((init_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
flat_move_assign,
((test_map)(test_set)(fancy_test_map)(fancy_test_set))
nonconcurrent_move_assign,
((test_map)(test_node_map)(test_set)(test_node_set)
(fancy_test_map)(fancy_test_node_map)(fancy_test_set)(fancy_test_node_set))
((init_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+13 -3
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
test::seed_t initialize_seed{674140082};
@@ -20,11 +22,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
namespace {
template <class X, class GF>
@@ -130,12 +140,12 @@ namespace {
// clang-format off
UNORDERED_TEST(
clear_tests,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_clear,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+88 -8
View File
@@ -1,15 +1,19 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
#define BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/concurrent_node_map_fwd.hpp>
#include <boost/unordered/concurrent_node_set_fwd.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_node_set.hpp>
#include <cstddef>
#include <type_traits>
@@ -27,6 +31,31 @@ struct value_cardinality<std::pair<K, V> >
static constexpr std::size_t value=2;
};
template <typename K>
struct value_nonconst_cardinality
{
static constexpr std::size_t value=1;
};
template <typename K, typename V>
struct value_nonconst_cardinality<std::pair<K, V> >
{
static constexpr std::size_t value=
1 * !std::is_const<K>::value +
1 * !std::is_const<V>::value ;
};
template <class Container>
struct is_container_node_based: std::false_type {};
template <typename K, typename V, typename H, typename P, typename A>
struct is_container_node_based<boost::concurrent_node_map<K, V, H, P, A> >
: std::true_type {};
template <typename K, typename H, typename P, typename A>
struct is_container_node_based<boost::concurrent_node_set<K, H, P, A> >
: std::true_type {};
template <class Container>
struct reference_container_impl;
@@ -39,30 +68,55 @@ struct reference_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
using type = boost::unordered_flat_map<K, V>;
};
template <typename K, typename V, typename H, typename P, typename A>
struct reference_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
{
using type = boost::unordered_node_map<K, V>;
};
template <typename K, typename H, typename P, typename A>
struct reference_container_impl<boost::concurrent_flat_set<K, H, P, A> >
{
using type = boost::unordered_flat_set<K>;
};
template <class Container>
struct flat_container_impl;
template <typename K, typename H, typename P, typename A>
struct reference_container_impl<boost::concurrent_node_set<K, H, P, A> >
{
using type = boost::unordered_node_set<K>;
};
template <class Container>
using flat_container = typename flat_container_impl<Container>::type;
struct nonconcurrent_container_impl;
template <class Container>
using nonconcurrent_container =
typename nonconcurrent_container_impl<Container>::type;
template <typename K, typename V, typename H, typename P, typename A>
struct flat_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
struct nonconcurrent_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
{
using type = boost::unordered_flat_map<K, V, H, P, A>;
};
template <typename K, typename V, typename H, typename P, typename A>
struct nonconcurrent_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
{
using type = boost::unordered_node_map<K, V, H, P, A>;
};
template <typename K, typename H, typename P, typename A>
struct flat_container_impl<boost::concurrent_flat_set<K, H, P, A> >
struct nonconcurrent_container_impl<boost::concurrent_flat_set<K, H, P, A> >
{
using type = boost::unordered_flat_set<K, H, P, A>;
};
template <typename K, typename H, typename P, typename A>
struct nonconcurrent_container_impl<boost::concurrent_node_set<K, H, P, A> >
{
using type = boost::unordered_node_set<K, H, P, A>;
};
template <typename Container, template <typename> class Allocator>
struct replace_allocator_impl;
@@ -95,7 +149,33 @@ struct replace_allocator_impl<
using type =
boost::concurrent_flat_set<K, H, P, Allocator<value_type> >;
};
template <
typename K, typename V, typename H, typename P, typename A,
template <typename> class Allocator
>
struct replace_allocator_impl<
boost::concurrent_node_map<K, V, H, P, A>, Allocator>
{
using value_type =
typename boost::concurrent_node_map<K, V, H, P, A>::value_type;
using type =
boost::concurrent_node_map<K, V, H, P, Allocator<value_type> >;
};
template <
typename K, typename H, typename P, typename A,
template <typename> class Allocator
>
struct replace_allocator_impl<
boost::concurrent_node_set<K, H, P, A>, Allocator>
{
using value_type =
typename boost::concurrent_node_set<K, H, P, A>::value_type;
using type =
boost::concurrent_node_set<K, H, P, Allocator<value_type> >;
};
template <typename K>
K const& get_key(K const& x) { return x; }
+52 -37
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
test::seed_t initialize_seed(4122023);
@@ -52,11 +54,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
@@ -109,7 +119,9 @@ std::initializer_list<set_type::value_type> set_init_list{
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
namespace {
template <class X>
@@ -865,7 +877,7 @@ namespace {
}
template <class X, class GF>
void flat_constructor(X*, GF gen_factory, test::random_generator rg)
void nonconcurrent_constructor(X*, GF gen_factory, test::random_generator rg)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
@@ -875,12 +887,13 @@ namespace {
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_cont = reference_container<X>(values.begin(), values.end());
auto reference_flat= flat_container<X>(values.begin(), values.end());
auto reference_nonc =
nonconcurrent_container<X>(values.begin(), values.end());
raii::reset_counts();
{
flat_container<X> flat(
nonconcurrent_container<X> nonc(
values.begin(), values.end(), reference_cont.size(), hasher(1),
key_equal(2), allocator_type(3));
@@ -890,9 +903,9 @@ namespace {
BOOST_TEST_EQ(old_dc, 0u);
BOOST_TEST_EQ(old_mc, 0u);
BOOST_TEST_EQ(old_cc, value_type_cardinality * flat.size());
BOOST_TEST_EQ(old_cc, value_type_cardinality * nonc.size());
X x(std::move(flat));
X x(std::move(nonc));
test_fuzzy_matches_reference(x, reference_cont, rg);
@@ -904,15 +917,16 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST(flat.empty());
BOOST_TEST(nonc.empty());
}
check_raii_counts();
{
flat_container<X> flat(0, hasher(1), key_equal(2), allocator_type(3));
nonconcurrent_container<X> nonc(
0, hasher(1), key_equal(2), allocator_type(3));
X x(std::move(flat));
X x(std::move(nonc));
BOOST_TEST(x.empty());
@@ -920,7 +934,7 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST(flat.empty());
BOOST_TEST(nonc.empty());
}
check_raii_counts();
@@ -937,17 +951,17 @@ namespace {
BOOST_TEST_EQ(old_mc, 0u);
BOOST_TEST_EQ(old_cc, 2u * value_type_cardinality * x.size());
flat_container<X> flat(std::move(x));
nonconcurrent_container<X> nonc(std::move(x));
BOOST_TEST(flat == reference_flat);
BOOST_TEST(nonc == reference_nonc);
BOOST_TEST_EQ(+raii::default_constructor, old_dc);
BOOST_TEST_EQ(+raii::move_constructor, old_mc);
BOOST_TEST_EQ(+raii::copy_constructor, old_cc);
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
BOOST_TEST(flat.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
BOOST_TEST(nonc.get_allocator() == allocator_type(3));
BOOST_TEST(x.empty());
}
@@ -957,13 +971,13 @@ namespace {
{
X x(0, hasher(1), key_equal(2), allocator_type(3));
flat_container<X> flat(std::move(x));
nonconcurrent_container<X> nonc(std::move(x));
BOOST_TEST(flat.empty());
BOOST_TEST(nonc.empty());
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
BOOST_TEST(flat.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
BOOST_TEST(nonc.get_allocator() == allocator_type(3));
BOOST_TEST(x.empty());
}
@@ -976,83 +990,84 @@ namespace {
// clang-format off
UNORDERED_TEST(
default_constructor,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
bucket_count_with_hasher_key_equal_and_allocator,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
soccc,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
from_iterator_range,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor_with_insertion,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_constructor,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_constructor_with_insertion,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
iterator_range_with_allocator,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
explicit_allocator,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
initializer_list_with_all_params,
((test_map_and_init_list)(test_set_and_init_list)))
((test_map_and_init_list)(test_node_map_and_init_list)
(test_set_and_init_list)(test_node_set_and_init_list)))
UNORDERED_TEST(
bucket_count_and_allocator,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
bucket_count_with_hasher_and_allocator,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
iterator_range_with_bucket_count_and_allocator,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
iterator_range_with_bucket_count_hasher_and_allocator,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
flat_constructor,
((test_map)(test_set))
nonconcurrent_constructor,
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+27 -11
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Copyright (C) 2024 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)
@@ -9,6 +9,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -80,11 +82,8 @@ namespace {
}
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
call_impl(values, x);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} lvalue_emplacer;
@@ -197,7 +196,12 @@ namespace {
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);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else {
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
@@ -208,8 +212,8 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
static constexpr auto input_type_nonconst_cardinality =
value_nonconst_cardinality<T>::value;
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
@@ -235,10 +239,18 @@ namespace {
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(
raii::move_constructor, input_type_nonconst_cardinality * x.size());
}
else {
BOOST_TEST_GT(
raii::move_constructor, input_type_nonconst_cardinality * x.size());
}
#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);
@@ -280,7 +292,9 @@ namespace {
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_node_map<raii, raii>* node_map;
boost::unordered::concurrent_flat_set<raii>* set;
boost::unordered::concurrent_node_set<raii>* node_set;
} // namespace
@@ -292,7 +306,7 @@ using test::sequential;
UNORDERED_TEST(
emplace,
((map)(set))
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_emplacer)(norehash_lvalue_emplacer)
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
@@ -455,6 +469,8 @@ namespace {
boost::unordered::concurrent_flat_map<counted_key_type, counted_value_type>*
test_counted_flat_map = {};
boost::unordered::concurrent_node_map<counted_key_type, counted_value_type>*
test_counted_node_map = {};
} // namespace
@@ -462,7 +478,7 @@ namespace {
UNORDERED_TEST(
emplace_map_key_value,
((test_counted_flat_map))
((test_counted_flat_map)(test_counted_node_map))
((copy)(move))
((counted_key_checker)(converting_key_checker))
((counted_value_checker)(converting_value_checker))
+35 -13
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
test::seed_t initialize_seed{1634048962};
@@ -20,28 +22,38 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
namespace {
UNORDERED_AUTO_TEST (simple_map_equality) {
using allocator_type = map_type::allocator_type;
template <class X>
void simple_map_equality(X*)
{
using allocator_type = typename X::allocator_type;
{
map_type x1(
X x1(
{{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3));
map_type x2(
X x2(
{{1, 11}, {2, 22}}, 0, hasher(2), key_equal(2), allocator_type(3));
map_type x3(
X x3(
{{1, 11}, {2, 23}}, 0, hasher(2), key_equal(2), allocator_type(3));
map_type x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
X x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
BOOST_TEST_EQ(x1.size(), x2.size());
BOOST_TEST(x1 == x2);
@@ -57,17 +69,19 @@ namespace {
}
}
UNORDERED_AUTO_TEST (simple_set_equality) {
using allocator_type = set_type::allocator_type;
template <class X>
void simple_set_equality(X*)
{
using allocator_type = typename X::allocator_type;
{
set_type x1(
X x1(
{1, 2}, 0, hasher(1), key_equal(2), allocator_type(3));
set_type x2(
X x2(
{1, 2}, 0, hasher(2), key_equal(2), allocator_type(3));
set_type x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
X x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
BOOST_TEST_EQ(x1.size(), x2.size());
BOOST_TEST(x1 == x2);
@@ -165,9 +179,17 @@ namespace {
} // namespace
// clang-format off
UNORDERED_TEST(
simple_map_equality,
((test_map)(test_node_map)))
UNORDERED_TEST(
simple_set_equality,
((test_set)(test_node_set)))
UNORDERED_TEST(
insert_and_compare,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+11 -3
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -439,11 +441,17 @@ namespace {
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_node_map<raii, raii>* node_map;
boost::unordered::concurrent_flat_set<raii>* set;
boost::unordered::concurrent_node_set<raii>* node_set;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transparent_map;
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
transp_key_equal>* transparent_node_map;
boost::unordered::concurrent_flat_set<raii, transp_hash,
transp_key_equal>* transparent_set;
boost::unordered::concurrent_node_set<raii, transp_hash,
transp_key_equal>* transparent_node_set;
} // namespace
@@ -454,14 +462,14 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map)(set))
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
erase,
((transparent_map)(transparent_set))
((transparent_map)(transparent_node_map)(transparent_set)(transparent_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
+17 -4
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
@@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
@@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
namespace {
test::seed_t initialize_seed(1794114520);
@@ -206,19 +218,20 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
copy_assign,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_assign,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
intializer_list_assign,
((test_map_and_init_list)(test_set_and_init_list)))
((test_map_and_init_list)(test_node_map_and_init_list)
(test_set_and_init_list)(test_node_set_and_init_list)))
// clang-format on
RUN_TESTS()
+19 -6
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
@@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
@@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
namespace {
test::seed_t initialize_seed(795610904);
@@ -339,29 +351,30 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
bucket_constructor,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
iterator_range,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
move_constructor,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
initializer_list_bucket_count,
((test_map_and_init_list)(test_set_and_init_list)))
((test_map_and_init_list)(test_node_map_and_init_list)
(test_set_and_init_list)(test_node_set_and_init_list)))
// clang-format on
RUN_TESTS()
+8 -2
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -283,8 +285,12 @@ namespace {
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
boost::unordered::concurrent_node_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* node_map;
boost::unordered::concurrent_flat_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* set;
boost::unordered::concurrent_node_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* node_set;
} // namespace
@@ -295,7 +301,7 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map)(set))
((map)(node_map)(set)(node_set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if))
+36 -9
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -153,7 +155,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_assign_copy_assign;
@@ -198,7 +208,7 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_insert_or_assign_copy_assign;
@@ -249,8 +259,15 @@ namespace {
BOOST_TEST_GT(num_inserts, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_cvisit;
@@ -288,8 +305,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_visit;
@@ -426,8 +449,12 @@ namespace {
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
boost::unordered::concurrent_node_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* node_map;
boost::unordered::concurrent_flat_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* set;
boost::unordered::concurrent_node_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* node_set;
} // namespace
@@ -438,7 +465,7 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert,
((map)(set))
((map)(node_map)(set)(node_set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
@@ -450,7 +477,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((map)(node_map))
((exception_init_type_generator_factory))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
+12 -2
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -16,11 +18,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
namespace {
test::seed_t initialize_seed(223333016);
@@ -79,7 +89,7 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
merge,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
+2 -2
View File
@@ -1,6 +1,6 @@
// Copyright 2024 Joaquin M Lopez Muoz.
// Copyright 2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_UNORDERED_CFOA_TESTS
#include "../unordered/explicit_alloc_ctor_tests.cpp"
+162
View File
@@ -0,0 +1,162 @@
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "helpers.hpp"
#include <boost/assert.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
node_map_type* test_node_map;
node_set_type* test_node_set;
namespace {
template <class X, class GF>
void extract_insert_tests(X*, GF gen_factory)
{
using value_type = typename X::value_type;
using allocator_type = typename X::allocator_type;
// set visit is always const access
using arg_visit_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
test::random_generator rg = test::sequential;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
X in(0,hasher(1),key_equal(2), allocator_type(3));
std::vector<X> out(2,in);
for(std::size_t i = 0; i < values.size(); ++i) {
in.insert(values[i]);
out[i % 3 == 0? 0 : 1].insert(values[i]);
}
raii::reset_counts();
thread_runner(values, [&](boost::span<value_type> s) {
std::size_t br1 = 0, br2 = 0, br3 = 0;
for(auto const& v: s) {
typename X::node_type nh;
while (nh.empty()) {
switch (br1++ % 3) {
case 0:
nh = in.extract(test::get_key<X>(v));
BOOST_ASSERT(!nh.empty());
break;
case 1:
nh = in.extract_if(
test::get_key<X>(v), [&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
return false;
});
BOOST_ASSERT(nh.empty());
break;
case 2: default:
nh = in.extract_if(
test::get_key<X>(v), [&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
return true;
});
BOOST_ASSERT(!nh.empty());
break;
}
}
BOOST_ASSERT(nh.get_allocator() == in.get_allocator());
while (!nh.empty()) {
auto& o = out[br2++ % out.size()];
typename X::insert_return_type r;
switch (br3++ % 3) {
case 0:
r = o.insert(std::move(nh));
break;
case 1:
r = o.insert_or_visit(
std::move(nh), [&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 2: default:
r = o.insert_or_cvisit(
std::move(nh), [&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
}
BOOST_ASSERT(r.inserted || !r.node.empty());
nh = std::move(r.node);
}
}
});
BOOST_TEST_EQ(in.size(), 0u);
BOOST_TEST_EQ(out[0].size() + out[1].size(), 2 * values.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::destructor, 0u);
}
template <class X>
void insert_empty_node_tests(X*)
{
using value_type = typename X::value_type;
using node_type = typename X::node_type ;
X x;
{
node_type nh;
auto r = x.insert(std::move(nh));
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_or_visit(std::move(nh), [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_or_cvisit(std::move(nh), [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
}
} // namespace
// clang-format off
UNORDERED_TEST(
extract_insert_tests,
((test_node_map)(test_node_set))
((value_type_generator_factory)))
UNORDERED_TEST(
insert_empty_node_tests,
((test_node_map)(test_node_set)))
// clang-format on
RUN_TESTS()
+56 -6
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/config/workaround.hpp>
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/concurrent_node_map_fwd.hpp>
#include <boost/unordered/concurrent_node_set_fwd.hpp>
#include <limits>
test::seed_t initialize_seed{32304628};
@@ -36,6 +38,27 @@ bool unequal_call(boost::unordered::concurrent_flat_map<T, T>& x1,
return x1 != x2;
}
template <class T>
void swap_call(boost::unordered::concurrent_node_map<T, T>& x1,
boost::unordered::concurrent_node_map<T, T>& x2)
{
swap(x1, x2);
}
template <class T>
bool equal_call(boost::unordered::concurrent_node_map<T, T>& x1,
boost::unordered::concurrent_node_map<T, T>& x2)
{
return x1 == x2;
}
template <class T>
bool unequal_call(boost::unordered::concurrent_node_map<T, T>& x1,
boost::unordered::concurrent_node_map<T, T>& x2)
{
return x1 != x2;
}
template <class T>
void swap_call(boost::unordered::concurrent_flat_set<T>& x1,
boost::unordered::concurrent_flat_set<T>& x2)
@@ -57,14 +80,41 @@ bool unequal_call(boost::unordered::concurrent_flat_set<T>& x1,
return x1 != x2;
}
template <class T>
void swap_call(boost::unordered::concurrent_node_set<T>& x1,
boost::unordered::concurrent_node_set<T>& x2)
{
swap(x1, x2);
}
template <class T>
bool equal_call(boost::unordered::concurrent_node_set<T>& x1,
boost::unordered::concurrent_node_set<T>& x2)
{
return x1 == x2;
}
template <class T>
bool unequal_call(boost::unordered::concurrent_node_set<T>& x1,
boost::unordered::concurrent_node_set<T>& x2)
{
return x1 != x2;
}
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
using map_type = boost::unordered::concurrent_flat_map<int, int>;
using set_type = boost::unordered::concurrent_flat_map<int, int>;
using node_map_type = boost::unordered::concurrent_node_map<int, int>;
using set_type = boost::unordered::concurrent_flat_set<int>;
using node_set_type = boost::unordered::concurrent_node_set<int>;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
template <typename X>
void fwd_swap_call(X*)
@@ -106,19 +156,19 @@ void max_size(X*)
// clang-format off
UNORDERED_TEST(
fwd_swap_call,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
fwd_equal_call,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
fwd_unequal_call,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
max_size,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
// clang-format on
RUN_TESTS()
+21 -2
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Copyright (C) 2024 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)
@@ -147,9 +147,15 @@ public:
cfoa_ptr(std::nullptr_t) : p_(nullptr){};
template <class U> using rebind = cfoa_ptr<U>;
operator bool() const { return !!p_; }
template <typename Q = T>
Q& operator*() const noexcept { return *p_; }
T* operator->() const noexcept { return p_; }
static cfoa_ptr<T> pointer_to(element_type& r) { return {std::addressof(r)}; }
template<typename Q = T>
static cfoa_ptr<Q> pointer_to(Q& r) { return {std::addressof(r)}; }
};
template <class T> struct stateful_allocator
@@ -670,4 +676,17 @@ public:
fancy_allocator& operator=(fancy_allocator const&) { return *this; }
};
namespace boost {
template <> struct pointer_traits<void_ptr>
{
template <class U> struct rebind_to
{
typedef ptr<U> type;
};
template<class U>
using rebind=typename rebind_to<U>::type;
};
} // namespace boost
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
+100 -21
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,6 +8,8 @@
#include <boost/config.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -179,8 +181,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -198,7 +207,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
else{
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
@@ -216,7 +232,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
else{
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
}
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -234,7 +257,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
else{
BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); // rehashing
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
@@ -260,7 +290,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GT(raii::move_constructor, 0u); // rehashing
}
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -284,7 +321,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 2 * x.size()); // rehashing
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
else{
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
@@ -319,8 +363,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_cvisit;
@@ -360,8 +411,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_visit;
@@ -665,12 +723,14 @@ namespace {
}
}
UNORDERED_AUTO_TEST (insert_sfinae_test) {
template <class X>
void insert_map_sfinae_test(X*)
{
// mostly a compile-time tests to ensure that there's no ambiguity when a
// user does this
using value_type =
typename boost::unordered::concurrent_flat_map<raii, raii>::value_type;
boost::unordered::concurrent_flat_map<raii, raii> x;
using value_type = typename X::value_type;
X x;
x.insert({1, 2});
x.insert_or_visit({2, 3}, [](value_type&) {});
@@ -684,11 +744,23 @@ namespace {
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
fancy_map;
boost::unordered::concurrent_node_map<raii, raii>* node_map;
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
transp_key_equal>* trans_node_map;
boost::unordered::concurrent_node_map<raii, raii, boost::hash<raii>,
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
fancy_node_map;
boost::unordered::concurrent_flat_set<raii>* set;
boost::unordered::concurrent_flat_set<raii, boost::hash<raii>,
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
std::equal_to<raii>, fancy_allocator<raii> >*
fancy_set;
boost::unordered::concurrent_node_set<raii>* node_set;
boost::unordered::concurrent_node_set<raii, boost::hash<raii>,
std::equal_to<raii>, fancy_allocator<raii> >*
fancy_node_set;
std::initializer_list<std::pair<raii const, raii> > map_init_list{
{raii{0}, raii{0}},
{raii{1}, raii{1}},
@@ -740,7 +812,9 @@ namespace {
};
auto map_and_init_list=std::make_pair(map,map_init_list);
auto node_map_and_init_list=std::make_pair(node_map,map_init_list);
auto set_and_init_list=std::make_pair(set,set_init_list);
auto node_set_and_init_list=std::make_pair(node_set,set_init_list);
} // namespace
@@ -751,11 +825,12 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map_and_init_list)(set_and_init_list)))
((map_and_init_list)(node_map_and_init_list)(set_and_init_list)))
UNORDERED_TEST(
insert,
((map)(fancy_map)(set)(fancy_set))
((map)(fancy_map)(node_map)(fancy_node_map)
(set)(fancy_set)(node_set)(fancy_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
@@ -766,7 +841,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((map)(node_map))
((init_type_generator_factory))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
@@ -774,10 +849,14 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((trans_map))
((trans_map)(trans_node_map))
((init_type_generator_factory))
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert_map_sfinae_test,
((map)(node_map)))
// clang-format on
RUN_TESTS()
+45 -10
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
test::seed_t initialize_seed{402031699};
@@ -23,19 +25,38 @@ using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
std::hash<raii>, std::equal_to<raii>,
stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii,
hasher, key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map2_type = boost::unordered::concurrent_node_map<raii, raii,
std::hash<raii>, std::equal_to<raii>,
stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using set2_type = boost::unordered::concurrent_flat_set<raii, std::hash<raii>,
std::equal_to<raii>, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set2_type = boost::unordered::concurrent_node_set<raii, std::hash<raii>,
std::equal_to<raii>, stateful_allocator<raii> >;
map_type* test_map;
map2_type* test_map2;
auto test_maps=std::make_pair(test_map,test_map2);
node_map_type* test_node_map;
node_map2_type* test_node_map2;
auto test_node_maps=std::make_pair(test_node_map,test_node_map2);
set_type* test_set;
set2_type* test_set2;
auto test_sets=std::make_pair(test_set,test_set2);
node_set_type* test_node_set;
node_set2_type* test_node_set2;
auto test_node_sets=std::make_pair(test_node_set,test_node_set2);
struct
{
template <class X1, class X2>
@@ -91,9 +112,16 @@ namespace {
});
BOOST_TEST_EQ(raii::copy_constructor, old_cc + expected_copies);
BOOST_TEST_EQ(
raii::move_constructor,
value_type_cardinality * reference_cont.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_EQ(
raii::move_constructor,
value_type_cardinality * reference_cont.size());
}
BOOST_TEST_EQ(+num_merged, reference_cont.size());
test_fuzzy_matches_reference(x, reference_cont, rg);
@@ -210,10 +238,17 @@ namespace {
t3.join();
if (num_merges > 0) {
// num merges is 0 most commonly in the cast of the limited_range
// generator as both maps will contains keys from 0 to 99
BOOST_TEST_EQ(
+raii::move_constructor, value_type_cardinality * num_merges);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// num merges is 0 most commonly in the cast of the limited_range
// generator as both maps will contains keys from 0 to 99
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * num_merges);
}
BOOST_TEST_GE(call_count, 1u);
}
@@ -229,14 +264,14 @@ namespace {
// clang-format off
UNORDERED_TEST(
merge_tests,
((test_maps)(test_sets))
((test_maps)(test_node_maps)(test_sets)(test_node_sets))
((lvalue_merge)(rvalue_merge))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert_and_merge_tests,
((test_maps)(test_sets))
((test_maps)(test_node_maps)(test_sets)(test_node_sets))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
@@ -0,0 +1,6 @@
// Copyright 2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_UNORDERED_CFOA_TESTS
#include "../unordered/node_handle_allocator_tests.cpp"
+2
View File
@@ -5,4 +5,6 @@
#define BOOST_UNORDERED_CFOA_TESTS
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include "../unordered/pmr_allocator_tests.cpp"
+8 -2
View File
@@ -1,4 +1,4 @@
// Copyright 2023 Joaquin M Lopez Munoz
// Copyright 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -32,15 +32,21 @@ namespace boost {
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/core/lightweight_test.hpp>
using test::default_generator;
using map_type = boost::unordered::concurrent_flat_map<raii, raii>;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii>;
using set_type = boost::unordered::concurrent_flat_set<raii>;
using node_set_type = boost::unordered::concurrent_node_set<raii>;
map_type* test_map;
map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
template<typename F>
void detect_reentrancy(F f)
@@ -105,7 +111,7 @@ namespace {
// clang-format off
UNORDERED_TEST(
reentrancy_tests,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)))
// clang-format on
+14 -4
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
using test::default_generator;
using test::limited_range;
@@ -18,11 +20,19 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
node_map_type* test_node_map;
set_type* test_set;
node_set_type* test_node_set;
namespace {
test::seed_t initialize_seed{748775921};
@@ -187,15 +197,15 @@ namespace {
// clang-format off
UNORDERED_TEST(
rehash_no_insert,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
reserve_no_insert,
((test_map)(test_set)))
((test_map)(test_node_map)(test_set)(test_node_set)))
UNORDERED_TEST(
insert_and_erase_with_rehash,
((test_map)(test_set))
((test_map)(test_node_map)(test_set)(test_node_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+8 -2
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -12,6 +12,8 @@
#include <boost/serialization/nvp.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
namespace {
@@ -70,11 +72,15 @@ namespace {
boost::concurrent_flat_map<
test::object, test::object, test::hash, test::equal_to>* test_flat_map;
boost::concurrent_node_map<
test::object, test::object, test::hash, test::equal_to>* test_node_map;
boost::concurrent_flat_set<
test::object, test::hash, test::equal_to>* test_flat_set;
boost::concurrent_node_set<
test::object, test::hash, test::equal_to>* test_node_set;
UNORDERED_TEST(serialization_tests,
((test_flat_map)(test_flat_set))
((test_flat_map)(test_node_map)(test_flat_set)(test_node_set))
((text_archive)(xml_archive))
((default_generator)))
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2024 Joaquin M Lopez Muoz.
// Copyright 2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+18 -3
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,8 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
test::seed_t initialize_seed{996130204};
@@ -62,9 +64,15 @@ using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
template <class T> struct is_nothrow_member_swappable
{
static bool const value =
@@ -293,21 +301,28 @@ namespace {
map_type* map;
replace_allocator<map_type, pocs_allocator>* pocs_map;
node_map_type* node_map;
replace_allocator<node_map_type, pocs_allocator>* pocs_node_map;
set_type* set;
replace_allocator<set_type, pocs_allocator>* pocs_set;
node_set_type* node_set;
replace_allocator<node_set_type, pocs_allocator>* pocs_node_set;
} // namespace
// clang-format off
UNORDERED_TEST(
swap_tests,
((map)(pocs_map)(set)(pocs_set))
((map)(pocs_map)(node_map)(pocs_node_map)
(set)(pocs_set)(node_set)(pocs_node_set))
((member_fn_swap)(free_fn_swap))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_swap,
((map)(set))
((map)(node_map)(set)(node_set))
((member_fn_swap)(free_fn_swap))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
+38 -8
View File
@@ -1,10 +1,12 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -161,8 +163,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
@@ -194,8 +203,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
@@ -229,7 +245,12 @@ namespace {
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
@@ -264,7 +285,12 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
@@ -367,6 +393,10 @@ namespace {
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transp_map;
boost::unordered::concurrent_node_map<raii, raii>* node_map;
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
transp_key_equal>* transp_node_map;
} // namespace
using test::default_generator;
@@ -379,7 +409,7 @@ value_generator<std::pair<raii, raii> > init_type_generator;
// clang-format off
UNORDERED_TEST(
try_emplace,
((map))
((map)(node_map))
((value_type_generator)(init_type_generator))
((lvalue_try_emplacer)(norehash_lvalue_try_emplacer)
(rvalue_try_emplacer)(norehash_rvalue_try_emplacer)
@@ -389,7 +419,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
try_emplace,
((transp_map))
((transp_map)(transp_node_map))
((init_type_generator))
((transp_try_emplace)(norehash_transp_try_emplace)
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
+170 -7
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 Joaquin M Lopez Munoz
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -17,7 +17,10 @@
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/compat/latch.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/iterator/transform_iterator.hpp>
@@ -27,7 +30,10 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <functional>
#include <iterator>
#include <thread>
#include <vector>
namespace {
@@ -967,9 +973,158 @@ namespace {
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transp_map;
boost::unordered::concurrent_node_map<raii, raii>* node_map;
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
transp_key_equal>* transp_node_map;
boost::unordered::concurrent_flat_set<raii>* set;
boost::unordered::concurrent_flat_set<raii, transp_hash,
transp_key_equal>* transp_set;
boost::unordered::concurrent_node_set<raii>* node_set;
boost::unordered::concurrent_node_set<raii, transp_hash,
transp_key_equal>* transp_node_set;
struct mutable_pair
{
mutable_pair(int first_ = 0, int second_ = 0):
first{first_}, second{second_} {}
int first = 0;
mutable int second = 0;
};
struct null_mutable_pair
{
operator mutable_pair() const { return {0,0}; }
};
struct mutable_pair_hash
{
using is_transparent = void;
std::size_t operator()(const mutable_pair& x) const
{
return boost::hash<int>{}(x.first);
}
std::size_t operator()(const null_mutable_pair&) const
{
return boost::hash<int>{}(0);
}
};
struct mutable_pair_equal_to
{
using is_transparent = void;
bool operator()(const mutable_pair& x, const mutable_pair& y) const
{
return x.first == y.first;
}
bool operator()(const null_mutable_pair&, const mutable_pair& y) const
{
return 0 == y.first;
}
bool operator()(const mutable_pair& x, const null_mutable_pair&) const
{
return x.first == 0;
}
};
template<typename F>
void exclusive_access_for(F f)
{
std::atomic_int num_started{0};
std::atomic_int in_visit{0};
boost::compat::latch finish{1};
auto bound_f = [&] {
++num_started;
f([&] (const mutable_pair& x) {
++in_visit;
++x.second;
finish.wait();
--in_visit;
return true;
});
};
std::thread t1{bound_f}, t2{bound_f};
while(num_started != 2) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
BOOST_TEST(in_visit <= 1);
finish.count_down();
t1.join();
t2.join();
}
template<class X>
void exclusive_access_set_visit(X*)
{
using visit_function = std::function<void (const mutable_pair&)>;
using returning_visit_function = std::function<bool (const mutable_pair&)>;
X x;
x.insert({0, 0});
exclusive_access_for([&](visit_function f) {
x.visit({0, 0}, f);
});
exclusive_access_for([&](visit_function f) {
x.visit(null_mutable_pair{}, f);
});
exclusive_access_for([&](visit_function f) {
mutable_pair a[] = {{0, 0}};
x.visit(std::begin(a), std::end(a), f);
});
exclusive_access_for([&](visit_function f) {
x.visit_all(f);
});
exclusive_access_for([&](returning_visit_function f) {
x.visit_while(f);
});
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
exclusive_access_for([&](visit_function f) {
x.visit_all(std::execution::par, f);
});
exclusive_access_for([&](returning_visit_function f) {
x.visit_while(std::execution::par, f);
});
#endif
exclusive_access_for([&](visit_function f) {
const mutable_pair p;
x.insert_or_visit(p, f);
});
exclusive_access_for([&](visit_function f) {
x.insert_or_visit({0,0}, f);
});
exclusive_access_for([&](visit_function f) {
x.insert_or_visit(null_mutable_pair{}, f);
});
exclusive_access_for([&](visit_function f) {
mutable_pair a[] = {{0, 0}};
x.insert_or_visit(std::begin(a), std::end(a), f);
});
exclusive_access_for([&](visit_function f) {
std::initializer_list<mutable_pair> il = {{0, 0}};
x.insert_or_visit(il, f);
});
exclusive_access_for([&](visit_function f) {
x.emplace_or_visit(0, 0, f);
});
}
boost::concurrent_flat_set<
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_set;
boost::concurrent_node_set<
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_node_set;
} // namespace
@@ -981,7 +1136,7 @@ using test::sequential;
UNORDERED_TEST(
visit,
((map)(set))
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)
(exec_policy_visit_while))
@@ -989,28 +1144,29 @@ UNORDERED_TEST(
UNORDERED_TEST(
visit,
((transp_map)(transp_set))
((transp_map)(transp_node_map)(transp_set)(transp_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((transp_visitor))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
empty_visit,
((map)(transp_map)(set)(transp_set))
((map)(transp_map)(node_map)(transp_node_map)
(set)(transp_set)(node_set)(transp_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((default_generator)(sequential)(limited_range))
)
UNORDERED_TEST(
insert_and_visit,
((map)(set))
((map)(node_map)(set)(node_set))
((value_type_generator_factory))
((sequential))
)
UNORDERED_TEST(
bulk_visit,
((map)(set))
((map)(node_map)(set)(node_set))
((regular_key_extract))
((value_type_generator_factory))
((sequential))
@@ -1018,12 +1174,19 @@ UNORDERED_TEST(
UNORDERED_TEST(
bulk_visit,
((transp_map)(transp_set))
((transp_map)(transp_node_map)(transp_set)(transp_node_set))
((transp_key_extract))
((value_type_generator_factory))
((sequential))
)
// https://github.com/boostorg/unordered/issues/260
UNORDERED_TEST(
exclusive_access_set_visit,
((mutable_set)(mutable_node_set))
)
// clang-format on
RUN_TESTS()
+199
View File
@@ -0,0 +1,199 @@
// Copyright 2024 Braden Ganetsky
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// This is a file for testing of visualizations,
// such as Visual Studio Natvis or GDB pretty printers.
// Run this test and break at the label called `break_here`.
// Inspect the variables to test correctness.
#if 0 // Change to `#if 1` to test turning off SIMD optimizations
#define BOOST_UNORDERED_DISABLE_SSE2
#define BOOST_UNORDERED_DISABLE_NEON
#endif
#define BOOST_UNORDERED_ENABLE_STATS
#include <boost/core/lightweight_test.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <boost/unordered/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_node_set.hpp>
#include <boost/unordered/unordered_set.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <memory>
#include <string>
#include <typeinfo>
// Prevent any "unused" errors
template <class... Args> void use(Args&&...) {}
using map_value_type = std::pair<const std::string, int>;
using set_value_type = std::string;
template <class Tester> void visualization_test(Tester& tester)
{
// clang-format off
auto fca_map_ptr = tester.template construct_map<boost::unordered_map>();
auto fca_multimap_ptr = tester.template construct_map<boost::unordered_multimap>();
auto fca_set_ptr = tester.template construct_set<boost::unordered_set>();
auto fca_multiset_ptr = tester.template construct_set<boost::unordered_multiset>();
auto& fca_map = *fca_map_ptr;
auto& fca_multimap = *fca_multimap_ptr;
auto& fca_set = *fca_set_ptr;
auto& fca_multiset = *fca_multiset_ptr;
auto foa_flat_map_ptr = tester.template construct_map<boost::unordered_flat_map>();
auto foa_flat_set_ptr = tester.template construct_set<boost::unordered_flat_set>();
auto foa_node_map_ptr = tester.template construct_map<boost::unordered_node_map>();
auto foa_node_set_ptr = tester.template construct_set<boost::unordered_node_set>();
auto& foa_flat_map = *foa_flat_map_ptr;
auto& foa_flat_set = *foa_flat_set_ptr;
auto& foa_node_map = *foa_node_map_ptr;
auto& foa_node_set = *foa_node_set_ptr;
auto cfoa_flat_map_ptr = tester.template construct_map<boost::concurrent_flat_map>();
auto cfoa_flat_set_ptr = tester.template construct_set<boost::concurrent_flat_set>();
auto cfoa_node_map_ptr = tester.template construct_map<boost::concurrent_node_map>();
auto cfoa_node_set_ptr = tester.template construct_set<boost::concurrent_node_set>();
auto& cfoa_flat_map = *cfoa_flat_map_ptr;
auto& cfoa_flat_set = *cfoa_flat_set_ptr;
auto& cfoa_node_map = *cfoa_node_map_ptr;
auto& cfoa_node_set = *cfoa_node_set_ptr;
// clang-format on
for (int i = 0; i < 5; ++i) {
const auto str = std::to_string(i * 2);
const auto num = i * 11;
fca_map.emplace(str, num);
fca_multimap.emplace(str, num);
fca_multimap.emplace(str, num + 1);
foa_flat_map.emplace(str, num);
foa_node_map.emplace(str, num);
cfoa_flat_map.emplace(str, num);
cfoa_node_map.emplace(str, num);
fca_set.emplace(str);
fca_multiset.emplace(str);
fca_multiset.emplace(str);
foa_flat_set.emplace(str);
foa_node_set.emplace(str);
cfoa_flat_set.emplace(str);
cfoa_node_set.emplace(str);
}
auto fca_map_begin = fca_map.begin();
auto fca_map_end = fca_map.end();
auto fca_multimap_begin = fca_multimap.begin();
auto fca_multimap_end = fca_multimap.end();
auto fca_set_begin = fca_set.begin();
auto fca_set_end = fca_set.end();
auto fca_multiset_begin = fca_multiset.begin();
auto fca_multiset_end = fca_multiset.end();
auto foa_flat_map_begin = foa_flat_map.begin();
auto foa_flat_map_end = foa_flat_map.end();
auto foa_flat_set_begin = foa_flat_set.begin();
auto foa_flat_set_end = foa_flat_set.end();
auto foa_node_map_begin = foa_node_map.begin();
auto foa_node_map_end = foa_node_map.end();
auto foa_node_set_begin = foa_node_set.begin();
auto foa_node_set_end = foa_node_set.end();
use(cfoa_flat_map, cfoa_flat_set, cfoa_node_map, cfoa_node_set);
use(fca_map_begin, fca_map_end, fca_multimap_begin, fca_multimap_end,
fca_set_begin, fca_set_end, fca_multiset_begin, fca_multiset_end);
use(foa_flat_map_begin, foa_flat_map_end, foa_flat_set_begin,
foa_flat_set_end, foa_node_map_begin, foa_node_map_end, foa_node_set_begin,
foa_node_set_end);
goto break_here;
break_here:;
}
class offset_ptr_tester_
{
static constexpr std::size_t SEGMENT_SIZE = 64 * 1024;
std::string segment_name = to_string(boost::uuids::random_generator()());
boost::interprocess::managed_shared_memory segment{
boost::interprocess::create_only, segment_name.c_str(), SEGMENT_SIZE};
using map_allocator = boost::interprocess::allocator<map_value_type,
boost::interprocess::managed_shared_memory::segment_manager>;
using set_allocator = boost::interprocess::allocator<set_value_type,
boost::interprocess::managed_shared_memory::segment_manager>;
template <template <class...> class MapTemplate>
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
std::equal_to<std::string>, map_allocator>;
template <template <class...> class SetTemplate>
using set_type = SetTemplate<std::string, boost::hash<std::string>,
std::equal_to<std::string>, set_allocator>;
public:
offset_ptr_tester_()
{
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
}
~offset_ptr_tester_()
{
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
}
template <template <class...> class MapTemplate>
map_type<MapTemplate>* construct_map()
{
return &*segment.construct<map_type<MapTemplate> >(
typeid(map_type<MapTemplate>).name())(
map_allocator(segment.get_segment_manager()));
}
template <template <class...> class SetTemplate>
set_type<SetTemplate>* construct_set()
{
return &*segment.construct<set_type<SetTemplate> >(
typeid(set_type<SetTemplate>).name())(
set_allocator(segment.get_segment_manager()));
}
} offset_ptr_tester;
class default_tester_
{
template <template <class...> class MapTemplate>
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
std::equal_to<std::string>, std::allocator<map_value_type> >;
template <template <class...> class SetTemplate>
using set_type = SetTemplate<std::string, boost::hash<std::string>,
std::equal_to<std::string>, std::allocator<set_value_type> >;
public:
template <template <class...> class MapTemplate>
std::unique_ptr<map_type<MapTemplate> > construct_map()
{
using type = map_type<MapTemplate>;
return std::unique_ptr<type>(new type());
}
template <template <class...> class SetTemplate>
std::unique_ptr<set_type<SetTemplate> > construct_set()
{
using type = set_type<SetTemplate>;
return std::unique_ptr<type>(new type());
}
} default_tester;
int main()
{
visualization_test(default_tester);
visualization_test(offset_ptr_tester);
return boost::report_errors();
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2024 Joaquin M Lopez Munz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR
#define BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR
#include <boost/core/allocator_access.hpp>
#include <utility>
namespace test {
template <typename Container, typename Allocator>
struct replace_allocator_impl;
template <typename Container, typename Allocator>
using replace_allocator =
typename replace_allocator_impl<Container, Allocator>::type;
template <
typename K, typename H, typename P, typename A,
template <typename, typename, typename, typename> class Set,
typename Allocator
>
struct replace_allocator_impl<Set<K, H, P, A>, Allocator>
{
using type = Set<
K, H, P, boost::allocator_rebind_t<Allocator, K> >;
};
template <
typename K, typename H, typename T, typename P, typename A,
template <typename, typename, typename, typename, typename> class Map,
typename Allocator
>
struct replace_allocator_impl<Map<K, T, H, P, A>, Allocator>
{
using type = Map<
K, T, H, P,
boost::allocator_rebind_t<Allocator, std::pair<K const, T> > >;
};
} // namespace test
#endif // !defined(BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR)
@@ -0,0 +1,46 @@
// Copyright 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#if !defined(BOOST_UNORDERED_TEST_NON_DEFAULT_CTBLE_ALLOCATOR_HEADER)
#define BOOST_UNORDERED_TEST_NON_DEFAULT_CTBLE_ALLOCATOR_HEADER
#include <memory>
namespace test
{
template <class T> struct non_default_ctble_allocator
{
typedef T value_type;
non_default_ctble_allocator(int) {}
template <class U>
non_default_ctble_allocator(const non_default_ctble_allocator<U>&) {}
template<class U>
bool operator==(non_default_ctble_allocator<U> const &) const noexcept
{
return true;
}
template<class U>
bool operator!=(non_default_ctble_allocator<U> const &) const noexcept
{
return false;
}
T* allocate(std::size_t n)
{
return std::allocator<T>().allocate(n);
}
void deallocate(T* p, std::size_t n)
{
std::allocator<T>().deallocate(p, n);
}
};
}
#endif
+25
View File
@@ -7,12 +7,16 @@
#include "../helpers/unordered.hpp"
#include "../helpers/test.hpp"
#include "../helpers/replace_allocator.hpp"
#include "../objects/test.hpp"
#include "../objects/cxx11_allocator.hpp"
#include "../objects/non_default_ctble_allocator.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/tracker.hpp"
#include "../helpers/equivalent.hpp"
#include <vector>
#if defined(BOOST_MSVC)
#pragma warning(disable : 4127) // conditional expression is constant
#endif
@@ -288,6 +292,27 @@ namespace assign_tests {
}
#endif
}
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "gh276\n";
{
// https://github.com/boostorg/unordered/issues/276
using value_type = typename T::value_type;
using replaced_allocator_container = test::replace_allocator<
T, test::non_default_ctble_allocator<int> >;
using replaced_allocator_type =
typename replaced_allocator_container::allocator_type;
test::random_values<T> v(4, generator);
std::vector<value_type> vv(v.begin(), v.end());
replaced_allocator_container
x(replaced_allocator_type(0)),
y(vv.begin(), vv.begin() + 4, replaced_allocator_type(0));
x = {vv[0], vv[1], vv[2], vv[3]};
BOOST_TEST(x == y);
}
}
using test::default_generator;
+24 -8
View File
@@ -52,23 +52,39 @@ template class instantiate_node_map<test::minimal::assignable const,
test::minimal::allocator<int> >;
#else
#define INSTANTIATE(type) \
template class boost::unordered::detail::instantiate_##type
INSTANTIATE(map)<int, int, boost::hash<int>, std::equal_to<int>,
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_map
{
typedef boost::unordered_map<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_multimap
{
typedef boost::unordered_multimap<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
};
template class instantiate_map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::allocator<int> >;
INSTANTIATE(multimap)<int const, int const, boost::hash<int>,
template class instantiate_multimap<int const, int const, boost::hash<int>,
std::equal_to<int>, test::minimal::allocator<int> >;
INSTANTIATE(
map)<test::minimal::assignable const, test::minimal::default_assignable const,
template class instantiate_map<test::minimal::assignable const,
test::minimal::default_assignable const,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
INSTANTIATE(multimap)<test::minimal::assignable, test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
template class instantiate_multimap<test::minimal::assignable,
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
#endif
template <template <class Key, class T, class H = boost::hash<Key>,
+21 -8
View File
@@ -52,19 +52,32 @@ template class instantiate_node_set<test::minimal::assignable const,
#else
#define INSTANTIATE(type) \
template class boost::unordered::detail::instantiate_##type
template <typename T, typename H, typename P, typename A> class instantiate_set
{
typedef boost::unordered_set<T, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
INSTANTIATE(set)<int, boost::hash<int>, std::equal_to<int>,
test::minimal::allocator<int> >;
INSTANTIATE(multiset)<int const, boost::hash<int>, std::equal_to<int>,
test::minimal::allocator<int> >;
template <typename T, typename H, typename P, typename A>
class instantiate_multiset
{
typedef boost::unordered_multiset<T, H, P, A> container;
container x;
typename container::node_type node_type;
};
INSTANTIATE(set)<test::minimal::assignable const,
template class instantiate_set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::allocator<int> >;
template class instantiate_multiset<int const, boost::hash<int>,
std::equal_to<int>, test::minimal::allocator<int> >;
template class instantiate_set<test::minimal::assignable const,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
INSTANTIATE(multiset)<test::minimal::assignable,
template class instantiate_multiset<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
+19 -2
View File
@@ -1,14 +1,17 @@
// Copyright 2024 Joaquin M Lopez Muoz.
// Copyright 2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifdef BOOST_UNORDERED_CFOA_TESTS
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#else
#include "../helpers/unordered.hpp"
#endif
#include "../helpers/helpers.hpp"
#include "../helpers/test.hpp"
#include <boost/type_traits/make_void.hpp>
#include <memory>
@@ -62,7 +65,16 @@ template <class Container> void test_explicit_alloc_ctor_extract(
template <class Container> void test_explicit_alloc_ctor_extract(
Container& c, std::true_type)
{
#ifdef BOOST_UNORDERED_CFOA_TESTS
typename Container::key_type k;
c.cvisit_while([&](typename Container::value_type const & x) {
k = test::get_key<Container>(x);
return false;
});
auto n = c.extract(k);
#else
auto n = c.extract(c.begin());
#endif
c.insert(std::move(n));
n = c.extract(typename Container::key_type());
c.insert(std::move(n));
@@ -122,8 +134,13 @@ UNORDERED_AUTO_TEST (explicit_alloc_ctor) {
test_explicit_alloc_ctor<boost::concurrent_flat_map<int, int,
boost::hash<int>, std::equal_to<int>,
explicit_allocator<std::pair<const int, int> > > >();
test_explicit_alloc_ctor<boost::concurrent_node_map<int, int,
boost::hash<int>, std::equal_to<int>,
explicit_allocator<std::pair<const int, int> > > >();
test_explicit_alloc_ctor<boost::concurrent_flat_set<
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
test_explicit_alloc_ctor<boost::concurrent_node_set<
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
#elif defined(BOOST_UNORDERED_FOA_TESTS)
test_explicit_alloc_ctor<boost::unordered_flat_map<int, int,
boost::hash<int>, std::equal_to<int>,
@@ -0,0 +1,184 @@
// Copyright (C) 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#if defined(BOOST_GCC)
// Spurious maybe-uninitialized warnings with allocators contained
// in node handles.
// Maybe related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108230
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#ifdef BOOST_UNORDERED_CFOA_TESTS
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#else
#include "../helpers/unordered.hpp"
#endif
#include "../helpers/test.hpp"
#include "../helpers/replace_allocator.hpp"
#include <boost/config/workaround.hpp>
#include <boost/core/allocator_access.hpp>
#include <memory>
#include <type_traits>
namespace {
template <class T> struct nonassignable_allocator
{
using value_type = T;
nonassignable_allocator() = default;
nonassignable_allocator(nonassignable_allocator const&) = default;
template <class U>
nonassignable_allocator(nonassignable_allocator<U> const&) {}
nonassignable_allocator& operator=(nonassignable_allocator const&) = delete;
T* allocate(std::size_t n)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
bool operator==(nonassignable_allocator const&) const { return true; }
bool operator!=(nonassignable_allocator const&) const { return false; }
};
template <class T> struct pocx_allocator
{
int x_;
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
pocx_allocator() : x_{-1} {}
pocx_allocator(pocx_allocator const&) = default;
pocx_allocator(int const x) : x_{x} {}
template <class U>
pocx_allocator(pocx_allocator<U> const& rhs) : x_{rhs.x_}
{
}
pocx_allocator& operator=(pocx_allocator const&) = default;
T* allocate(std::size_t n)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
bool operator==(pocx_allocator const& rhs) const { return x_ == rhs.x_; }
bool operator!=(pocx_allocator const& rhs) const { return x_ != rhs.x_; }
};
template<typename X, typename Allocator>
void node_handle_allocator_tests(
X*, std::pair<Allocator, Allocator> allocators)
{
using value_type = typename X::value_type;
using replaced_allocator_container = test::replace_allocator<X, Allocator>;
using node_type = typename replaced_allocator_container::node_type;
replaced_allocator_container x1(allocators.first);
node_type nh;
x1.emplace(value_type());
nh = x1.extract(0);
BOOST_TEST(!nh.empty());
BOOST_TEST(nh.get_allocator() == x1.get_allocator());
replaced_allocator_container x2(allocators.second);
x2.emplace(value_type());
nh = x2.extract(0);
BOOST_TEST(!nh.empty());
BOOST_TEST(nh.get_allocator() == x2.get_allocator());
}
template<typename X, typename Allocator>
void node_handle_allocator_swap_tests(
X*, std::pair<Allocator, Allocator> allocators)
{
using value_type = typename X::value_type;
using replaced_allocator_container = test::replace_allocator<X, Allocator>;
using node_type = typename replaced_allocator_container::node_type;
replaced_allocator_container x1(allocators.first), x2(allocators.second);
x1.emplace(value_type());
x2.emplace(value_type());
node_type nh1, nh2;
nh1 = x1.extract(0);
swap(nh1, nh2);
BOOST_TEST(nh1.empty());
BOOST_TEST(!nh2.empty());
BOOST_TEST(nh2.get_allocator() == x1.get_allocator());
nh1 = x2.extract(0);
swap(nh1, nh2);
BOOST_TEST(!nh1.empty());
BOOST_TEST(nh1.get_allocator() == x1.get_allocator());
BOOST_TEST(!nh2.empty());
BOOST_TEST(nh2.get_allocator() == x2.get_allocator());
}
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
#pragma warning(push)
#pragma warning(disable : 4592) // symbol will be dynamically initialized
#endif
std::pair<
std::allocator<int>, std::allocator<int> > test_std_allocators({},{});
std::pair<
nonassignable_allocator<int>,
nonassignable_allocator<int> > test_nonassignable_allocators({},{});
std::pair<
pocx_allocator<int>, pocx_allocator<int> > test_pocx_allocators(5,6);
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
#pragma warning(pop) // C4592
#endif
#if defined(BOOST_UNORDERED_FOA_TESTS)
boost::unordered_node_map<int, int>* test_map;
boost::unordered_node_set<int>* test_set;
#elif defined(BOOST_UNORDERED_CFOA_TESTS)
boost::concurrent_node_map<int, int>* test_map;
boost::concurrent_node_set<int>* test_set;
#else
boost::unordered_map<int, int>* test_map;
boost::unordered_set<int>* test_set;
#endif
} // namespace
// clang-format off
UNORDERED_TEST(
node_handle_allocator_tests,
((test_map)(test_set))
((test_std_allocators)(test_nonassignable_allocators)
(test_pocx_allocators)))
UNORDERED_TEST(
node_handle_allocator_swap_tests,
((test_map)(test_set))
((test_std_allocators)(test_pocx_allocators)))
// clang-format on
RUN_TESTS()
+12 -1
View File
@@ -33,12 +33,23 @@ namespace pmr_allocator_tests {
test_string_flat_map;
static boost::unordered::pmr::concurrent_flat_map<pmr_string, pmr_string>*
test_pmr_string_flat_map;
static boost::unordered::pmr::concurrent_node_map<std::string, std::string>*
test_string_node_map;
static boost::unordered::pmr::concurrent_node_map<pmr_string, pmr_string>*
test_pmr_string_node_map;
static boost::unordered::pmr::concurrent_flat_set<std::string>*
test_string_flat_set;
static boost::unordered::pmr::concurrent_flat_set<pmr_string>*
test_pmr_string_flat_set;
static boost::unordered::pmr::concurrent_node_set<std::string>*
test_string_node_set;
static boost::unordered::pmr::concurrent_node_set<pmr_string>*
test_pmr_string_node_set;
#define PMR_ALLOCATOR_TESTS_ARGS \
((test_string_flat_map)(test_pmr_string_flat_map)(test_string_flat_set)(test_pmr_string_flat_set))
((test_string_flat_map)(test_pmr_string_flat_map) \
(test_string_node_map)(test_pmr_string_node_map) \
(test_string_flat_set)(test_pmr_string_flat_set) \
(test_string_node_set)(test_pmr_string_node_set))
#elif defined(BOOST_UNORDERED_FOA_TESTS)
static boost::unordered::pmr::unordered_flat_map<std::string, std::string>*
test_string_flat_map;
+37 -15
View File
@@ -1,4 +1,4 @@
// Copyright 2024 Joaquin M Lopez Muoz.
// Copyright 2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,8 +7,12 @@
#ifdef BOOST_UNORDERED_CFOA_TESTS
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/unordered/concurrent_node_map.hpp>
#include <boost/unordered/concurrent_node_set.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_node_set.hpp>
#include "../cfoa/helpers.hpp"
#else
#include "../helpers/unordered.hpp"
@@ -19,6 +23,7 @@
#include "../helpers/test.hpp"
#include <boost/assert.hpp>
#include <boost/core/make_span.hpp>
#include <cmath>
#include <cstring>
template <class T> struct unequal_allocator
@@ -45,16 +50,20 @@ template <class T> struct unequal_allocator
int n_;
};
bool exact_same(double x, double y)
bool esentially_same(double x, double y)
{
return std::memcmp(
reinterpret_cast<void*>(&x), reinterpret_cast<void*>(&y),
sizeof(double))==0;
// Some optimizer-related issues in GCC X86 result in last-bit differences
// on doubles that should otherwise be identical.
// https://stackoverflow.com/a/253874/213114
static constexpr double epsilon = 1.0E-6;
return fabs(x - y) <= ( (fabs(x) > fabs(y) ? fabs(x) : fabs(y)) * epsilon);
}
bool not_exact_same(double x, double y)
bool not_esentially_same(double x, double y)
{
return !exact_same(x, y);
return !esentially_same(x, y);
}
enum check_stats_contition
@@ -69,19 +78,19 @@ void check_stat(const Stats& s, check_stats_contition cond)
{
switch (cond) {
case stats_empty:
BOOST_TEST(exact_same(s.average, 0.0));
BOOST_TEST(exact_same(s.variance, 0.0));
BOOST_TEST(exact_same(s.deviation, 0.0));
BOOST_TEST(esentially_same(s.average, 0.0));
BOOST_TEST(esentially_same(s.variance, 0.0));
BOOST_TEST(esentially_same(s.deviation, 0.0));
break;
case stats_full:
BOOST_TEST_GT(s.average, 0.0);
if(not_exact_same(s.variance, 0.0)) {
if(not_esentially_same(s.variance, 0.0)) {
BOOST_TEST_GT(s.variance, 0.0);
BOOST_TEST_GT(s.deviation, 0.0);
}
break;
case stats_mostly_full:
if(not_exact_same(s.variance, 0.0)) {
if(not_esentially_same(s.variance, 0.0)) {
BOOST_TEST_GT(s.average, 0.0);
BOOST_TEST_GT(s.variance, 0.0);
BOOST_TEST_GT(s.deviation, 0.0);
@@ -94,9 +103,9 @@ void check_stat(const Stats& s, check_stats_contition cond)
template <class Stats> void check_stat(const Stats& s1, const Stats& s2)
{
BOOST_TEST(exact_same(s1.average, s2.average));
BOOST_TEST(exact_same(s1.variance, s2.variance));
BOOST_TEST(exact_same(s1.deviation, s2.deviation));
BOOST_TEST(esentially_same(s1.average, s2.average));
BOOST_TEST(esentially_same(s1.variance, s2.variance));
BOOST_TEST(esentially_same(s1.deviation, s2.deviation));
}
template <class Stats>
@@ -345,15 +354,28 @@ UNORDERED_AUTO_TEST (stats_) {
boost::concurrent_flat_map<
int, int, boost::hash<int>, std::equal_to<int>,
unequal_allocator< std::pair< const int, int> >>>();
test_stats<
boost::concurrent_node_map<
int, int, boost::hash<int>, std::equal_to<int>,
unequal_allocator< std::pair< const int, int> >>>();
test_stats<
boost::concurrent_flat_set<
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
test_stats<
boost::concurrent_node_set<
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
test_stats_concurrent_unordered_interop<
boost::unordered_flat_map<int, int>,
boost::concurrent_flat_map<int, int>>();
test_stats_concurrent_unordered_interop<
boost::unordered_node_map<int, int>,
boost::concurrent_node_map<int, int>>();
test_stats_concurrent_unordered_interop<
boost::unordered_flat_set<int>,
boost::concurrent_flat_set<int>>();
test_stats_concurrent_unordered_interop<
boost::unordered_node_set<int>,
boost::concurrent_node_set<int>>();
#elif defined(BOOST_UNORDERED_FOA_TESTS)
test_stats<
boost::unordered_flat_map<