Compare commits

..

23 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
73 changed files with 2776 additions and 198 deletions
+2 -2
View File
@@ -279,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",
),
+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
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`].
+4
View File
@@ -9,6 +9,9 @@
== 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^]).
@@ -16,6 +19,7 @@
* 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
+17
View File
@@ -132,6 +132,23 @@ will grant visitation functions const/non-const access to the element depending
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
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`],
+193 -3
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);
@@ -394,8 +428,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
* 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`.
@@ -1113,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]
@@ -1207,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);
+139 -3
View File
@@ -160,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);
@@ -355,8 +374,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
* 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`.
@@ -1086,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]
+214 -3
View File
@@ -175,6 +175,31 @@ namespace boost {
template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_visit](node_type&& nh, F f);
template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_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_node_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_node_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_node_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_node_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2);
template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](const key_type& k, Args&&... args);
template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](key_type&& k, Args&&... args);
template<class K, class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](K&& k, Args&&... args);
@@ -192,6 +217,20 @@ namespace boost {
template<class K, class... Args, class F>
bool xref:#concurrent_node_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_node_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_node_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_node_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_node_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_node_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_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj);
template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj);
template<class K, class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](K&& k, M&& obj);
@@ -406,8 +445,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
* 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_node_map operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`.
@@ -1167,7 +1207,7 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
this->xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
-----
[horizontal]
@@ -1196,6 +1236,130 @@ Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` a
---
==== 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:;; 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:;; 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:;; 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 f2, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_node_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_node_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Node and Visit
```c++
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
```
If `nh` is empty, does nothing.
Otherwise, inserts the associated element in the table if and only if there is no element in the table with a key equivalent to `nh.key()`,
and then invokes `f1` with a non-const reference to the newly inserted element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `insert_or_cvisit` is used.
[horizontal]
Returns:;; An `insert_return_type` object constructed from `inserted` and `node`: +
* If `nh` is empty, `inserted` is `false` and `node` is empty.
* Otherwise if the insertion took place, `inserted` is true and `node` is empty.
* If the insertion failed, `inserted` is false and `node` has the previous value of `nh`.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` or call to `f1` or `f2`, the function has no effect.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` and the container are not equal.
---
==== try_emplace
```c++
template<class... Args> bool try_emplace(const key_type& k, Args&&... args);
@@ -1278,6 +1442,53 @@ 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)...))
```
The interface is exposition only, as C++ does not allow to declare parameter `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);
+161 -3
View File
@@ -166,6 +166,29 @@ namespace boost {
template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_visit](node_type&& nh, F f);
template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_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_node_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_node_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_node_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_node_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_node_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_node_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2);
size_type xref:#concurrent_node_set_erase[erase](const key_type& k);
template<class K> size_type xref:#concurrent_node_set_erase[erase](const K& k);
@@ -368,8 +391,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element.
* Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const
and its name does not contain `cvisit`.)
* 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_node_set operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`.
@@ -1140,7 +1164,7 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
this->xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
-----
[horizontal]
@@ -1169,6 +1193,140 @@ Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` a
---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_or_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_or_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:;; 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 f2, 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 `f` 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`.
---
==== 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`.
---
==== 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:;; 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_node_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_node_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Node and Visit
```c++
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
```
If `nh` is empty, does nothing.
Otherwise, inserts the associated element in the table if and only if there is no element in the table with a key equivalent to `nh.value()`,
and then invokes `f1` with a const reference to the newly inserted element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Returns:;; An `insert_return_type` object constructed from `inserted` and `node`: +
* If `nh` is empty, `inserted` is `false` and `node` is empty.
* Otherwise if the insertion took place, `inserted` is true and `node` is empty.
* If the insertion failed, `inserted` is false and `node` has the previous value of `nh`.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` or call to `f1` or `f2`, the function has no effect.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` and the container are not equal.
---
==== erase
```c++
size_type erase(const key_type& k);
+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);
+144 -12
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_))
@@ -425,16 +426,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 F>
@@ -462,19 +465,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_CONST_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_CONST_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 F>
@@ -502,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)
@@ -538,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);
+239 -12
View File
@@ -197,6 +197,7 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
concurrent_node_map(
unordered_node_map<Key, T, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
@@ -426,16 +427,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());
}
insert_return_type insert(node_type&& nh)
@@ -497,19 +500,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 F>
@@ -549,19 +554,21 @@ 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 F>
@@ -585,6 +592,130 @@ namespace boost {
}
}
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 F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
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 F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -606,6 +737,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)
{
@@ -681,6 +836,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);
+189 -12
View File
@@ -194,6 +194,7 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
concurrent_node_set(
unordered_node_set<Key, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
@@ -432,16 +433,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());
}
insert_return_type insert(node_type&& nh)
@@ -488,19 +491,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_CONST_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_CONST_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 F>
@@ -549,19 +554,21 @@ 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 F>
@@ -585,6 +592,152 @@ namespace boost {
}
}
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>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
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 F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -606,6 +759,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);
@@ -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
@@ -755,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)
{
@@ -769,86 +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 F,typename T=element_type>
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_or_visit(element_type&& x, F&& f)
insert_and_visit(element_type&& x,F1&& f1,F2&& f2)
{
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 F,typename T=element_type>
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_or_cvisit(element_type&& x, F&& f)
insert_and_cvisit(element_type&& x,F1&& f1,F2&& f2)
{
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 Key>
@@ -1408,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>
@@ -1442,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>
@@ -1465,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();
@@ -1498,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_)
@@ -1539,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);
@@ -1552,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())){
@@ -1572,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;
}
@@ -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));
}
@@ -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>
@@ -182,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_))
@@ -178,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_))
@@ -189,6 +189,7 @@ 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_))
@@ -187,6 +187,7 @@ namespace boost {
{
}
template <bool avoid_explicit_instantiation = true>
unordered_node_set(
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
@@ -13,7 +13,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverlength-strings"
#endif
__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
__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"
+4
View File
@@ -163,6 +163,10 @@ compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIM
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
+133
View File
@@ -53,6 +53,34 @@ namespace {
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
}
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.first.x_, v.second.x_, f1, f2);
}
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.first.x_, v.second.x_, f1, f2);
}
struct lvalue_emplacer_type
{
template <class T, class X> void call_impl(std::vector<T>& values, X& x)
@@ -133,6 +161,55 @@ namespace {
}
} lvalue_emplace_or_cvisit;
struct lvalue_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_cvisit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_cvisit;
struct lvalue_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -176,6 +253,55 @@ namespace {
}
} lvalue_emplace_or_visit;
struct lvalue_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_visit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_visit;
struct copy_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -312,6 +438,13 @@ UNORDERED_TEST(
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
emplace,
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_emplace_and_cvisit)(lvalue_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
namespace {
@@ -0,0 +1,15 @@
// Copyright 2024 Braden Ganetsky
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#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>
template class boost::concurrent_flat_map<int, int>;
template class boost::concurrent_flat_set<int>;
template class boost::concurrent_node_map<int, int>;
template class boost::concurrent_node_set<int>;
int main() { return 0; }
+41 -3
View File
@@ -86,7 +86,7 @@ namespace {
while (!nh.empty()) {
auto& o = out[br2++ % out.size()];
typename X::insert_return_type r;
switch (br3++ % 3) {
switch (br3++ % 5) {
case 0:
r = o.insert(std::move(nh));
break;
@@ -97,13 +97,37 @@ namespace {
(void)v2;
});
break;
case 2: default:
case 2:
r = o.insert_or_cvisit(
std::move(nh), [&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 3:
r = o.insert_and_visit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 4: default:
r = o.insert_and_cvisit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
}
BOOST_ASSERT(r.inserted || !r.node.empty());
nh = std::move(r.node);
@@ -144,6 +168,20 @@ namespace {
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_and_visit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_and_cvisit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
}
} // namespace
@@ -156,7 +194,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert_empty_node_tests,
((test_node_map)(test_node_set)))
((test_node_map)(test_node_set)))
// clang-format on
RUN_TESTS()
+463 -23
View File
@@ -149,7 +149,7 @@ namespace {
}
thread_runner(values2, [&x](boost::span<raii_convertible> s) {
x.insert(s.begin(), s.end());
BOOST_TEST_EQ(x.insert(s.begin(), s.end()), s.size());
});
BOOST_TEST_EQ(
@@ -376,6 +376,62 @@ namespace {
}
} lvalue_insert_or_cvisit;
struct lvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_cvisit;
struct lvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -424,6 +480,61 @@ namespace {
}
} lvalue_insert_or_visit;
struct lvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b =
x.insert_and_visit(r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_visit;
struct rvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -470,6 +581,66 @@ namespace {
}
} rvalue_insert_or_cvisit;
struct rvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_cvisit;
struct rvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -522,6 +693,65 @@ namespace {
}
} rvalue_insert_or_visit;
struct rvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_visit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_visit;
struct iterator_range_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -538,11 +768,12 @@ namespace {
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_cvisit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
BOOST_TEST_EQ(x.insert_or_cvisit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
}),
s.size());
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
@@ -561,6 +792,60 @@ namespace {
}
} iterator_range_insert_or_cvisit;
struct iterator_range_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values2,
[&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
BOOST_TEST_EQ(x.insert_and_cvisit(
s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
}),
s.size());
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_cvisit;
struct iterator_range_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -568,6 +853,13 @@ namespace {
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
@@ -577,11 +869,12 @@ namespace {
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
BOOST_TEST_EQ(x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
}),
s.size());
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
@@ -600,6 +893,68 @@ namespace {
}
} iterator_range_insert_or_visit;
struct iterator_range_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values2,
[&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
BOOST_TEST_EQ(x.insert_and_visit(
s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
}),
s.size());
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_visit;
struct non_copyable_function
{
non_copyable_function() = default;
non_copyable_function(const non_copyable_function&) = delete;
non_copyable_function(non_copyable_function&&) = default;
template <class... Args> void operator()(Args&&...) const {}
};
template <class X, class GF, class F>
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
{
@@ -655,8 +1010,9 @@ namespace {
{
X x;
thread_runner(
dummy, [&x, &init_list](boost::span<raii>) { x.insert(init_list); });
thread_runner(dummy, [&x, &init_list](boost::span<raii>) {
BOOST_TEST_EQ(x.insert(init_list), init_list.size());
});
BOOST_TEST_EQ(x.size(), reference_cont.size());
@@ -686,16 +1042,22 @@ namespace {
X x;
thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span<raii>) {
x.insert_or_visit(init_list, [&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
BOOST_TEST_EQ(x.insert_or_visit(init_list,
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
}),
init_list.size());
x.insert_or_cvisit(
init_list, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
BOOST_TEST_EQ(x.insert_or_cvisit(init_list,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
}),
init_list.size());
x.insert_or_visit(init_list, non_copyable_function{});
x.insert_or_cvisit(init_list, non_copyable_function{});
});
BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) +
@@ -721,6 +1083,70 @@ namespace {
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
{
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
X x;
thread_runner(dummy,
[&x, &init_list, &num_inserts, &num_invokes](boost::span<raii>) {
BOOST_TEST_EQ(x.insert_and_visit(
init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
}),
init_list.size());
BOOST_TEST_EQ(x.insert_and_cvisit(
init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
}),
init_list.size());
x.insert_and_visit(
init_list, non_copyable_function{}, non_copyable_function{});
x.insert_and_cvisit(
init_list, non_copyable_function{}, non_copyable_function{});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) +
(num_threads - 1) * init_list.size() +
num_threads * init_list.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
}
@@ -735,6 +1161,9 @@ namespace {
x.insert_or_visit({2, 3}, [](value_type&) {});
x.insert_or_cvisit({3, 4}, [](value_type const&) {});
x.insert_and_visit({4, 5}, [](value_type&) {}, [](value_type&) {});
x.insert_and_cvisit({5, 6}, [](value_type&) {}, [](value_type const&) {});
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
@@ -825,7 +1254,8 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map_and_init_list)(node_map_and_init_list)(set_and_init_list)))
((map_and_init_list)(node_map_and_init_list)
(set_and_init_list)(node_set_and_init_list)))
UNORDERED_TEST(
insert,
@@ -840,6 +1270,16 @@ UNORDERED_TEST(
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map)(fancy_map)(node_map)(fancy_node_map)
(set)(fancy_set)(node_set)(fancy_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_insert_and_cvisit)(lvalue_insert_and_visit)
(rvalue_insert_and_cvisit)(rvalue_insert_and_visit)
(iterator_range_insert_and_cvisit)(iterator_range_insert_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map)(node_map))
((init_type_generator_factory))
+17 -3
View File
@@ -2,12 +2,22 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#include <boost/config/pragma_message.hpp>
#if defined(__MSVC_RUNTIME_CHECKS)
BOOST_PRAGMA_MESSAGE(
"Test skipped because of /RTCc, which is incompatible with Boost.Interprocess");
int main() {}
#else
#include <boost/unordered/concurrent_flat_map.hpp>
#include <atomic>
#include <boost/asio.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/process/child.hpp>
#include <boost/process/process.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <chrono>
@@ -57,9 +67,11 @@ int parent(const char* exe_name)
allocator(segment.get_segment_manager()));
std::atomic_int& start = *segment.construct<std::atomic_int>(start_name)(0);
std::vector<boost::process::child> children;
boost::asio::io_context ctx;
std::vector<boost::process::process> children;
for (int i = 0; i < NUM_CHILDS; ++i) {
children.emplace_back(exe_name, std::to_string(i), segment_name);
children.push_back(boost::process::process(
ctx.get_executor(), exe_name, {std::to_string(i), segment_name_str}));
}
start.store(1);
@@ -112,3 +124,5 @@ int main(int argc, char** argv)
return child(std::atoi(argv[1]),argv[2]);
}
}
#endif
+275
View File
@@ -177,6 +177,53 @@ namespace {
}
} lvalue_try_emplace_or_cvisit;
struct lvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_cvisit;
struct lvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -217,6 +264,53 @@ namespace {
}
} lvalue_try_emplace_or_visit;
struct lvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_visit;
struct rvalue_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -258,6 +352,54 @@ namespace {
}
} rvalue_try_emplace_or_cvisit;
struct rvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_cvisit;
struct rvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -298,6 +440,53 @@ namespace {
}
} rvalue_try_emplace_or_visit;
struct rvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_visit;
struct transp_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -326,6 +515,41 @@ namespace {
}
} transp_try_emplace_or_cvisit;
struct transp_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_cvisit;
struct transp_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
@@ -355,6 +579,42 @@ namespace {
}
} transp_try_emplace_or_visit;
struct transp_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_visit;
template <class X, class G, class F>
void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg)
{
@@ -417,6 +677,14 @@ UNORDERED_TEST(
(rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((map)(node_map))
((value_type_generator)(init_type_generator))
((lvalue_try_emplace_and_cvisit)(lvalue_try_emplace_and_visit)
(rvalue_try_emplace_and_cvisit)(rvalue_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map)(transp_node_map))
@@ -424,6 +692,13 @@ UNORDERED_TEST(
((transp_try_emplace)(norehash_transp_try_emplace)
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map)(transp_node_map))
((init_type_generator))
((transp_try_emplace_and_cvisit)(transp_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
@@ -0,0 +1,29 @@
// Copyright 2024 Braden Ganetsky
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifdef BOOST_UNORDERED_FOA_TESTS
#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>
template class boost::unordered_flat_map<int, int>;
template class boost::unordered_flat_set<int>;
template class boost::unordered_node_map<int, int>;
template class boost::unordered_node_set<int>;
#else
#include <boost/unordered/unordered_map.hpp>
#include <boost/unordered/unordered_set.hpp>
template class boost::unordered_map<int, int>;
template class boost::unordered_multimap<int, int>;
template class boost::unordered_multiset<int>;
template class boost::unordered_set<int>;
#endif // BOOST_UNORDERED_FOA_TESTS
int main() { return 0; }
+12 -6
View File
@@ -1,15 +1,19 @@
// 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)
#include <boost/config.hpp>
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_CLANG_VERSION) && BOOST_CLANG_VERSION < 30900
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE(
"This version of clang is incompatible with Boost.Process");
int main() {}
#elif defined(__MSVC_RUNTIME_CHECKS)
BOOST_PRAGMA_MESSAGE(
"Test skipped because of /RTCc, which is incompatible with Boost.Interprocess");
int main() {}
#else
#include "../helpers/test.hpp"
@@ -28,8 +32,8 @@ int main() {}
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/process/child.hpp>
#include <boost/process/filesystem.hpp>
#include <boost/asio.hpp>
#include <boost/process/process.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
@@ -141,7 +145,8 @@ parent(std::string const& shm_name_, char const* exe_name, C*)
BOOST_TEST(c->empty());
boost::process::child child(exe_name, shm_name);
boost::asio::io_context ctx;
boost::process::process child(ctx.get_executor(), exe_name, {shm_name});
child.wait();
int ret = child.exit_code();
@@ -240,7 +245,8 @@ parent(std::string const& shm_name_, char const* exe_name, C*)
BOOST_TEST(c->empty());
boost::process::child child(exe_name, shm_name);
boost::asio::io_context ctx;
boost::process::process child(ctx.get_executor(), exe_name, {shm_name});
child.wait();
int ret = child.exit_code();