Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 085414479a | |||
| 515412ce0a | |||
| 6395ce0486 |
@@ -31,28 +31,14 @@ environment:
|
||||
B2_VARIANT: debug,release
|
||||
|
||||
matrix:
|
||||
- FLAVOR: Visual Studio 2008
|
||||
- FLAVOR: Visual Studio 2008, 2010, 2012
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-9.0
|
||||
B2_TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2010
|
||||
- FLAVOR: Visual Studio 2013, 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-10.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2012
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-11.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-12.0
|
||||
|
||||
- FLAVOR: Visual Studio 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-14.0
|
||||
B2_TOOLSET: msvc-12.0,msvc-14.0
|
||||
|
||||
- FLAVOR: Visual Studio 2017, C++14
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
@@ -69,32 +55,32 @@ environment:
|
||||
B2_CXXSTD: latest
|
||||
B2_TOOLSET: msvc-14.1
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++03)
|
||||
- FLAVOR: cygwin (32-bit, C++03,11)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 03
|
||||
B2_CXXSTD: 03,11
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++11)
|
||||
- FLAVOR: cygwin (32-bit, C++14,1z)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 11
|
||||
B2_CXXSTD: 14,1z
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++14)
|
||||
- FLAVOR: cygwin (64-bit, C++03,11)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 14
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 03,11
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++1z)
|
||||
- FLAVOR: cygwin (64-bit, C++14,1z)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 1z
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 14,1z
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (64-bit, latest, C++03)
|
||||
|
||||
@@ -20,4 +20,4 @@ b2 -d0 headers
|
||||
|
||||
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
|
||||
@@ -32,16 +32,6 @@ env:
|
||||
CODECOV_NAME: Github Actions
|
||||
|
||||
jobs:
|
||||
runner-selection:
|
||||
# runs-on: ubuntu-latest
|
||||
runs-on: ${{ github.repository_owner == 'boostorg' && fromJSON('[ "self-hosted", "linux", "x64", "ubuntu-latest-aws" ]') || 'ubuntu-latest' }}
|
||||
outputs:
|
||||
labelmatrix: ${{ steps.aws_hosted_runners.outputs.labelmatrix }}
|
||||
steps:
|
||||
- name: AWS Hosted Runners
|
||||
id: aws_hosted_runners
|
||||
uses: cppalliance/aws-hosted-runners@v1.0.0
|
||||
|
||||
posix:
|
||||
defaults:
|
||||
run:
|
||||
@@ -52,55 +42,50 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# Linux, gcc
|
||||
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', install: 'g++-7' }
|
||||
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', install: 'g++-8' }
|
||||
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
|
||||
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', install: 'g++-10' }
|
||||
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', install: 'g++-11' }
|
||||
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' }
|
||||
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' }
|
||||
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-9' }
|
||||
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-10' }
|
||||
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-11' }
|
||||
- { name: "gcc-12 w/ sanitizers (03,11,14)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '03,11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
|
||||
compiler: gcc-12, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
|
||||
- { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '17,20,2b', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
|
||||
- { name: Collect coverage, coverage: yes,
|
||||
compiler: gcc-12, cxxstd: '03,20', os: 'ubuntu-22.04', install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
|
||||
compiler: gcc-12, cxxstd: '03,20', os: ubuntu-22.04, install: 'g++-12-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,
|
||||
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
|
||||
- { name: "cfoa tsan (gcc)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: gcc-12,
|
||||
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes }
|
||||
|
||||
# Linux, clang, libc++
|
||||
- { compiler: clang-7, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
|
||||
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
|
||||
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
|
||||
# clang-12 doesn't work on 'ubuntu-22.04', the linker can't find -lunwind for some reason
|
||||
- { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
|
||||
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
|
||||
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
|
||||
# clang-12 doesn't work on ubuntu-22.04, the linker can't find -lunwind for some reason
|
||||
- { name: "clang-12 w/ sanitizers (03,11,14)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '03,11,14', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
|
||||
compiler: clang-12, cxxstd: '03,11,14', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
|
||||
- { name: "clang-12 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '17,20,2b', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
|
||||
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
|
||||
compiler: clang-12, cxxstd: '17,20,2b', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
|
||||
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
|
||||
- { compiler: clang-14, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
|
||||
|
||||
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
|
||||
- { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '03,11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
|
||||
compiler: clang-14, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
|
||||
- { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '17,20,2b', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, 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)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: clang-14,
|
||||
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: '03,11,14,17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
|
||||
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
|
||||
|
||||
# OSX, clang
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: 'macos-11' }
|
||||
- { compiler: clang, cxxstd: '03,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: '03,11,14,17,20,2b', os: 'macos-13' }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
|
||||
- { compiler: clang, cxxstd: '11,14,17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests' }
|
||||
|
||||
timeout-minutes: 360
|
||||
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
|
||||
# timeout-minutes: 210
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
timeout-minutes: 180
|
||||
runs-on: ${{matrix.os}}
|
||||
container: ${{matrix.container}}
|
||||
env: {B2_USE_CCACHE: 1}
|
||||
|
||||
@@ -244,16 +229,15 @@ jobs:
|
||||
fail-fast: false
|
||||
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.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"' }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
|
||||
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: 'windows-2019', variant: 'debug,release' }
|
||||
- { 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.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"' }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
|
||||
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019, variant: 'debug,release' }
|
||||
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -310,8 +294,7 @@ jobs:
|
||||
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -365,12 +348,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: 'ubuntu-20.04', build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
|
||||
- { os: 'windows-2019', build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
|
||||
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
|
||||
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
|
||||
|
||||
timeout-minutes: 120
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
@@ -298,7 +298,7 @@ The successful lookup benchmarks are done by looking up all `n` values, in their
|
||||
The unsuccessful lookup benchmarks use `n` randomly generated integers but using a different seed value.
|
||||
|
||||
|
||||
=== GCC 12, x64
|
||||
=== GCC 11, x64
|
||||
|
||||
|
||||
[caption=]
|
||||
@@ -317,7 +317,7 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
=== Clang 15, x64
|
||||
=== Clang 12, x64
|
||||
|
||||
|
||||
[caption=]
|
||||
@@ -336,7 +336,7 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
=== Visual Studio 2022, x64
|
||||
=== Visual Studio 2019, x64
|
||||
|
||||
|
||||
[caption=]
|
||||
@@ -374,7 +374,7 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
=== GCC 12, x86
|
||||
=== GCC 11, x86
|
||||
|
||||
|
||||
[caption=]
|
||||
@@ -393,7 +393,7 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
=== Clang 15, x86
|
||||
=== Clang 12, x86
|
||||
|
||||
|
||||
[caption=]
|
||||
@@ -412,7 +412,7 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
=== Visual Studio 2022, x86
|
||||
=== Visual Studio 2019, x86
|
||||
|
||||
|
||||
[caption=]
|
||||
|
||||
@@ -6,19 +6,6 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.84.0 - Major update
|
||||
|
||||
* Added `boost::concurrent_flat_set`.
|
||||
* Added `[c]visit_while` operations to concurrent containers,
|
||||
with serial and parallel variants.
|
||||
* Added efficient move construction of `boost::unordered_flat_(map|set)` from
|
||||
`boost::concurrent_flat_(map|set)` and vice versa.
|
||||
* Added debug-mode mechanisms for detecting illegal reentrancies into
|
||||
a concurrent container from user code.
|
||||
* Added Boost.Serialization support to all containers and their (non-local) iterator types.
|
||||
* Added support for fancy pointers to open-addressing and concurrent containers.
|
||||
This enables scenarios like the use of Boost.Interprocess allocators to construct containers in shared memory.
|
||||
|
||||
== Release 1.83.0 - Major update
|
||||
|
||||
* Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing.
|
||||
|
||||
@@ -148,14 +148,14 @@ The main differences with C++ unordered associative containers are:
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
There is currently no specification in the C++ standard for this or any other type of concurrent
|
||||
data structure. The APIs of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`
|
||||
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
|
||||
with the crucial difference that iterators are not provided
|
||||
There is currently no specification in the C++ standard for this or any other concurrent
|
||||
data structure. `boost::concurrent_flat_map` takes the same template parameters as `std::unordered_map`
|
||||
and all the maps provided by Boost.Unordered, and its API is modelled after that of
|
||||
`boost::unordered_flat_map` with the crucial difference that iterators are not provided
|
||||
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
|
||||
so, Boost.Unordered concurrent containers are technically not models of
|
||||
so, `boost::concurrent_flat_map` is technically not a
|
||||
https://en.cppreference.com/w/cpp/named_req/Container[Container^], although
|
||||
they meet all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
it meets all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
containers except those implying iterators.
|
||||
|
||||
In a non-concurrent unordered container, iterators serve two main purposes:
|
||||
@@ -163,7 +163,7 @@ In a non-concurrent unordered container, iterators serve two main purposes:
|
||||
* Access to an element previously located via lookup.
|
||||
* Container traversal.
|
||||
|
||||
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
|
||||
In place of iterators, `boost::concurrent_flat_map` uses _internal visitation_
|
||||
facilities as a thread-safe substitute. Classical operations returning an iterator to an
|
||||
element already existing in the container, like for instance:
|
||||
|
||||
@@ -191,15 +191,15 @@ template<class F> size_t visit_all(F f);
|
||||
----
|
||||
|
||||
of which there are parallelized versions in C++17 compilers with parallel
|
||||
algorithm support. In general, the interface of concurrent containers
|
||||
is derived from that of their non-concurrent counterparts by a fairly straightforward
|
||||
process of replacing iterators with visitation where applicable. If for
|
||||
regular maps `iterator` and `const_iterator` provide mutable and const access to elements,
|
||||
algorithm support. In general, the interface of `boost::concurrent_flat_map`
|
||||
is derived from that of `boost::unordered_flat_map` by a fairly straightforward
|
||||
process of replacing iterators with visitation where applicable. If
|
||||
`iterator` and `const_iterator` provide mutable and const access to elements,
|
||||
respectively, here visitation is granted mutable or const access depending on
|
||||
the constness of the member function used (there are also `*cvisit` overloads for
|
||||
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
|
||||
explicit const visitation).
|
||||
|
||||
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
|
||||
The one notable operation not provided is `operator[]`/`at`, which can be
|
||||
replaced, if in a more convoluted manner, by
|
||||
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
:idprefix: concurrent_
|
||||
|
||||
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
hash tables that allow concurrent write/read access from
|
||||
Boost.Unordered currently provides just one concurrent container named `boost::concurrent_flat_map`.
|
||||
`boost::concurrent_flat_map` is a hash table that allows concurrent write/read access from
|
||||
different threads without having to implement any synchronzation mechanism on the user's side.
|
||||
|
||||
[source,c++]
|
||||
@@ -36,16 +36,16 @@ In the example above, threads access `m` without synchronization, just as we'd d
|
||||
single-threaded scenario. In an ideal setting, if a given workload is distributed among
|
||||
_N_ threads, execution is _N_ times faster than with one thread —this limit is
|
||||
never attained in practice due to synchronization overheads and _contention_ (one thread
|
||||
waiting for another to leave a locked portion of the map), but Boost.Unordered concurrent containers
|
||||
are designed to perform with very little overhead and typically achieve _linear scaling_
|
||||
waiting for another to leave a locked portion of the map), but `boost::concurrent_flat_map`
|
||||
is designed to perform with very little overhead and typically achieves _linear scaling_
|
||||
(that is, performance is proportional to the number of threads up to the number of
|
||||
logical cores in the CPU).
|
||||
|
||||
== Visitation-based API
|
||||
|
||||
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
|
||||
will notice is that these classes _do not provide iterators_ (which makes them technically
|
||||
not https://en.cppreference.com/w/cpp/named_req/Container[Containers^]
|
||||
The first thing a new user of `boost::concurrent_flat_map` will notice is that this
|
||||
class _does not provide iterators_ (which makes it technically
|
||||
not a https://en.cppreference.com/w/cpp/named_req/Container[Container^]
|
||||
in the C++ standard sense). The reason for this is that iterators are inherently
|
||||
thread-unsafe. Consider this hypothetical code:
|
||||
|
||||
@@ -73,7 +73,7 @@ m.visit(k, [](const auto& x) { // x is the element with key k (if it exists)
|
||||
----
|
||||
|
||||
The visitation function passed by the user (in this case, a lambda function)
|
||||
is executed internally by Boost.Unordered in
|
||||
is executed internally by `boost::concurrent_flat_map` in
|
||||
a thread-safe manner, so it can access the element without worrying about other
|
||||
threads interfering in the process.
|
||||
|
||||
@@ -112,7 +112,7 @@ if (found) {
|
||||
}
|
||||
----
|
||||
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_map`, and
|
||||
many classical operations have visitation-enabled variations:
|
||||
|
||||
[source,c++]
|
||||
@@ -129,17 +129,13 @@ the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
|
||||
will grant visitation functions const/non-const access to the element depending on whether
|
||||
`m` is const/non-const. Const access can be always be explicitly requested
|
||||
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
|
||||
in higher parallelization. For `boost::concurrent_flat_set`, on the other hand,
|
||||
visitation is always const access.
|
||||
Consult the references of
|
||||
xref:#concurrent_flat_set[`boost::concurrent_flat_set`] and
|
||||
xref:#concurrent_flat_map[`boost::concurrent_flat_map`]
|
||||
for the complete list of visitation-enabled operations.
|
||||
in higher parallelization. Consult the xref:#concurrent_flat_map[reference]
|
||||
for a complete list of available operations.
|
||||
|
||||
== Whole-Table Visitation
|
||||
|
||||
In the absence of iterators, `visit_all` is provided
|
||||
as an alternative way to process all the elements in the container:
|
||||
In the absence of iterators, `boost::concurrent_flat_map` provides `visit_all`
|
||||
as an alternative way to process all the elements in the map:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -158,28 +154,7 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel
|
||||
});
|
||||
----
|
||||
|
||||
Traversal can be interrupted midway:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
// finds the key to a given (unique) value
|
||||
|
||||
int key = 0;
|
||||
int value = ...;
|
||||
bool found = !m.visit_while([&](const auto& x) {
|
||||
if(x.second == value) {
|
||||
key = x.first;
|
||||
return false; // finish
|
||||
}
|
||||
else {
|
||||
return true; // keep on visiting
|
||||
}
|
||||
});
|
||||
|
||||
if(found) { ... }
|
||||
----
|
||||
|
||||
There is one last whole-table visitation operation, `erase_if`:
|
||||
There is another whole-table visitation operation, `erase_if`:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -188,15 +163,15 @@ m.erase_if([](auto& x) {
|
||||
});
|
||||
----
|
||||
|
||||
`visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
whole-table visitation operations do not block the table during execution: this implies that elements
|
||||
`erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
these operations do not block the table during execution: this implies that elements
|
||||
may be inserted, modified or erased by other threads during visitation. It is
|
||||
advisable not to assume too much about the exact global state of a concurrent container
|
||||
advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map`
|
||||
at any point in your program.
|
||||
|
||||
== Blocking Operations
|
||||
|
||||
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
Boost.Unordered container. Unlike most other operations, these are _blocking_,
|
||||
that is, all other threads are prevented from accesing the tables involved while a copy, assignment,
|
||||
clear or merge operation is in progress. Blocking is taken care of automatically by the library
|
||||
@@ -205,31 +180,3 @@ and the user need not take any special precaution, but overall performance may b
|
||||
Another blocking operation is _rehashing_, which happens explicitly via `rehash`/`reserve`
|
||||
or during insertion when the table's load hits `max_load()`. As with non-concurrent containers,
|
||||
reserving space in advance of bulk insertions will generally speed up the process.
|
||||
|
||||
== Interoperability with non-concurrent containers
|
||||
|
||||
As open-addressing and concurrent containers are based on the same internal data structure,
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map` can
|
||||
be efficiently move-constructed from `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
respectively, and vice versa.
|
||||
This interoperability comes handy in multistage scenarios where parts of the data processing happen
|
||||
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
|
||||
we want to construct a histogram from a huge input vector of words:
|
||||
the population phase can be done in parallel with `boost::concurrent_flat_map` and results
|
||||
then transferred to the final container.
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
std::vector<std::string> words = ...;
|
||||
|
||||
// Insert words in parallel
|
||||
boost::concurrent_flat_map<std::string_view, std::size_t> m0;
|
||||
std::for_each(
|
||||
std::execution::par, words.begin(), words.end(),
|
||||
[&](const auto& word) {
|
||||
m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; });
|
||||
});
|
||||
|
||||
// Transfer to a regular unordered_flat_map
|
||||
boost::unordered_flat_map m=std::move(m0);
|
||||
----
|
||||
|
||||
@@ -69,7 +69,6 @@ namespace boost {
|
||||
explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a);
|
||||
xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -90,10 +89,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#concurrent_flat_map_destructor[~concurrent_flat_map]();
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_copy_assignment[operator++=++](const concurrent_flat_map& other);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -116,16 +114,6 @@ namespace boost {
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
// capacity
|
||||
++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept;
|
||||
size_type xref:#concurrent_flat_map_size[size]() const noexcept;
|
||||
@@ -317,7 +305,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the table.
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the table's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -375,18 +364,6 @@ if concurrent outstanding operations on `y` do not access `x` directly or indire
|
||||
|
||||
---
|
||||
|
||||
=== Configuration Macros
|
||||
|
||||
==== `BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK`
|
||||
|
||||
In debug builds (more precisely, when
|
||||
link:../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST_ASSERT_IS_VOID`^]
|
||||
is not defined), __container reentrancies__ (illegaly invoking an operation on `m` from within
|
||||
a function visiting elements of `m`) are detected and signalled through `BOOST_ASSERT_MSG`.
|
||||
When run-time speed is a concern, the feature can be disabled by globally defining
|
||||
this macro.
|
||||
|
||||
|
||||
=== Constructors
|
||||
|
||||
==== Default Constructor
|
||||
@@ -514,21 +491,6 @@ Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from unordered_flat_map
|
||||
|
||||
```c++
|
||||
concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#unordered_flat_map[`unordered_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; O(`bucket_count()`)
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -673,9 +635,8 @@ Concurrency:;; Blocking on `*this` and `other`.
|
||||
==== Move Assignment
|
||||
```c++
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -759,50 +720,6 @@ Unsequenced execution policies are not allowed.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class F> bool visit_while(F f);
|
||||
template<class F> bool visit_while(F f) const;
|
||||
template<class F> bool cvisit_while(F f) const;
|
||||
```
|
||||
|
||||
Successively invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
|
||||
---
|
||||
|
||||
==== Parallel [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
|
||||
Invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
Execution is parallelized according to the semantics of the execution policy specified.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`.
|
||||
Notes:;; Only available in compilers supporting C++17 parallel algorithms. +
|
||||
+
|
||||
These overloads only participate in overload resolution if `std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>` is `true`. +
|
||||
+
|
||||
Unsequenced execution policies are not allowed. +
|
||||
+
|
||||
Parallelization implies that execution does not necessary finish as soon as `f` returns `false`, and as a result
|
||||
`f` may be invoked with further elements for which the return value is also `false`.
|
||||
|
||||
---
|
||||
|
||||
=== Size and Capacity
|
||||
|
||||
==== empty
|
||||
@@ -1519,33 +1436,3 @@ Equivalent to
|
||||
-----
|
||||
c.xref:#concurrent_flat_map_erase_if[erase_if](pred);
|
||||
-----
|
||||
|
||||
=== Serialization
|
||||
|
||||
``concurrent_flat_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an concurrent_flat_map to an archive
|
||||
|
||||
Saves all the elements of a `concurrent_flat_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
Concurrency:;; Blocking on `x`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an concurrent_flat_map from an archive
|
||||
|
||||
Deletes all preexisting elements of a `concurrent_flat_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `concurrent_flat_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Concurrency:;; Blocking on `x`.
|
||||
|
||||
@@ -44,8 +44,7 @@ boost::unordered_flat_map
|
||||
|
||||
^.^h|*Concurrent*
|
||||
^|
|
||||
^| `boost::concurrent_flat_set` +
|
||||
`boost::concurrent_flat_map`
|
||||
^| `boost::concurrent_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
@@ -57,8 +56,9 @@ in the market within the technical constraints imposed by the required standard
|
||||
interface to accommodate the implementation.
|
||||
There are two variants: **flat** (the fastest) and **node-based**, which
|
||||
provide pointer stability under rehashing at the expense of being slower.
|
||||
* Finally, **concurrent containers** are designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Their interface is radically different from that of regular C++ containers.
|
||||
* Finally, `boost::concurrent_flat_map` (the only **concurrent container** provided
|
||||
at present) is a hashmap designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Its interface is radically different from that of regular C++ containers.
|
||||
|
||||
All sets and maps in Boost.Unordered are instantiatied similarly as
|
||||
`std::unordered_set` and `std::unordered_map`, respectively:
|
||||
@@ -73,7 +73,6 @@ namespace boost {
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set
|
||||
// and concurrent_flat_set
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
|
||||
@@ -121,21 +121,20 @@ for Visual Studio on an x64-mode Intel CPU with SSE2 and for GCC on an IBM s390x
|
||||
== Concurrent Containers
|
||||
|
||||
The same data structure used by Boost.Unordered open-addressing containers has been chosen
|
||||
also as the foundation of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`:
|
||||
also as the foundation of `boost::concurrent_flat_map`:
|
||||
|
||||
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
|
||||
concurrent scenarios.
|
||||
* Open-addressing layouts are eminently suitable for concurrent access and modification
|
||||
with minimal locking. In particular, the metadata array can be used for implementations of
|
||||
lookup that are lock-free up to the last step of actual element comparison.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for
|
||||
xref:#concurrent_interoperability_with_non_concurrent_containers[fast transfer]
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`,
|
||||
and vice versa.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for fast transfer
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`.
|
||||
(This feature has not been implemented yet.)
|
||||
|
||||
=== Hash Function and Platform Interoperability
|
||||
|
||||
Concurrent containers make the same decisions and provide the same guarantees
|
||||
`boost::concurrent_flat_map` makes the same decisions and provides the same guarantees
|
||||
as Boost.Unordered open-addressing containers with regards to
|
||||
xref:#rationale_hash_function[hash function defaults] and
|
||||
xref:#rationale_platform_interoperability[platform interoperability].
|
||||
|
||||
@@ -11,4 +11,3 @@ include::unordered_flat_set.adoc[]
|
||||
include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
include::concurrent_flat_map.adoc[]
|
||||
include::concurrent_flat_set.adoc[]
|
||||
|
||||
@@ -67,8 +67,8 @@ xref:#rationale_closed_addressing_containers[corresponding section].
|
||||
|
||||
== Open-addressing Containers
|
||||
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unordered_node_set` and
|
||||
`boost:unordered_flat_map`/`unordered_node_map`.
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_map`/`unordered_node_map` and
|
||||
`boost:unordered_flat_set`/`unordered_node_set`.
|
||||
|
||||
|
||||
[#img-foa-layout]
|
||||
@@ -76,7 +76,7 @@ The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unor
|
||||
image::foa.png[align=center]
|
||||
|
||||
As with all open-addressing containers, elements (or pointers to the element nodes in the case of
|
||||
`boost::unordered_node_set` and `boost::unordered_node_map`) are stored directly in the bucket array.
|
||||
`boost::unordered_node_map` and `boost::unordered_node_set`) are stored directly in the bucket array.
|
||||
This array is logically divided into 2^_n_^ _groups_ of 15 elements each.
|
||||
In addition to the bucket array, there is an associated _metadata array_ with 2^_n_^
|
||||
16-byte words.
|
||||
@@ -129,7 +129,7 @@ xref:#rationale_open_addresing_containers[corresponding section].
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
|
||||
`boost::concurrent_flat_map` uses the basic
|
||||
xref:#structures_open_addressing_containers[open-addressing layout] described above
|
||||
augmented with synchronization mechanisms.
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ namespace boost {
|
||||
explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a);
|
||||
xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -97,10 +96,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
|
||||
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -313,7 +311,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -473,22 +472,6 @@ from `other`, and the allocator is copy-constructed from `a`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_flat_map
|
||||
|
||||
```c++
|
||||
unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_flat_map[`concurrent_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -632,9 +615,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_map& operator=(unordered_flat_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1326,7 +1308,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
@@ -1465,54 +1447,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_flat_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_flat_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_flat_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_flat_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_flat_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_flat_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_flat_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_flat_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace boost {
|
||||
xref:#unordered_flat_set_iterator_range_constructor_with_allocator[unordered_flat_set](InputIterator f, InputIterator l, const allocator_type& a);
|
||||
explicit xref:#unordered_flat_set_allocator_constructor[unordered_flat_set](const Allocator& a);
|
||||
xref:#unordered_flat_set_copy_constructor_with_allocator[unordered_flat_set](const unordered_flat_set& other, const Allocator& a);
|
||||
xref:#unordered_flat_set_move_constructor_from_concurrent_flat_set[unordered_flat_set](concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_flat_set_move_constructor_with_allocator[unordered_flat_set](unordered_flat_set&& other, const Allocator& a);
|
||||
xref:#unordered_flat_set_initializer_list_constructor[unordered_flat_set](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -91,10 +91,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
|
||||
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -262,7 +261,8 @@ and https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the con
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -422,22 +422,6 @@ from `other`, and the allocator is copy-constructed from `a`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_flat_set
|
||||
|
||||
```c++
|
||||
unordered_flat_set(concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_flat_set[`concurrent_flat_set`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -581,9 +565,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_set& operator=(unordered_flat_set&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1104,7 +1087,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
@@ -1218,54 +1201,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_flat_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_flat_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_flat_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_flat_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_flat_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_flat_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_flat_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_flat_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -323,12 +323,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_map``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
@@ -1830,59 +1825,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
|
||||
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -290,13 +290,6 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_multimap``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
|
||||
=== Typedefs
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
@@ -1559,59 +1552,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_multimap``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_multimap to an archive
|
||||
|
||||
Saves all the elements of an `unordered_multimap` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_multimap from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_multimap` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_multimap` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
|
||||
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_multimap` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_multimap` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -278,12 +278,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_multiset``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
@@ -1490,58 +1485,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_multiset``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_multiset to an archive
|
||||
|
||||
Saves all the elements of an `unordered_multiset` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_multiset from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_multiset` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_multiset` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_multiset` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_multiset` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -95,10 +95,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_map_destructor[~unordered_node_map]();
|
||||
unordered_node_map& xref:#unordered_node_map_copy_assignment[operator++=++](const unordered_node_map& other);
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_map& xref:#unordered_node_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -315,7 +314,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -649,9 +649,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_map& operator=(unordered_node_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1407,7 +1406,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
@@ -1546,57 +1545,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_node_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_node_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_node_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_node_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_node_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_node_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `key_type` and `mapped_type` are constructible from
|
||||
`std::remove_const<key_type>::type&&` and `std::remove_const<mapped_type>::type&&`,
|
||||
respectively.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_node_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_node_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -90,10 +90,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_set_destructor[~unordered_node_set]();
|
||||
unordered_node_set& xref:#unordered_node_set_copy_assignment[operator++=++](const unordered_node_set& other);
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_set& xref:#unordered_node_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -265,7 +264,8 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -602,9 +602,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_set& operator=(unordered_node_set&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1189,7 +1188,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
@@ -1303,55 +1302,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_node_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_node_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_node_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_node_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_node_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_node_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_node_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_node_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -279,12 +279,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_set``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
@@ -1555,58 +1550,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Fast open-addressing concurrent hashmap.
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
@@ -12,21 +12,71 @@
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_map_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_map_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/type_traits/type_identity.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
|
||||
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
|
||||
"The provided Callable must be invocable with value_type&");
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
|
||||
static_assert( \
|
||||
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
|
||||
"The provided Callable must be invocable with value_type const&");
|
||||
|
||||
#if BOOST_CXX_VERSION >= 202002L
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced."); \
|
||||
static_assert( \
|
||||
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_COMMA ,
|
||||
|
||||
#define BOOST_UNORDERED_LAST_ARG(Arg, Args) \
|
||||
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_COMMA Args> >
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args))
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_LAST_ARG(Arg, 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> >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Allocator>
|
||||
class concurrent_flat_map
|
||||
{
|
||||
@@ -34,16 +84,10 @@ namespace boost {
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_flat_map;
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class unordered_flat_map;
|
||||
|
||||
using type_policy = detail::foa::flat_map_types<Key, T>;
|
||||
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
|
||||
|
||||
template <class K, class V, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
|
||||
@@ -53,11 +97,6 @@ namespace boost {
|
||||
friend typename concurrent_flat_map<K, V, H, KE, A>::size_type erase_if(
|
||||
concurrent_flat_map<K, V, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
@@ -184,13 +223,6 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
concurrent_flat_map(
|
||||
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_flat_map() = default;
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
|
||||
@@ -199,8 +231,10 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
|
||||
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
@@ -244,14 +278,6 @@ namespace boost {
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template<typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t visit(FwdIterator first,FwdIterator last,F f)const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
@@ -329,56 +355,6 @@ namespace boost {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
@@ -435,7 +411,6 @@ namespace boost {
|
||||
BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
|
||||
-> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::forward<Ty>(value), f);
|
||||
}
|
||||
|
||||
@@ -490,7 +465,7 @@ namespace boost {
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
@@ -687,7 +662,7 @@ namespace boost {
|
||||
float max_load_factor() const noexcept
|
||||
{
|
||||
return table_.max_load_factor();
|
||||
}
|
||||
};
|
||||
void max_load_factor(float) {}
|
||||
size_type max_load() const noexcept { return table_.max_load(); }
|
||||
|
||||
@@ -736,13 +711,6 @@ namespace boost {
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
@@ -839,4 +807,12 @@ namespace boost {
|
||||
using unordered::concurrent_flat_map;
|
||||
} // namespace boost
|
||||
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY
|
||||
#undef BOOST_UNORDERED_COMMA
|
||||
#undef BOOST_UNORDERED_LAST_ARG
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Fast open-addressing concurrent hashmap.
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
@@ -1,697 +0,0 @@
|
||||
/* Fast open-addressing concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_set_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_set_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/type_traits/type_identity.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class Hash, class Pred, class Allocator>
|
||||
class concurrent_flat_set
|
||||
{
|
||||
private:
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_flat_set;
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class unordered_flat_set;
|
||||
|
||||
using type_policy = detail::foa::flat_set_types<Key>;
|
||||
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
|
||||
|
||||
template <class K, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_flat_set<K, H, KE, A> const& lhs,
|
||||
concurrent_flat_set<K, H, KE, A> const& rhs);
|
||||
|
||||
template <class K, class H, class KE, class A, class Predicate>
|
||||
friend typename concurrent_flat_set<K, H, KE, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_flat_set<K, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = typename type_policy::value_type;
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::type_identity<Allocator>::type;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
using const_pointer =
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
|
||||
concurrent_flat_set()
|
||||
: concurrent_flat_set(detail::foa::default_bucket_count)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_set(size_type n, const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_set(InputIterator f, InputIterator l,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
this->insert(f, l);
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set const& rhs)
|
||||
: table_(rhs.table_,
|
||||
boost::allocator_select_on_container_copy_construction(
|
||||
rhs.get_allocator()))
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set&& rhs)
|
||||
: table_(std::move(rhs.table_))
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_set(
|
||||
InputIterator f, InputIterator l, allocator_type const& a)
|
||||
: concurrent_flat_set(f, l, 0, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_set(allocator_type const& a)
|
||||
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
concurrent_flat_set const& rhs, allocator_type const& a)
|
||||
: table_(rhs.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set&& rhs, allocator_type const& a)
|
||||
: table_(std::move(rhs.table_), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: concurrent_flat_set(n, hf, eql, a)
|
||||
{
|
||||
this->insert(il.begin(), il.end());
|
||||
}
|
||||
|
||||
concurrent_flat_set(size_type n, const allocator_type& a)
|
||||
: concurrent_flat_set(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
size_type n, const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_set(
|
||||
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
|
||||
: concurrent_flat_set(f, l, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_set(InputIterator f, InputIterator l, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(f, l, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
std::initializer_list<value_type> il, const allocator_type& a)
|
||||
: concurrent_flat_set(
|
||||
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
|
||||
const allocator_type& a)
|
||||
: concurrent_flat_set(il, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(il, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
concurrent_flat_set(
|
||||
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_flat_set() = default;
|
||||
|
||||
concurrent_flat_set& operator=(concurrent_flat_set const& rhs)
|
||||
{
|
||||
table_ = rhs.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_set& operator=(concurrent_flat_set&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_set& operator=(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
table_ = ilist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
size_type max_size() const noexcept { return table_.max_size(); }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
cvisit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type cvisit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_all(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
cvisit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.cvisit_all(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type const& obj)
|
||||
{
|
||||
return table_.insert(obj);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type&& obj)
|
||||
{
|
||||
return table_.insert(std::move(obj));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert(K&& k)
|
||||
{
|
||||
return table_.try_emplace(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_cvisit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
erase(K&& k)
|
||||
{
|
||||
return table_.erase(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
|
||||
{
|
||||
return table_.erase_if(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value &&
|
||||
!detail::is_execution_policy<K>::value,
|
||||
size_type>::type
|
||||
erase_if(K&& k, F f)
|
||||
{
|
||||
return table_.erase_if(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
erase_if(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.erase_if(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
|
||||
|
||||
void swap(concurrent_flat_set& other) noexcept(
|
||||
boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
|
||||
{
|
||||
return table_.swap(other.table_);
|
||||
}
|
||||
|
||||
void clear() noexcept { table_.clear(); }
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>& x)
|
||||
{
|
||||
BOOST_ASSERT(get_allocator() == x.get_allocator());
|
||||
return table_.merge(x.table_);
|
||||
}
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>&& x)
|
||||
{
|
||||
return merge(x);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type count(key_type const& k) const
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
count(K const& k)
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool contains(key_type const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
contains(K const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
/// Hash Policy
|
||||
///
|
||||
size_type bucket_count() const noexcept { return table_.capacity(); }
|
||||
|
||||
float load_factor() const noexcept { return table_.load_factor(); }
|
||||
float max_load_factor() const noexcept
|
||||
{
|
||||
return table_.max_load_factor();
|
||||
}
|
||||
void max_load_factor(float) {}
|
||||
size_type max_load() const noexcept { return table_.max_load(); }
|
||||
|
||||
void rehash(size_type n) { table_.rehash(n); }
|
||||
void reserve(size_type n) { table_.reserve(n); }
|
||||
|
||||
/// Observers
|
||||
///
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
}
|
||||
|
||||
hasher hash_function() const { return table_.hash_function(); }
|
||||
key_equal key_eq() const { return table_.key_eq(); }
|
||||
};
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return lhs.table_ == rhs.table_;
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)))
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_flat_set<K, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
class Hash =
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Pred =
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Allocator = std::allocator<
|
||||
typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Hash = boost::hash<T>,
|
||||
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T>,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_flat_set< T, Hash, Pred, Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T>, std::size_t, Allocator)
|
||||
-> concurrent_flat_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T >, Allocator)
|
||||
-> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Hash, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
|
||||
-> concurrent_flat_set<T, Hash, std::equal_to<T>, Allocator>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using unordered::concurrent_flat_set;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
@@ -1,55 +0,0 @@
|
||||
/* Fast open-addressing concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key> >
|
||||
class concurrent_flat_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_flat_set;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
@@ -1,72 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/addressof.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* constructs a stack-based object from a serialization archive */
|
||||
|
||||
template<typename T>
|
||||
struct archive_constructed:private noncopyable
|
||||
{
|
||||
template<class Archive>
|
||||
archive_constructed(const char* name,Archive& ar,unsigned int version)
|
||||
{
|
||||
core::load_construct_data_adl(ar,boost::addressof(get()),version);
|
||||
BOOST_TRY{
|
||||
ar>>core::make_nvp(name,get());
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
get().~T();
|
||||
BOOST_RETHROW;
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
|
||||
~archive_constructed()
|
||||
{
|
||||
get().~T();
|
||||
}
|
||||
|
||||
#if defined(BOOST_GCC)&&(BOOST_GCC>=4*10000+6*100)
|
||||
#define BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
T& get(){return *reinterpret_cast<T*>(&space);}
|
||||
|
||||
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
|
||||
#pragma GCC diagnostic pop
|
||||
#undef BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
|
||||
#endif
|
||||
|
||||
private:
|
||||
typename aligned_storage<sizeof(T),alignment_of<T>::value>::type space;
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
struct bad_archive_exception:std::runtime_error
|
||||
{
|
||||
bad_archive_exception():std::runtime_error("Invalid or corrupted archive"){}
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
|
||||
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
|
||||
"The provided Callable must be invocable with value_type&");
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
|
||||
static_assert( \
|
||||
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
|
||||
"The provided Callable must be invocable with value_type const&");
|
||||
|
||||
#if BOOST_CXX_VERSION >= 202002L
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced."); \
|
||||
static_assert( \
|
||||
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_COMMA ,
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args) \
|
||||
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_DETAIL_COMMA Args> >
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, 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> >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
@@ -114,18 +114,16 @@ to normal separate chaining implementations.
|
||||
*/
|
||||
|
||||
#include <boost/unordered/detail/prime_fmod.hpp>
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
|
||||
#include <boost/core/addressof.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/bit.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/core/invoke_swap.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/move/core.hpp>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/swap.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
|
||||
@@ -289,25 +287,6 @@ namespace boost {
|
||||
p = pbg->buckets + x;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
friend void serialization_track(
|
||||
Archive& ar, grouped_bucket_iterator const& x)
|
||||
{
|
||||
// requires: not at end() position
|
||||
track_address(ar, x.p);
|
||||
track_address(ar, x.pbg);
|
||||
}
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar,unsigned int)
|
||||
{
|
||||
// requires: not at end() position
|
||||
serialize_tracked_address(ar, p);
|
||||
serialize_tracked_address(ar, pbg);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Node> struct const_grouped_local_bucket_iterator;
|
||||
@@ -651,7 +630,7 @@ namespace boost {
|
||||
bool b = boost::allocator_propagate_on_container_swap<
|
||||
allocator_type>::type::value;
|
||||
if (b) {
|
||||
boost::core::invoke_swap(get_node_allocator(), other.get_node_allocator());
|
||||
boost::swap(get_node_allocator(), other.get_node_allocator());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,15 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/archive_constructed.hpp>
|
||||
#include <boost/unordered/detail/bad_archive_exception.hpp>
|
||||
#include <boost/unordered/detail/foa/core.hpp>
|
||||
#include <boost/unordered/detail/foa/reentrancy_check.hpp>
|
||||
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
|
||||
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
|
||||
#include <boost/unordered/detail/serialization_version.hpp>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
@@ -248,95 +241,63 @@ group_access* dummy_group_accesses()
|
||||
|
||||
/* subclasses table_arrays to add an additional group_access array */
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
template<typename Value,typename Group,typename SizePolicy>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
|
||||
{
|
||||
using group_access_allocator_type=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using group_access_pointer=
|
||||
typename boost::allocator_pointer<group_access_allocator_type>::type;
|
||||
using super=table_arrays<Value,Group,SizePolicy>;
|
||||
|
||||
using super=table_arrays<Value,Group,SizePolicy,Allocator>;
|
||||
concurrent_table_arrays(const super& arrays,group_access *pga):
|
||||
super{arrays},group_accesses{pga}{}
|
||||
|
||||
concurrent_table_arrays(const super& arrays,group_access_pointer pga):
|
||||
super{arrays},group_accesses_{pga}{}
|
||||
|
||||
group_access* group_accesses()const noexcept{
|
||||
return boost::to_address(group_accesses_);
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_(
|
||||
group_access_allocator_type al,std::size_t n)
|
||||
template<typename Allocator>
|
||||
static concurrent_table_arrays new_(Allocator& al,std::size_t n)
|
||||
{
|
||||
super x{super::new_(al,n)};
|
||||
BOOST_TRY{
|
||||
return new_group_access(al,x);
|
||||
concurrent_table_arrays arrays{super::new_(al,n),nullptr};
|
||||
if(!arrays.elements){
|
||||
arrays.group_accesses=dummy_group_accesses<SizePolicy::min_size()>();
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
super::delete_(al,x);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
else{
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)
|
||||
{
|
||||
set_group_access(
|
||||
al,arrays,std::is_same<group_access*,group_access_pointer>{});
|
||||
}
|
||||
BOOST_TRY{
|
||||
auto aal=access_alloc(al);
|
||||
arrays.group_accesses=boost::to_address(
|
||||
access_traits::allocate(aal,arrays.groups_size_mask+1));
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::false_type /* fancy pointers */)
|
||||
{
|
||||
arrays.group_accesses_=
|
||||
boost::allocator_allocate(al,arrays.groups_size_mask+1);
|
||||
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses()+i) group_access();
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses+i) group_access();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::true_type /* optimize when elements() is null */)
|
||||
{
|
||||
if(!arrays.elements()){
|
||||
arrays.group_accesses_=
|
||||
dummy_group_accesses<SizePolicy::min_size()>();
|
||||
} else {
|
||||
set_group_access(al,arrays,std::false_type{});
|
||||
BOOST_CATCH(...){
|
||||
super::delete_(al,arrays);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_group_access(
|
||||
group_access_allocator_type al,const super& x)
|
||||
{
|
||||
concurrent_table_arrays arrays{x,nullptr};
|
||||
set_group_access(al,arrays);
|
||||
return arrays;
|
||||
}
|
||||
|
||||
static void delete_(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
template<typename Allocator>
|
||||
static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
delete_group_access(al,arrays);
|
||||
if(arrays.elements){
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
using pointer=typename access_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
|
||||
auto aal=access_alloc(al);
|
||||
access_traits::deallocate(
|
||||
aal,pointer_traits::pointer_to(*arrays.group_accesses),
|
||||
arrays.groups_size_mask+1);
|
||||
}
|
||||
super::delete_(al,arrays);
|
||||
}
|
||||
|
||||
static void delete_group_access(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
if(arrays.elements()){
|
||||
boost::allocator_deallocate(
|
||||
al,arrays.group_accesses_,arrays.groups_size_mask+1);
|
||||
}
|
||||
}
|
||||
|
||||
group_access_pointer group_accesses_;
|
||||
group_access *group_accesses;
|
||||
};
|
||||
|
||||
struct atomic_size_control
|
||||
@@ -346,7 +307,7 @@ struct atomic_size_control
|
||||
|
||||
atomic_size_control(std::size_t ml_,std::size_t size_):
|
||||
pad0_{},ml{ml_},pad1_{},size{size_}{}
|
||||
atomic_size_control(const atomic_size_control& x):
|
||||
atomic_size_control(atomic_size_control& x):
|
||||
pad0_{},ml{x.ml.load()},pad1_{},size{x.size.load()}{}
|
||||
|
||||
/* padding to avoid false sharing internally and with sorrounding data */
|
||||
@@ -374,7 +335,9 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
}
|
||||
|
||||
/* foa::concurrent_table serves as the foundation for end-user concurrent
|
||||
* hash containers.
|
||||
* hash containers. The TypePolicy parameter can specify flat/node-based
|
||||
* map-like and set-like containers, though currently we're only providing
|
||||
* boost::concurrent_flat_map.
|
||||
*
|
||||
* The exposed interface (completed by the wrapping containers) is not that
|
||||
* of a regular container (in fact, it does not model Container as understood
|
||||
@@ -396,7 +359,7 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
|
||||
* on C++17 stdlib parallel algorithms.
|
||||
*
|
||||
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
|
||||
* Consult boost::unordered_flat_map docs for the full API reference.
|
||||
* Heterogeneous lookup is suported by default, that is, without checking for
|
||||
* any ::is_transparent typedefs --this checking is done by the wrapping
|
||||
* containers.
|
||||
@@ -424,13 +387,10 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* reduced hash value is set) and the insertion counter is atomically
|
||||
* incremented: if no other thread has incremented the counter during the
|
||||
* whole operation (which is checked by comparing with c0), then we're
|
||||
* good to go and complete the insertion, otherwise we roll back and
|
||||
* start over.
|
||||
* good to go and complete the insertion, otherwise we roll back and start
|
||||
* over.
|
||||
*/
|
||||
|
||||
template<typename,typename,typename,typename>
|
||||
class table; /* concurrent/non-concurrent interop */
|
||||
|
||||
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
using concurrent_table_core_impl=table_core<
|
||||
TypePolicy,group15<atomic_integral>,concurrent_table_arrays,
|
||||
@@ -452,10 +412,10 @@ class concurrent_table:
|
||||
using group_type=typename super::group_type;
|
||||
using super::N;
|
||||
using prober=typename super::prober;
|
||||
using arrays_type=typename super::arrays_type;
|
||||
using size_ctrl_type=typename super::size_ctrl_type;
|
||||
using compatible_nonconcurrent_table=table<TypePolicy,Hash,Pred,Allocator>;
|
||||
friend compatible_nonconcurrent_table;
|
||||
|
||||
template<
|
||||
typename TypePolicy2,typename Hash2,typename Pred2,typename Allocator2>
|
||||
friend class concurrent_table;
|
||||
|
||||
public:
|
||||
using key_type=typename super::key_type;
|
||||
@@ -490,30 +450,6 @@ public:
|
||||
concurrent_table(x,al_,x.exclusive_access()){}
|
||||
concurrent_table(concurrent_table&& x,const Allocator& al_):
|
||||
concurrent_table(std::move(x),al_,x.exclusive_access()){}
|
||||
|
||||
template<typename ArraysType>
|
||||
concurrent_table(
|
||||
compatible_nonconcurrent_table&& x,
|
||||
arrays_holder<ArraysType,Allocator>&& ah):
|
||||
super{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
[&x]{return arrays_type::new_group_access(
|
||||
x.al(),typename arrays_type::super{
|
||||
x.arrays.groups_size_index,x.arrays.groups_size_mask,
|
||||
to_pointer<typename arrays_type::group_type_pointer>(
|
||||
reinterpret_cast<group_type*>(x.arrays.groups())),
|
||||
x.arrays.elements_});},
|
||||
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
{
|
||||
x.arrays=ah.release();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
concurrent_table(compatible_nonconcurrent_table&& x):
|
||||
concurrent_table(std::move(x),x.make_empty_arrays())
|
||||
{}
|
||||
|
||||
~concurrent_table()=default;
|
||||
|
||||
concurrent_table& operator=(const concurrent_table& x)
|
||||
@@ -523,8 +459,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_table& operator=(concurrent_table&& x)noexcept(
|
||||
noexcept(std::declval<super&>() = std::declval<super&&>()))
|
||||
concurrent_table& operator=(concurrent_table&& x)
|
||||
{
|
||||
auto lck=exclusive_access(*this,x);
|
||||
super::operator=(std::move(x));
|
||||
@@ -565,97 +500,6 @@ public:
|
||||
return visit(x,std::forward<F>(f));
|
||||
}
|
||||
|
||||
static constexpr std::size_t bulk_visit_size=16;
|
||||
|
||||
#if 0
|
||||
#define BOOST_UNORDERED_BULK_VISIT_CASE(r,b) \
|
||||
case r: \
|
||||
res+=unprotected_bulk_visit<b>( \
|
||||
group_shared{},first,std::forward<F>(f)); \
|
||||
break;
|
||||
|
||||
template<typename FwdIterator,typename F>
|
||||
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
|
||||
{
|
||||
BOOST_STATIC_ASSERT(bulk_visit_size==16);
|
||||
|
||||
std::size_t res=0;
|
||||
auto n=static_cast<std::size_t>(std::distance(first,last));
|
||||
auto m=n/bulk_visit_size;
|
||||
auto lck=shared_access();
|
||||
|
||||
if(m){
|
||||
for(std::size_t i=m-1;i--;){
|
||||
res+=unprotected_bulk_visit<bulk_visit_size>(
|
||||
group_shared{},first,std::forward<F>(f));
|
||||
std::advance(first,bulk_visit_size);
|
||||
}
|
||||
|
||||
switch(n-m*bulk_visit_size){
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(0,bulk_visit_size)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(1,bulk_visit_size+1)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(2,bulk_visit_size+2)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(3,bulk_visit_size+3)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(4,bulk_visit_size+4)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(5,bulk_visit_size+5)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(6,bulk_visit_size+6)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(7,bulk_visit_size+7)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(8,bulk_visit_size+8)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(9,bulk_visit_size+9)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(10,bulk_visit_size+10)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(11,bulk_visit_size+11)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(12,bulk_visit_size+12)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(13,bulk_visit_size+13)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(14,bulk_visit_size+14)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(15,bulk_visit_size+15)
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(n){
|
||||
case 0:break;
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(1,1)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(2,2)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(3,3)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(4,4)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(5,5)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(6,6)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(7,7)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(8,8)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(9,9)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(10,10)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(11,11)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(12,12)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(13,13)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(14,14)
|
||||
BOOST_UNORDERED_BULK_VISIT_CASE(15,15)
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#undef BOOST_UNORDERED_BULK_VISIT_CASE
|
||||
#else
|
||||
template<typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
|
||||
{
|
||||
std::size_t res=0;
|
||||
auto n=static_cast<std::size_t>(std::distance(first,last));
|
||||
auto lck=shared_access();
|
||||
|
||||
while(n){
|
||||
auto m=n<2*bulk_visit_size?n:bulk_visit_size;
|
||||
res+=unprotected_bulk_visit(group_shared{},first,m,std::forward<F>(f));
|
||||
n-=m;
|
||||
std::advance(first,m);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename F> std::size_t visit_all(F&& f)
|
||||
{
|
||||
return visit_all_impl(group_exclusive{},std::forward<F>(f));
|
||||
@@ -695,46 +539,6 @@ case r: \
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename F> bool visit_while(F&& f)
|
||||
{
|
||||
return visit_while_impl(group_exclusive{},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> bool visit_while(F&& f)const
|
||||
{
|
||||
return visit_while_impl(group_shared{},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> bool cvisit_while(F&& f)const
|
||||
{
|
||||
return visit_while(std::forward<F>(f));
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool visit_while(ExecutionPolicy&& policy,F&& f)
|
||||
{
|
||||
return visit_while_impl(
|
||||
group_exclusive{},
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool visit_while(ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
return visit_while_impl(
|
||||
group_shared{},
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool cvisit_while(ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
return visit_while(
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool empty()const noexcept{return size()==0;}
|
||||
|
||||
std::size_t size()const noexcept
|
||||
@@ -1031,14 +835,11 @@ case r: \
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename,typename,typename,typename> friend class concurrent_table;
|
||||
|
||||
using mutex_type=rw_spinlock;
|
||||
using multimutex_type=multimutex<mutex_type,128>; // TODO: adapt 128 to the machine
|
||||
using shared_lock_guard=reentrancy_checked<shared_lock<mutex_type>>;
|
||||
using exclusive_lock_guard=reentrancy_checked<lock_guard<multimutex_type>>;
|
||||
using exclusive_bilock_guard=
|
||||
reentrancy_bichecked<scoped_bilock<multimutex_type>>;
|
||||
using shared_lock_guard=shared_lock<mutex_type>;
|
||||
using exclusive_lock_guard=lock_guard<multimutex_type>;
|
||||
using exclusive_bilock_guard=scoped_bilock<multimutex_type>;
|
||||
using group_shared_lock_guard=typename group_access::shared_lock_guard;
|
||||
using group_exclusive_lock_guard=typename group_access::exclusive_lock_guard;
|
||||
using group_insert_counter_type=typename group_access::insert_counter_type;
|
||||
@@ -1058,18 +859,18 @@ private:
|
||||
{
|
||||
thread_local auto id=(++thread_counter)%mutexes.size();
|
||||
|
||||
return shared_lock_guard{this,mutexes[id]};
|
||||
return shared_lock_guard{mutexes[id]};
|
||||
}
|
||||
|
||||
inline exclusive_lock_guard exclusive_access()const
|
||||
{
|
||||
return exclusive_lock_guard{this,mutexes};
|
||||
return exclusive_lock_guard{mutexes};
|
||||
}
|
||||
|
||||
static inline exclusive_bilock_guard exclusive_access(
|
||||
const concurrent_table& x,const concurrent_table& y)
|
||||
{
|
||||
return {&x,&y,x.mutexes,y.mutexes};
|
||||
return {x.mutexes,y.mutexes};
|
||||
}
|
||||
|
||||
template<typename Hash2,typename Pred2>
|
||||
@@ -1077,7 +878,7 @@ private:
|
||||
const concurrent_table& x,
|
||||
const concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& y)
|
||||
{
|
||||
return {&x,&y,x.mutexes,y.mutexes};
|
||||
return {x.mutexes,y.mutexes};
|
||||
}
|
||||
|
||||
/* Tag-dispatched shared/exclusive group access */
|
||||
@@ -1087,18 +888,18 @@ private:
|
||||
|
||||
inline group_shared_lock_guard access(group_shared,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].shared_access();
|
||||
return this->arrays.group_accesses[pos].shared_access();
|
||||
}
|
||||
|
||||
inline group_exclusive_lock_guard access(
|
||||
group_exclusive,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].exclusive_access();
|
||||
return this->arrays.group_accesses[pos].exclusive_access();
|
||||
}
|
||||
|
||||
inline group_insert_counter_type& insert_counter(std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].insert_counter();
|
||||
return this->arrays.group_accesses[pos].insert_counter();
|
||||
}
|
||||
|
||||
/* Const casts value_type& according to the level of group access for
|
||||
@@ -1169,29 +970,6 @@ private:
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
bool visit_while_impl(GroupAccessMode access_mode,F&& f)const
|
||||
{
|
||||
auto lck=shared_access();
|
||||
return for_all_elements_while(access_mode,[&](element_type* p){
|
||||
return f(cast_for(access_mode,type_policy::value_from(*p)));
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
|
||||
bool visit_while_impl(
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
auto lck=shared_access();
|
||||
return for_all_elements_while(
|
||||
access_mode,std::forward<ExecutionPolicy>(policy),
|
||||
[&](element_type* p){
|
||||
return f(cast_for(access_mode,type_policy::value_from(*p)));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename GroupAccessMode,typename Key,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_visit(
|
||||
GroupAccessMode access_mode,
|
||||
@@ -1217,10 +995,10 @@ private:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
@@ -1241,153 +1019,6 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<
|
||||
std::size_t M,typename GroupAccessMode,
|
||||
typename FwdIterator,typename F
|
||||
>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t unprotected_bulk_visit(
|
||||
GroupAccessMode access_mode,FwdIterator first,F&& f)const
|
||||
{
|
||||
std::size_t res=0,
|
||||
hashes[M],
|
||||
positions[M];
|
||||
int masks[M];
|
||||
auto it=first;
|
||||
|
||||
for(std::size_t i=0;i<M;++i,++it){
|
||||
hashes[i]=this->hash_for(*it);
|
||||
auto pos=positions[i]=this->position_for(hashes[i]);
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.groups()+pos);
|
||||
}
|
||||
|
||||
for(std::size_t i=0;i<M;++i){
|
||||
auto hash=hashes[i];
|
||||
auto pos=positions[i];
|
||||
masks[i]=(this->arrays.groups()+pos)->match(hash);
|
||||
if(masks[i]){
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.group_accesses()+pos);
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(this->arrays.elements()+pos*N,N);
|
||||
}
|
||||
}
|
||||
|
||||
it=first;
|
||||
for(std::size_t i=0;i<M;++i,++it){
|
||||
auto pos=positions[i];
|
||||
prober pb(pos);
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=masks[i];
|
||||
element_type *p;
|
||||
if(mask){
|
||||
p=this->arrays.elements()+pos*N;
|
||||
goto post_prefetch;
|
||||
}
|
||||
else{
|
||||
goto post_mask;
|
||||
}
|
||||
do{
|
||||
pos=pb.get();
|
||||
pg=this->arrays.groups()+pos;
|
||||
mask=pg->match(hashes[i]);
|
||||
if(BOOST_UNLIKELY(mask!=0)){ /* unlikely bc we're past the 1st probe */
|
||||
p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
post_prefetch:
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(
|
||||
pg->is_occupied(n)&&
|
||||
bool(this->pred()(*it,this->key_from(p[n]))))){
|
||||
f(cast_for(access_mode,type_policy::value_from(p[n])));
|
||||
++res;
|
||||
goto next_key;
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
post_mask:
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hashes[i]))){
|
||||
goto next_key;
|
||||
}
|
||||
}
|
||||
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
|
||||
next_key:;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
template<typename GroupAccessMode,typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_bulk_visit(
|
||||
GroupAccessMode access_mode,FwdIterator first,std::size_t m,F&& f)const
|
||||
{
|
||||
BOOST_ASSERT(s<2*bulk_visit_size);
|
||||
|
||||
std::size_t res=0,
|
||||
hashes[2*bulk_visit_size-1],
|
||||
positions[2*bulk_visit_size-1];
|
||||
int masks[2*bulk_visit_size-1];
|
||||
auto it=first;
|
||||
|
||||
for(auto i=m;i--;++it){
|
||||
auto hash=hashes[i]=this->hash_for(*it);
|
||||
auto pos=positions[i]=this->position_for(hash);
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.groups()+pos);
|
||||
}
|
||||
|
||||
for(auto i=m;i--;){
|
||||
auto hash=hashes[i];
|
||||
auto pos=positions[i];
|
||||
auto mask=masks[i]=(this->arrays.groups()+pos)->match(hash);
|
||||
if(mask){
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.group_accesses()+pos);
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(this->arrays.elements()+pos*N,N);
|
||||
}
|
||||
}
|
||||
|
||||
it=first;
|
||||
for(auto i=m;i--;++it){
|
||||
auto pos=positions[i];
|
||||
prober pb(pos);
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=masks[i];
|
||||
element_type *p;
|
||||
if(!mask)goto post_mask;
|
||||
p=this->arrays.elements()+pos*N;
|
||||
for(;;){
|
||||
{
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(
|
||||
pg->is_occupied(n)&&
|
||||
bool(this->pred()(*it,this->key_from(p[n]))))){
|
||||
f(cast_for(access_mode,type_policy::value_from(p[n])));
|
||||
++res;
|
||||
goto next_key;
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
post_mask:
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hashes[i]))||
|
||||
BOOST_UNLIKELY(!pb.next(this->arrays.groups_size_mask))){
|
||||
goto next_key;
|
||||
}
|
||||
pos=pb.get();
|
||||
pg=this->arrays.groups()+pos;
|
||||
mask=pg->match(hashes[i]);
|
||||
if(BOOST_LIKELY(mask==0))goto next_key;
|
||||
p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
}
|
||||
next_key:;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4800 */
|
||||
#endif
|
||||
@@ -1580,7 +1211,7 @@ private:
|
||||
if(BOOST_LIKELY(rsize.succeeded())){
|
||||
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto lck=access(group_exclusive{},pos);
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
@@ -1590,7 +1221,7 @@ private:
|
||||
/* other thread inserted from pos0, need to start over */
|
||||
goto startover;
|
||||
}
|
||||
auto p=this->arrays.elements()+pos*N+n;
|
||||
auto p=this->arrays.elements+pos*N+n;
|
||||
this->construct_element(p,std::forward<Args>(args)...);
|
||||
rslot.commit();
|
||||
rsize.commit();
|
||||
@@ -1623,37 +1254,18 @@ private:
|
||||
auto for_all_elements(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),void())
|
||||
{
|
||||
for_all_elements_while(
|
||||
access_mode,[&](group_type* pg,unsigned int n,element_type* p)
|
||||
{f(pg,n,p);return true;});
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr),bool())
|
||||
{
|
||||
return for_all_elements_while(
|
||||
access_mode,[&](group_type*,unsigned int,element_type* p){return f(p);});
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),bool())
|
||||
{
|
||||
auto p=this->arrays.elements();
|
||||
if(p){
|
||||
for(auto pg=this->arrays.groups(),last=pg+this->arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups()));
|
||||
auto mask=this->match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(!f(pg,n,p+n))return false;
|
||||
mask&=mask-1;
|
||||
}
|
||||
auto p=this->arrays.elements;
|
||||
if(!p)return;
|
||||
for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups));
|
||||
auto mask=this->match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
f(pg,n,p+n);
|
||||
mask&=mask-1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
@@ -1672,15 +1284,15 @@ private:
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),void())
|
||||
{
|
||||
if(!this->arrays.elements())return;
|
||||
auto first=this->arrays.groups(),
|
||||
if(!this->arrays.elements)return;
|
||||
auto first=this->arrays.groups,
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
std::for_each(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
std::size_t pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
f(&g,n,p+n);
|
||||
@@ -1689,164 +1301,8 @@ private:
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
|
||||
bool for_all_elements_while(
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
{
|
||||
if(!this->arrays.elements())return true;
|
||||
auto first=this->arrays.groups(),
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
return std::all_of(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(!f(p+n))return false;
|
||||
mask&=mask-1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar,unsigned int version)
|
||||
{
|
||||
core::split_member(ar,*this,version);
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int version)const
|
||||
{
|
||||
save(
|
||||
ar,version,
|
||||
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int,std::true_type /* set */)const
|
||||
{
|
||||
auto lck=exclusive_access();
|
||||
const std::size_t s=super::size();
|
||||
const serialization_version<value_type> value_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("value_version",value_version);
|
||||
|
||||
super::for_all_elements([&,this](element_type* p){
|
||||
auto& x=type_policy::value_from(*p);
|
||||
core::save_construct_data_adl(ar,std::addressof(x),value_version);
|
||||
ar<<serialization::make_nvp("item",x);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int,std::false_type /* map */)const
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
const std::size_t s=super::size();
|
||||
const serialization_version<raw_key_type> key_version;
|
||||
const serialization_version<raw_mapped_type> mapped_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("key_version",key_version);
|
||||
ar<<core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
super::for_all_elements([&,this](element_type* p){
|
||||
/* To remain lib-independent from Boost.Serialization and not rely on
|
||||
* the user having included the serialization code for std::pair
|
||||
* (boost/serialization/utility.hpp), we serialize the key and the
|
||||
* mapped value separately.
|
||||
*/
|
||||
|
||||
auto& x=type_policy::value_from(*p);
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(x.first),key_version);
|
||||
ar<<serialization::make_nvp("key",x.first);
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(x.second),mapped_version);
|
||||
ar<<serialization::make_nvp("mapped",x.second);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int version)
|
||||
{
|
||||
load(
|
||||
ar,version,
|
||||
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int,std::true_type /* set */)
|
||||
{
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
serialization_version<value_type> value_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("value_version",value_version);
|
||||
|
||||
super::clear();
|
||||
super::reserve(s);
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<value_type> value("item",ar,value_version);
|
||||
auto& x=value.get();
|
||||
auto hash=this->hash_for(x);
|
||||
auto pos0=this->position_for(hash);
|
||||
|
||||
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
|
||||
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int,std::false_type /* map */)
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
serialization_version<raw_key_type> key_version;
|
||||
serialization_version<raw_mapped_type> mapped_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("key_version",key_version);
|
||||
ar>>core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
super::clear();
|
||||
super::reserve(s);
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<raw_key_type> key("key",ar,key_version);
|
||||
archive_constructed<raw_mapped_type> mapped("mapped",ar,mapped_version);
|
||||
auto& k=key.get();
|
||||
auto& m=mapped.get();
|
||||
auto hash=this->hash_for(k);
|
||||
auto pos0=this->position_for(hash);
|
||||
|
||||
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
|
||||
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
|
||||
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
|
||||
}
|
||||
}
|
||||
|
||||
static std::atomic<std::size_t> thread_counter;
|
||||
mutable multimutex_type mutexes;
|
||||
};
|
||||
|
||||