Compare commits

...

86 Commits

Author SHA1 Message Date
Peter Dimov 6c0b763176 Disable Interprocess tests when /RTC is in effect. 2025-01-17 18:40:03 +02:00
Christian Mazakas 40cf55240b Revert "update documentation to use antora"
This reverts commit 3c452f93c5.
2024-12-31 12:11:06 -08:00
Christian Mazakas 3c452f93c5 update documentation to use antora 2024-12-31 12:00:52 -08:00
joaquintides cb835cdf1d Merge pull request #295 from k3DW/inline-asm
Fix inline assembly for armv7 target
2024-12-30 20:55:26 +01:00
Braden Ganetsky 07f6463c1c Fix inline assembly for armv7 target
This fix is based on boostorg/outcome#308
2024-12-29 12:55:49 -06:00
joaquintides c0c8d61688 Merge pull request #298 from boostorg/fix/split-msvc-14.2
split msvc-14.2 jobs
2024-12-29 16:17:35 +01:00
joaquintides d0116814c9 split msvc-14.2 jobs 2024-12-29 12:06:18 +01:00
joaquintides a885388480 Merge pull request #297 from boostorg/fix/upgrade-macos
bumped all macos jobs one version up
2024-12-28 22:17:16 +01:00
joaquintides accae6fdd9 bumped all macos jobs one version up 2024-12-28 17:51:31 +01:00
joaquintides a633bbaa4d Merge pull request #296 from boostorg/fix/upgrade-boost-process
upgraded tests to boost::process::v2
2024-12-28 17:41:34 +01:00
joaquintides 1389043933 upgraded to boost::process::v2 2024-12-28 12:29:33 +01:00
joaquintides b41c054c66 Merge pull request #293 from boostorg/fix/upgrade_droneubuntu2404
updated cppalliance/droneubuntu2304 to 2404
2024-10-23 18:32:15 +02:00
joaquintides fbc981eac9 Merge pull request #292 from k3DW/explicit-instantiation
Ensure each container type can be explicitly instantiated
2024-10-23 16:47:16 +02:00
joaquintides 5d2b27d1d9 updated cppalliance/droneubuntu2304 to 2404 2024-10-23 13:47:41 +02:00
Braden Ganetsky a36937d9b5 Ensure fca containers have the embedded GDB pretty-printers if none of the [c]foa containers have also been included. (#291)
This was missed in PR #274. We don't yet have automated pretty-printer tests, so this change will have no effect on CI.
2024-10-09 09:54:47 +02:00
Braden Ganetsky e214ecdbd0 Use std::ref instead of copying the function parameter… (#290)
* GHA: revise numerous CI jobs. Fix sanitizer on new kernel

* Use std::ref instead of copying the function parameter in std::initializer_list overloads of insert_{or|and}_[c]visit

* Reimplement detail::is_invocable

* Update docs for std::ref(f) changes

---------

Co-authored-by: sdarwin <samuel.d.darwin@gmail.com>
2024-10-08 17:35:58 +02:00
Sam Darwin 94ab2f0776 GHA: revise numerous CI jobs. Fix sanitizer on new kernel (#289) 2024-10-08 17:35:35 +02:00
joaquintides 1bdb3717d3 updated concurrent benchmarks 2024-10-08 17:28:52 +02:00
Braden Ganetsky cfdfae0a65 Ensure each container type can be explicitly instantiated 2024-10-06 22:38:00 -05:00
Braden Ganetsky 489b50bc0f Merge pull request #287 from k3DW/insert-return-type
Change the range insertion return type match the documentation
2024-09-27 22:24:08 -05:00
Braden Ganetsky 3fe7871745 Fix the return value from cfoa 'insert()' overloads to match the docs for iterator-pair and initializer-list 2024-09-26 17:45:33 -05:00
Braden Ganetsky 1e043993ac Update cfoa 'insert()' tests to check the return value of the iterator-pair and initializer-list overloads 2024-09-25 22:59:13 -05:00
joaquintides 834580b539 added insert_and_visit and similar operations to concurrent containers (#283) 2024-09-21 10:58:30 +02:00
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
138 changed files with 11741 additions and 625 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
+3 -2
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',
@@ -278,14 +279,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
linux_pipeline(
"Linux 23.04 GCC 13 32/64 (11,14)",
"cppalliance/droneubuntu2304:1",
"cppalliance/droneubuntu2404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14', ADDRMD: '32,64' },
"g++-13 g++-13-multilib",
),
linux_pipeline(
"Linux 23.04 GCC 13 32/64 (17,20,2b)",
"cppalliance/droneubuntu2304:1",
"cppalliance/droneubuntu2404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '17,20,2b', ADDRMD: '32,64' },
"g++-13 g++-13-multilib",
),
+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
+51 -32
View File
@@ -57,20 +57,23 @@ jobs:
- { compiler: gcc-9, cxxstd: '11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-11' }
- { name: "gcc-12 w/ sanitizers (11)", sanitize: yes,
compiler: gcc-12, cxxstd: '11', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (14)", sanitize: yes,
compiler: gcc-12, cxxstd: '14', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (17)", sanitize: yes,
compiler: gcc-12, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "gcc-12 w/ sanitizers (20)", sanitize: yes,
compiler: gcc-12, cxxstd: '20', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "gcc-12 w/ sanitizers (2b)", sanitize: yes,
compiler: gcc-12, cxxstd: '2b', os: 'ubuntu-22.04', ccache_key: "san2" }
- { compiler: gcc-12, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-12' }
- { compiler: gcc-13, cxxstd: '11,14,17,20', os: 'ubuntu-24.04', install: 'g++-13' }
- { compiler: gcc-14, cxxstd: '11,14,17,20', os: 'ubuntu-24.04', install: 'g++-14' }
- { name: "gcc-14 w/ sanitizers (11)", sanitize: yes,
compiler: gcc-14, cxxstd: '11', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san1" }
- { name: "gcc-14 w/ sanitizers (14)", sanitize: yes,
compiler: gcc-14, cxxstd: '14', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san1" }
- { name: "gcc-14 w/ sanitizers (17)", sanitize: yes,
compiler: gcc-14, cxxstd: '17', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san2" }
- { name: "gcc-14 w/ sanitizers (20)", sanitize: yes,
compiler: gcc-14, cxxstd: '20', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san2" }
- { name: "gcc-14 w/ sanitizers (2b)", sanitize: yes,
compiler: gcc-14, cxxstd: '2b', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san2" }
- { name: Collect coverage, coverage: yes,
compiler: gcc-12, cxxstd: '20', os: 'ubuntu-22.04', install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
compiler: gcc-14, cxxstd: '20', os: 'ubuntu-24.04', install: 'g++-14 g++-14-multilib', address-model: '32,64', ccache_key: "cov" }
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: gcc-12,
- { name: "cfoa tsan (gcc-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-24.04', install: 'g++-14', compiler: gcc-14,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
# Linux, clang, libc++
@@ -88,29 +91,36 @@ jobs:
compiler: clang-12, cxxstd: '2b', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: 'clang-13', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
- { compiler: 'clang-13', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
- { compiler: 'clang-14', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
- { compiler: 'clang-14', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
- { compiler: 'clang-15', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
- { compiler: 'clang-15', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
- { compiler: 'clang-16', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-16 libc++-16-dev libc++abi-16-dev' }
- { compiler: 'clang-16', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-16 libc++-16-dev libc++abi-16-dev' }
- { compiler: 'clang-17', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-17 libc++-17-dev libc++abi-17-dev' }
- { compiler: 'clang-17', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-17 libc++-17-dev libc++abi-17-dev' }
- { compiler: 'clang-18', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-dev' }
- { compiler: 'clang-18', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-dev' }
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
- { name: "clang-14 w/ sanitizers (11,14)", sanitize: yes,
compiler: clang-14, cxxstd: '11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "clang-14 w/ sanitizers (17)", sanitize: yes,
compiler: clang-14, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "clang-14 w/ sanitizers (20)", sanitize: yes,
compiler: clang-14, cxxstd: '20', container: 'ubuntu:22.04', os: 'ubuntu-latest', ccache_key: "san2" }
- { name: "clang-14 w/ sanitizers (2b)", sanitize: yes,
compiler: clang-14, cxxstd: '2b', container: 'ubuntu:22.04', os: 'ubuntu-latest', ccache_key: "san2" }
- { name: "clang-18 w/ sanitizers (11,14)", sanitize: yes,
compiler: clang-18, cxxstd: '11,14', os: 'ubuntu-24.04', ccache_key: "san1" }
- { name: "clang-18 w/ sanitizers (17)", sanitize: yes,
compiler: clang-18, cxxstd: '17', os: 'ubuntu-24.04', ccache_key: "san2" }
- { name: "clang-18 w/ sanitizers (20)", sanitize: yes,
compiler: clang-18, cxxstd: '20', container: 'ubuntu:24.04', os: 'ubuntu-latest', ccache_key: "san2" }
- { name: "clang-18 w/ sanitizers (2b)", sanitize: yes,
compiler: clang-18, cxxstd: '2b', container: 'ubuntu:24.04', os: 'ubuntu-latest', ccache_key: "san2" }
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: clang-14,
- { name: "cfoa tsan (clang-18)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-24.04', compiler: clang-18,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes,
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev', ccache_key: "tsan" }
- { compiler: 'clang-15', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
- { compiler: 'clang-15', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-dev', ccache_key: "tsan" }
# OSX, clang
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', sanitize: yes, ccache_key: "san1" }
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13' }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13', sanitize: yes, ccache_key: "san1" }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-14' }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-15' }
timeout-minutes: 360
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
@@ -239,7 +249,12 @@ jobs:
- name: Run tests
if: '!matrix.coverity'
run: B2_TARGETS=${{matrix.targets}} ci/build.sh
run: |
if [[ ${B2_TSAN} == "yes" ]] && [[ $(uname) == "Linux" ]]; then
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
sudo sysctl vm.mmap_rnd_bits=28
fi
B2_TARGETS=${{matrix.targets}} ci/build.sh
- name: Upload coverage
if: matrix.coverage
@@ -263,7 +278,8 @@ jobs:
matrix:
include:
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '20,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
@@ -326,8 +342,8 @@ jobs:
fail-fast: false
matrix:
include:
- { sys: MINGW32, compiler: gcc, cxxstd: '11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '11,17,20' }
- { sys: MINGW32, compiler: gcc, cxxstd: '11,17,20', variant: 'release' }
- { sys: MINGW64, compiler: gcc, cxxstd: '11,17,20', variant: 'debug,release' }
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
@@ -361,9 +377,12 @@ jobs:
B2_CXXSTD: ${{matrix.cxxstd}}
B2_SANITIZE: ${{matrix.sanitize}}
B2_STDLIB: ${{matrix.stdlib}}
B2_VARIANT: ${{matrix.variant}}
run: ci/github/install.sh
- name: Run tests
env:
B2_VARIANT: ${{matrix.variant}}
run: ci/build.sh
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
+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
;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 30 KiB

+3 -2
View File
@@ -459,13 +459,14 @@ h|unsuccessful lookup
|===
== boost::concurrent_flat_map
== boost::concurrent_(flat|node)_map
All benchmarks were created using:
* `https://spec.oneapi.io/versions/latest/elements/oneTBB/source/containers/concurrent_hash_map_cls.html[oneapi::tbb::concurrent_hash_map^]<int, int>`
* `https://github.com/greg7mdp/gtl/blob/main/docs/phmap.md[gtl::parallel_flat_hash_map^]<int, int>` with 64 submaps
* `boost::concurrent_flat_map<int, int>`
* `boost::concurrent_node_map<int, int>`
The source code can be https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_concurrent_flat_map[found here^].
@@ -475,7 +476,7 @@ operations follow a https://en.wikipedia.org/wiki/Zipf%27s_law#Formal_definition
with different _skew_ parameters: the higher the skew, the more concentrated are the keys in the lower values
of the covered range.
`boost::concurrent_flat_map` is exercised using both regular and xref:#concurrent_bulk_visitation[bulk visitation]:
`boost::concurrent_flat_map` and `boost::concurrent_node_map` are exercised using both regular and xref:#concurrent_bulk_visitation[bulk visitation]:
in the latter case, lookup keys are buffered in a local array and then processed at
once each time the buffer reaches xref:#concurrent_flat_map_constants[`bulk_visit_size`].
+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.
+16
View File
@@ -6,6 +6,22 @@
: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`.
* Added `insert_and_visit(x, f1, f2)` and similar operations to concurrent containers, which
allow for visitation of an element right after insertion (by contrast, `insert_or_visit(x, f)` only
visits the element if insertion did _not_ take place).
* 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^]).
* Allowed non-copyable callables to be passed to the `std::initializer_list` overloads of `insert_{and|or}_[c]visit` for concurrent containers, by internally passing a `std::reference_wrapper` of the callable to the iterator-pair overloads.
== 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`].
+46 -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,32 @@ 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.
Although expected to be used much less frequently, concurrent containers
also provide insertion operations where an element can be visited right after
element creation (in addition to the usual visitation when an equivalent
element already exists):
[source,c++]
----
m.insert_and_cvisit(x,
[](const auto& y) {
std::cout<< "(" << y.first << ", " << y.second <<") inserted\n";
},
[](const auto& y) {
std::cout<< "(" << y.first << ", " << y.second << ") already exists\n";
});
----
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 +265,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 +278,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:
+198 -4
View File
@@ -169,6 +169,27 @@ namespace boost {
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args);
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args);
template<class K, class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](K&& k, Args&&... args);
@@ -186,6 +207,19 @@ namespace boost {
template<class K, class... Args, class F>
bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj);
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj);
template<class K, class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj);
@@ -389,9 +423,14 @@ 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:
** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`.
@@ -1109,7 +1148,113 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `emplace_and_cvisit` is used.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `const value_type&` argument participate in overload resolution
only if `std::remove_cv<std::remove_reference<decltype(obj)>::type>::type` is `value_type`.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `value_type&&` argument participate in overload resolution
only if `std::remove_reference<decltype(obj)>::type` is `value_type`.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator, class F1, class F2>
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator, class F1, class F2>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
-----
[horizontal]
@@ -1203,6 +1348,55 @@ The `template<class K, class\... Args, class F>` overloads only participate in o
---
==== try_emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
```
Inserts an element constructed from `k` and `args` into the table if there is no existing element with key `k` contained within it,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; No `value_type` is constructed
if there is an element with an equivalent key; otherwise, the construction is of the form: +
+
--
```c++
// first four overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// last two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
Invalidates pointers and references to elements if a rehashing is issued.
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
The `template<class K, class\... Args, class F1, class F2>` overloads only participate 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.
--
---
==== insert_or_assign
```c++
template<class M> bool insert_or_assign(const key_type& k, M&& obj);
+162 -4
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>
@@ -150,6 +160,25 @@ namespace boost {
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
size_type xref:#concurrent_flat_set_erase[erase](const key_type& k);
template<class K> size_type xref:#concurrent_flat_set_erase[erase](const K& k);
@@ -340,9 +369,14 @@ 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:
** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
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 +742,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 +761,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 +796,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 +811,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 +832,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 +848,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;
```
@@ -1064,7 +1106,123 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
---
==== Transparent insert_and_[c]visit
```c++
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
```
Inserts an element constructed from `std::forward<K>(k)` in the container if and only if there is no element in the container with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
These overloads only participate 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.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator,class F1, class F2>
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
-----
[horizontal]
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 ...
...
```
"""
+195 -12
View File
@@ -190,6 +190,7 @@ namespace boost {
}
template <bool avoid_explicit_instantiation = true>
concurrent_flat_map(
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
@@ -419,16 +420,18 @@ namespace boost {
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
size_type insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
size_type count_elements = 0;
for (auto pos = begin; pos != end; ++pos, ++count_elements) {
table_.emplace(*pos);
}
return count_elements;
}
void insert(std::initializer_list<value_type> ilist)
size_type insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
return this->insert(ilist.begin(), ilist.end());
}
template <class M>
@@ -471,19 +474,21 @@ namespace boost {
}
template <class InputIterator, class F>
void insert_or_visit(InputIterator first, InputIterator last, F f)
size_type insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
for (; first != last; ++first) {
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_or_visit(*first, f);
}
return count_elements;
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
size_type 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);
return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
}
template <class Ty, class F>
@@ -502,19 +507,101 @@ namespace boost {
}
template <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_or_cvisit(*first, f);
}
return count_elements;
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
size_type 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);
return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
size_type insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_and_visit(*first, f1, f2);
}
return count_elements;
}
template <class F1, class F2>
size_type insert_and_visit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return this->insert_and_visit(
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
size_type insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_and_cvisit(*first, f1, f2);
}
return count_elements;
}
template <class F1, class F2>
size_type insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return this->insert_and_cvisit(
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
@@ -538,6 +625,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
@@ -613,6 +724,78 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
+206 -17
View File
@@ -187,6 +187,7 @@ namespace boost {
}
template <bool avoid_explicit_instantiation = true>
concurrent_flat_set(
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
@@ -227,6 +228,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 +249,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 +276,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 +303,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 +322,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 +353,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 +372,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
@@ -368,30 +426,32 @@ namespace boost {
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
size_type insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
size_type count_elements = 0;
for (auto pos = begin; pos != end; ++pos, ++count_elements) {
table_.emplace(*pos);
}
return count_elements;
}
void insert(std::initializer_list<value_type> ilist)
size_type insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
return this->insert(ilist.begin(), ilist.end());
}
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_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,23 +461,25 @@ 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>
void insert_or_visit(InputIterator first, InputIterator last, F f)
size_type insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_or_visit(*first, f);
}
return count_elements;
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
size_type 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);
return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
}
template <class F>
@@ -445,19 +507,122 @@ namespace boost {
}
template <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_or_cvisit(*first, f);
}
return count_elements;
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
size_type 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);
return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_visit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
size_type insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_and_visit(*first, f1, f2);
}
return count_elements;
}
template <class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return this->insert_and_visit(
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_cvisit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
size_type insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
size_type count_elements = 0;
for (; first != last; ++first, ++count_elements) {
table_.emplace_and_cvisit(*first, f1, f2);
}
return count_elements;
}
template <class F1, class F2>
size_type insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return this->insert_and_cvisit(
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
@@ -469,7 +634,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)...);
}
@@ -481,6 +646,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
File diff suppressed because it is too large Load Diff
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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
@@ -1,5 +1,5 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* 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)
@@ -13,10 +13,7 @@
#include <boost/config.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <functional>
#include <iterator>
#include <type_traits>
#include <boost/unordered/detail/type_traits.hpp>
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
@@ -58,16 +55,41 @@
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args) \
mp11::mp_at_c<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>, \
mp11::mp_size<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>>::value - 2>
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type> >
template <class...> struct is_invocable_helper : std::false_type
{
};
template <class F, class... Args>
struct is_invocable_helper<
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>, F,
Args...> : std::true_type
{
};
template <class F, class... Args>
using is_invocable = is_invocable_helper<void, F, Args...>;
} // namespace detail
} // namespace unordered
@@ -215,7 +215,7 @@ struct atomic_integral
/* Group-level concurrency protection. It provides a rw mutex plus an
* atomic insertion counter for optimistic insertion (see
* unprotected_norehash_emplace_or_visit).
* unprotected_norehash_emplace_and_visit).
*/
struct group_access
@@ -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)
{
@@ -747,6 +755,22 @@ public:
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_exclusive{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_shared{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args)
{
@@ -761,62 +785,121 @@ public:
group_shared{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_visit(const init_type& x,F&& f)
template<typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit(Args&&... args)
{
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
return construct_and_emplace_and_visit_flast(
group_exclusive{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(const init_type& x,F&& f)
template<typename... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(Args&&... args)
{
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
return construct_and_emplace_and_visit_flast(
group_shared{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_visit(init_type&& x,F&& f)
template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_visit(Value&& x,F&& f)
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
return insert_and_visit(
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& x,F&& f)
template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(Value&& x,F&& f)
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
return insert_and_cvisit(
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
/* SFINAE tilts call ambiguities in favor of init_type */
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_visit(const Value& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_visit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_cvisit(const Value& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_cvisit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_visit(Value&& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_visit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_cvisit(Value&& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_and_visit(element_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_and_cvisit(element_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
template<typename Key>
@@ -889,6 +972,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)
@@ -1353,23 +1459,59 @@ private:
);
}
struct call_construct_and_emplace_and_visit
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->construct_and_emplace_and_visit(
std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_construct_and_emplace_and_visit{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return construct_and_emplace_and_visit(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
auto lck=shared_access();
alloc_cted_insert_type<type_policy,Allocator,Args...> x(
this->al(),std::forward<Args>(args)...);
int res=unprotected_norehash_emplace_or_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value()));
int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
if(BOOST_LIKELY(res>=0))return res!=0;
lck.unlock();
rehash_if_full();
return noinline_emplace_or_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value()));
return noinline_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
}
template<typename... Args>
@@ -1387,6 +1529,15 @@ private:
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_NOINLINE bool noinline_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
}
struct call_emplace_or_visit_impl
{
template<typename... Args>
@@ -1410,15 +1561,49 @@ private:
);
}
struct call_emplace_and_visit_impl
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->emplace_and_visit_impl(std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_emplace_and_visit_impl{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit_impl(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_impl(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
for(;;){
{
auto lck=shared_access();
int res=unprotected_norehash_emplace_or_visit(
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
if(BOOST_LIKELY(res>=0))return res!=0;
}
rehash_if_full();
@@ -1443,6 +1628,16 @@ private:
return true;
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return unprotected_norehash_emplace_and_visit(
access_mode,[&](const value_type&){},
std::forward<F>(f),std::forward<Args>(args)...);
}
struct reserve_size
{
reserve_size(concurrent_table& x_):x(x_)
@@ -1484,10 +1679,10 @@ private:
bool commit_=false;
};
template<typename GroupAccessMode,typename F,typename... Args>
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
unprotected_norehash_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
const auto &k=this->key_from(std::forward<Args>(args)...);
auto hash=this->hash_for(k);
@@ -1497,7 +1692,7 @@ private:
startover:
boost::uint32_t counter=insert_counter(pos0);
if(unprotected_visit(
access_mode,k,pos0,hash,std::forward<F>(f)))return 0;
access_mode,k,pos0,hash,std::forward<F2>(f2)))return 0;
reserve_size rsize(*this);
if(BOOST_LIKELY(rsize.succeeded())){
@@ -1517,6 +1712,7 @@ private:
this->construct_element(p,std::forward<Args>(args)...);
rslot.commit();
rsize.commit();
f1(cast_for(group_exclusive{},type_policy::value_from(*p)));
BOOST_UNORDERED_ADD_STATS(this->cstats.insertion,(pb.length()));
return 1;
}
@@ -1733,7 +1929,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 +1939,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 +1963,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
@@ -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.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -19,27 +19,28 @@ namespace unordered{
namespace detail{
namespace foa{
template<typename Tuple>
template<std::size_t Offset,typename Tuple>
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
1
Offset
>;
template<std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Tuple>
template<std::size_t Offset,std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Offset,Tuple>
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
{
return tuple_rotate_right_return_type<Tuple>{
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
return tuple_rotate_right_return_type<Offset,Tuple>{
std::get<(Is+sizeof...(Is)-Offset)%sizeof...(Is)>(
std::forward<Tuple>(x))...};
}
template<typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
template<std::size_t Offset=1,typename Tuple>
tuple_rotate_right_return_type<Offset,Tuple> tuple_rotate_right(Tuple&& x)
{
using RawTuple=typename std::remove_cv<
typename std::remove_reference<Tuple>::type>::type;
return tuple_rotate_right_aux(
return tuple_rotate_right_aux<Offset>(
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
std::forward<Tuple>(x));
}
@@ -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
@@ -21,6 +21,7 @@
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_printers.hpp>
#include <boost/assert.hpp>
#include <boost/core/allocator_traits.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)
@@ -181,6 +182,7 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
unordered_flat_map(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
@@ -202,6 +204,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)
@@ -177,6 +178,7 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
unordered_flat_set(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
@@ -198,6 +200,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)...);
}
+32 -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,13 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
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 +211,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 +290,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 +309,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 +494,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;
}
+31 -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,13 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
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 +209,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 +303,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 +322,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 +390,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)
+60 -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,13 +156,17 @@ 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 ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
compile unordered/explicit_instantiation_tests.cpp : : fca_explicit_instantiation_tests ;
compile unordered/explicit_instantiation_tests.cpp : <define>BOOST_UNORDERED_FOA_TESTS : foa_explicit_instantiation_tests ;
compile cfoa/explicit_instantiation_tests.cpp : : cfoa_explicit_instantiation_tests ;
local FCA_EXCEPTION_TESTS =
constructor_exception_tests
copy_exception_tests
@@ -230,6 +242,7 @@ local FOA_TESTS =
fancy_pointer_noleak
pmr_allocator_tests
stats_tests
node_handle_allocator_tests
;
for local test in $(FOA_TESTS)
@@ -251,7 +264,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 +299,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 +327,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 +356,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 +367,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 +400,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 +413,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 +431,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; }

Some files were not shown because too many files have changed in this diff Show More