Compare commits
301 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64f60542ac | |||
| a757f3737b | |||
| 26ab9ff584 | |||
| 1e92d9f545 | |||
| 2cf72093b1 | |||
| 88d4d64edf | |||
| 7f7e577e77 | |||
| 80d7203d78 | |||
| 5dfed4deec | |||
| e08f9f11a1 | |||
| c304284773 | |||
| 0e8affcc07 | |||
| ba00dbd65a | |||
| c214fb44a3 | |||
| c704788718 | |||
| a91efeb237 | |||
| 7812b26d3a | |||
| 8bd07e17c3 | |||
| b3c0368ab5 | |||
| 37edc392a5 | |||
| fb403bc233 | |||
| 910b8de697 | |||
| 3aff995ae0 | |||
| 68c018fda6 | |||
| a9ad06139e | |||
| 9a0e5e9ea8 | |||
| b1a52d0236 | |||
| 1723358298 | |||
| 6bf0e93a41 | |||
| 0e5ef25a69 | |||
| ab84a922cf | |||
| d8bc38f154 | |||
| ecd4a82ed5 | |||
| 0898219edc | |||
| d9b62d24dc | |||
| b771278813 | |||
| 2226a7238d | |||
| 00c6a93a56 | |||
| 8ea1cbbd64 | |||
| 81a47ca88d | |||
| c54151769f | |||
| e73d5ecdd1 | |||
| 8a4e987030 | |||
| 8544d9f3c8 | |||
| ed80cb14e2 | |||
| 91eb2ddbd1 | |||
| 3bd28dc3d7 | |||
| e4d2da40f2 | |||
| a004e71dd0 | |||
| ddcab1c171 | |||
| e3cbf03f47 | |||
| f8fbbc3b76 | |||
| 52bf2bf7a2 | |||
| cf39fc4c38 | |||
| 1309361a02 | |||
| 946491489e | |||
| 19202d2a8d | |||
| 3be466966d | |||
| d22787c218 | |||
| b86dee9a18 | |||
| 863984a7c8 | |||
| e9b3ad4a5f | |||
| e00d700457 | |||
| 53e20a2a1b | |||
| af4cdf8fab | |||
| 7003e91d44 | |||
| e86bb5cce2 | |||
| 6a4728add0 | |||
| f02afbc815 | |||
| 6399dfd1e4 | |||
| f468fb77e0 | |||
| 5e316ebc88 | |||
| 0c90585511 | |||
| 13b4fd7133 | |||
| 27b4c62bd2 | |||
| 846de7ca82 | |||
| 62cf58d1ca | |||
| 4482031329 | |||
| 7b1af37b9c | |||
| 1c48f665ea | |||
| e4072747bb | |||
| ce674c8007 | |||
| 45ebc1e555 | |||
| 02c3bd4072 | |||
| 5f4172c13f | |||
| 40c5c6907a | |||
| 8dbe380d6c | |||
| 37ff9de0db | |||
| ae9f35073b | |||
| a85df27d4c | |||
| 88c70b885d | |||
| 240f5f94b1 | |||
| 7415721f7d | |||
| 8b5539756b | |||
| 49ac1035bb | |||
| d085c40541 | |||
| 99d5b9b1bb | |||
| ad86f9211b | |||
| d9515fdc2f | |||
| eedbc6bcf7 | |||
| 297fb2e8c4 | |||
| 99eab2dfb1 | |||
| 1070c9b69e | |||
| 9e6b5a7e43 | |||
| e1f11e840c | |||
| f2d4f1968b | |||
| 4a7116b996 | |||
| 41584e73d9 | |||
| 52453cb5a4 | |||
| 44b918896d | |||
| ff356ac083 | |||
| 3064801a89 | |||
| e9d9f19e76 | |||
| 9b85c38e37 | |||
| 0b2829c793 | |||
| a9c2d1daa5 | |||
| d29815b3a8 | |||
| 7ed7878278 | |||
| 4c927cd2f2 | |||
| 2fae05ed31 | |||
| 41abb96d83 | |||
| 63f51e7c41 | |||
| ba4f321934 | |||
| 68563fab72 | |||
| d8e9503869 | |||
| 86e998cbc7 | |||
| ce722e4d56 | |||
| 77eaab4803 | |||
| 96562f4c3b | |||
| d66ceaf7fe | |||
| 2e44769247 | |||
| e64fd45760 | |||
| 454fc7501e | |||
| a89c62728a | |||
| 1d8c0f91f8 | |||
| 25ba8e4dfc | |||
| fb4437219f | |||
| 0d1fdae84c | |||
| c5debf11cf | |||
| 9e4f89aa43 | |||
| 12935eb695 | |||
| 2386188d69 | |||
| e4b32f7f5e | |||
| 3df435d4d3 | |||
| ed076facc4 | |||
| b09c5bdcf0 | |||
| 6abb94bb68 | |||
| d2b1f095c8 | |||
| 3d34571654 | |||
| d683b3ac3e | |||
| e16a8737f4 | |||
| 8f348aea26 | |||
| 9a8ba2bdff | |||
| 3aaefdcc78 | |||
| ca59ed8c50 | |||
| ff78ef25c0 | |||
| ec487b5c6c | |||
| 0a03ed8170 | |||
| 5881dcc2b2 | |||
| 2070cbe2cc | |||
| 8198b9c57c | |||
| c35e9fc631 | |||
| f244ba55de | |||
| 5e225fe46c | |||
| 06535e518e | |||
| 81584c22cf | |||
| 83040211e8 | |||
| 4ed301df97 | |||
| 8010f506a6 | |||
| 840ea1ce5c | |||
| 447306bfb9 | |||
| 4569c1bec0 | |||
| 37ad547e18 | |||
| e037ee8d82 | |||
| 9bbadfdb34 | |||
| c1b63f68c8 | |||
| 0590e3bf8c | |||
| 8f17f5f548 | |||
| 3a8bea9e1d | |||
| b33b354818 | |||
| b08837b93e | |||
| b3b840df30 | |||
| 2ef47f0813 | |||
| 27f5513911 | |||
| a5100a9d35 | |||
| ccbc639677 | |||
| 18725a682d | |||
| dac1135199 | |||
| ead55a8938 | |||
| 8f39001ff0 | |||
| 8b056b902e | |||
| 5724adbbe9 | |||
| 1c5640cfbe | |||
| 4d982e133e | |||
| 1bf193b8fc | |||
| 7faec20f26 | |||
| ced8b45add | |||
| 15c9bc40f7 | |||
| 9aedb95296 | |||
| e56cb4418c | |||
| 1ae21c6d1a | |||
| 82acad3794 | |||
| c50461528c | |||
| bd08e49255 | |||
| dbba3cec7b | |||
| 6c5b5b3186 | |||
| 47dd02a4b3 | |||
| 8429d1a6aa | |||
| 86d3f9f632 | |||
| 48e92afd92 | |||
| 5a5c31de35 | |||
| 088941dc7b | |||
| 5f6f1691e5 | |||
| 45d6c07be6 | |||
| 5c63cd2bdb | |||
| 86318c1e88 | |||
| 0ff1fa0f6e | |||
| d90bf0c438 | |||
| dc63438e22 | |||
| 0a3ac26f00 | |||
| c64ed1caa3 | |||
| efc33b18be | |||
| c189d8d5e8 | |||
| 886227db3b | |||
| 719eb90d03 | |||
| fadb686bc5 | |||
| aebefaef0d | |||
| 63a564e8ff | |||
| 16c1593368 | |||
| 53580a3070 | |||
| d26aeed68f | |||
| 73eefd2b5d | |||
| 3df600d069 | |||
| a1adacdfe2 | |||
| f405fa9118 | |||
| 7b5d6e7c29 | |||
| 7d0ba7178e | |||
| df5e2c3aca | |||
| 343e344582 | |||
| 34ed463bea | |||
| 5986f75d16 | |||
| b08810c0c4 | |||
| 7c608ae7bc | |||
| d51cee35d5 | |||
| d0289b4c7b | |||
| bc7ba9cb3b | |||
| 6b10c8a4d3 | |||
| b9805c7309 | |||
| 844460fd22 | |||
| a600278d27 | |||
| 4398ffe8c9 | |||
| eed1eeedeb | |||
| 8c0c230f78 | |||
| e96ec1e79a | |||
| be7dd51fab | |||
| 4dec95840b | |||
| d44fb36581 | |||
| 9d3bbe27db | |||
| 13ed4afc95 | |||
| b3f54e74a7 | |||
| d6374e2718 | |||
| 74156b6b06 | |||
| 965048d3e6 | |||
| 36dfe6b98b | |||
| e4e89bdbde | |||
| 85a9d8d81d | |||
| b21ad13bbb | |||
| f0d10308e3 | |||
| f240e587cb | |||
| 2a7dc9e40e | |||
| 99b8d0063b | |||
| c6aff9278a | |||
| 129497133e | |||
| eaa9ff0cec | |||
| fa735864a3 | |||
| 0ce180e19b | |||
| e578b7eee3 | |||
| 1844695b0c | |||
| 54b9adbdbc | |||
| 8b71656cd7 | |||
| a858ef5688 | |||
| 18395c8d21 | |||
| e68ec5c9a9 | |||
| d3b264345d | |||
| f1e1733c06 | |||
| 66ffbdb881 | |||
| f7f5466ed2 | |||
| 83410fcabe | |||
| 1ee91d494d | |||
| e8fe550d6c | |||
| 5b0fe7f655 | |||
| ba439437bf | |||
| aa6eef135e | |||
| 1dd085daaa | |||
| 2a8f50bcb7 | |||
| 866c58a879 | |||
| c9a75a56c2 | |||
| e1861aa3d9 | |||
| 242c7cb93a | |||
| 0597463902 | |||
| 14d80725eb |
@@ -6,11 +6,12 @@ local library = "unordered";
|
||||
|
||||
local triggers =
|
||||
{
|
||||
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
|
||||
branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ]
|
||||
};
|
||||
|
||||
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
||||
local asan = { ASAN: '1' };
|
||||
local tsan = { TSAN: '1' };
|
||||
|
||||
local linux_pipeline(name, image, environment, packages = "", sources = [], arch = "amd64") =
|
||||
{
|
||||
@@ -224,6 +225,13 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 64 TSAN (11,14,17,20,2b)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11,14,17,20,2b', ADDRMD: '64', TARGET: 'libs/unordered/test//cfoa_tests' } + tsan,
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.5",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
@@ -343,6 +351,13 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14 libc++ 64 TSAN",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', ADDRMD: '64', TARGET: 'libs/unordered/test//cfoa_tests', CXXSTD: '11,14,17,20', STDLIB: 'libc++' } + tsan,
|
||||
"clang-14 libc++-14-dev libc++abi-14-dev",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 15",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
@@ -362,6 +377,12 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 12.4 Xcode 13.4.1 TSAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,1z', TARGET: 'libs/unordered/test//cfoa_tests' } + tsan,
|
||||
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2015 msvc-14.0",
|
||||
"cppalliance/dronevs2015",
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
set -ex
|
||||
export PATH=~/.local/bin:/usr/local/bin:$PATH
|
||||
|
||||
: ${TARGET:="libs/$LIBRARY/test"}
|
||||
|
||||
DRONE_BUILD_DIR=$(pwd)
|
||||
|
||||
BOOST_BRANCH=develop
|
||||
@@ -22,4 +24,4 @@ python tools/boostdep/depinst/depinst.py $LIBRARY
|
||||
./b2 -d0 headers
|
||||
|
||||
echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam
|
||||
./b2 -j3 libs/$LIBRARY/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
|
||||
./b2 -j3 $TARGET toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${STDLIB:+stdlib=$STDLIB} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${TSAN:+thread-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
|
||||
|
||||
@@ -19,9 +19,9 @@ on:
|
||||
- fix/**
|
||||
- pr/**
|
||||
|
||||
concurrency:
|
||||
group: ${{format('{0}:{1}', github.repository, github.ref)}}
|
||||
cancel-in-progress: true
|
||||
#concurrency:
|
||||
# group: ${{format('{0}:{1}', github.repository, github.ref)}}
|
||||
# cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GIT_FETCH_JOBS: 8
|
||||
@@ -42,39 +42,52 @@ 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-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" }
|
||||
- { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
|
||||
- { name: Collect coverage, coverage: yes,
|
||||
compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64', ccache_key: "cov" }
|
||||
# - { 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" }
|
||||
# - { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
# compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
|
||||
# - { name: Collect coverage, coverage: yes,
|
||||
# compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64', ccache_key: "cov" }
|
||||
|
||||
- { 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, ccache_key: "tsan-gcc" }
|
||||
|
||||
# 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' }
|
||||
# - { 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" }
|
||||
- { 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-14, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
|
||||
# - { 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" }
|
||||
# - { 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-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" }
|
||||
- { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
|
||||
# - { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes,
|
||||
# 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" }
|
||||
|
||||
- { 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-clang" }
|
||||
|
||||
# OSX, clang
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, sanitize: yes }
|
||||
- { compiler: clang, cxxstd: '11,14', os: macos-11, ccache_key: "11-14" }
|
||||
- { compiler: clang, cxxstd: '17,2a', os: macos-11, ccache_key: "17-2a" }
|
||||
- { compiler: clang, cxxstd: '11,14', os: macos-12, sanitize: yes, ccache_key: "11-14" }
|
||||
- { compiler: clang, cxxstd: '17,2a', os: macos-12, sanitize: yes, ccache_key: "17-2a" }
|
||||
- { compiler: clang, cxxstd: '11,14', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan-macos-1" }
|
||||
- { compiler: clang, cxxstd: '17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan-macos-2" }
|
||||
|
||||
timeout-minutes: 120
|
||||
timeout-minutes: 240
|
||||
runs-on: ${{matrix.os}}
|
||||
container: ${{matrix.container}}
|
||||
env: {B2_USE_CCACHE: 1}
|
||||
@@ -183,6 +196,8 @@ jobs:
|
||||
B2_COMPILER: ${{matrix.compiler}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_SANITIZE: ${{matrix.sanitize}}
|
||||
B2_TSAN: ${{matrix.thread-sanitize}}
|
||||
B2_TARGETS: ${{matrix.targets}}
|
||||
B2_STDLIB: ${{matrix.stdlib}}
|
||||
# More entries can be added in the same way, see the B2_ARGS assignment in ci/enforce.sh for the possible keys.
|
||||
# B2_DEFINES: ${{matrix.defines}}
|
||||
@@ -196,7 +211,7 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
if: '!matrix.coverity'
|
||||
run: ci/build.sh
|
||||
run: B2_TARGETS=${{matrix.targets}} B2_FLAGS='-l 3600' ci/build.sh
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.coverage
|
||||
@@ -208,187 +223,3 @@ jobs:
|
||||
env:
|
||||
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
|
||||
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
|
||||
windows:
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
strategy:
|
||||
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' }
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
REM Copy ci folder if not testing Boost.CI
|
||||
if "%GITHUB_REPOSITORY%" == "%GITHUB_REPOSITORY:boost-ci=%" xcopy /s /e /q /i /y boost-ci-cloned\ci .\ci
|
||||
rmdir /s /q boost-ci-cloned
|
||||
|
||||
- name: Setup Boost
|
||||
run: ci\github\install.bat
|
||||
|
||||
- name: Run tests
|
||||
if: '!matrix.coverage'
|
||||
run: ci\build.bat
|
||||
env:
|
||||
B2_TOOLSET: ${{matrix.toolset}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
|
||||
B2_DEFINES: ${{matrix.defines}}
|
||||
B2_VARIANT: ${{matrix.variant}}
|
||||
B2_CXXFLAGS: ${{matrix.cxxflags}}
|
||||
|
||||
- name: Collect coverage
|
||||
shell: powershell
|
||||
if: matrix.coverage
|
||||
run: ci\opencppcoverage.ps1
|
||||
env:
|
||||
B2_TOOLSET: ${{matrix.toolset}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
files: __out/cobertura.xml
|
||||
|
||||
MSYS2:
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup MSYS2 environment
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.sys}}
|
||||
update: true
|
||||
install: git python
|
||||
pacboy: gcc:p cmake:p ninja:p
|
||||
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
|
||||
- name: Setup Boost
|
||||
env:
|
||||
B2_COMPILER: ${{matrix.compiler}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_SANITIZE: ${{matrix.sanitize}}
|
||||
B2_STDLIB: ${{matrix.stdlib}}
|
||||
run: ci/github/install.sh
|
||||
|
||||
- name: Run tests
|
||||
run: ci/build.sh
|
||||
|
||||
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
|
||||
- name: Run CMake tests
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_test__ && cd __build_cmake_test__
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
|
||||
cmake --build . --target tests --config Debug -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config Debug
|
||||
|
||||
CMake:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
strategy:
|
||||
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' }
|
||||
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
- name: Setup Boost
|
||||
env: {B2_DONT_BOOTSTRAP: 1}
|
||||
run: source ci/github/install.sh
|
||||
|
||||
- name: Run CMake tests
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_test__ && cd __build_cmake_test__
|
||||
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
|
||||
cmake --build . --target tests --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
||||
- name: Run CMake subdir tests
|
||||
run: |
|
||||
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
|
||||
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test"
|
||||
cd "$cmake_test_folder"
|
||||
mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__
|
||||
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} ..
|
||||
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
||||
- name: Install Library
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
|
||||
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX=~/.local -DBoost_VERBOSE=ON -DBoost_DEBUG=ON ..
|
||||
cmake --build . --target install --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
- name: Run CMake install tests
|
||||
run: |
|
||||
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
|
||||
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test"
|
||||
cd "$cmake_test_folder"
|
||||
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
|
||||
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH=~/.local ..
|
||||
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
@@ -272,6 +274,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -345,6 +350,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
template<class K, class V> using boost_unordered_map_fnv1a =
|
||||
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map_fnv1a =
|
||||
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map_fnv1a =
|
||||
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -373,6 +381,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
@@ -390,6 +399,7 @@ int main()
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
|
||||
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
@@ -273,6 +275,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -346,6 +351,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
template<class K, class V> using boost_unordered_map_fnv1a =
|
||||
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map_fnv1a =
|
||||
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map_fnv1a =
|
||||
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -374,6 +382,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
@@ -391,6 +400,7 @@ int main()
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
|
||||
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
@@ -289,6 +291,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -315,6 +320,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
@@ -289,6 +291,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -325,6 +330,7 @@ int main()
|
||||
#endif
|
||||
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021, 2022 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
@@ -340,6 +342,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -366,6 +371,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021, 2022 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
@@ -227,6 +229,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -300,6 +305,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
template<class K, class V> using boost_unordered_map_fnv1a =
|
||||
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map_fnv1a =
|
||||
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map_fnv1a =
|
||||
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -328,6 +336,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
@@ -345,6 +354,7 @@ int main()
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
|
||||
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2021, 2022 Peter Dimov.
|
||||
// Copyright 2023 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
@@ -181,6 +183,9 @@ template<class K, class V> using std_unordered_map =
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_node_map =
|
||||
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_flat_map =
|
||||
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
@@ -207,6 +212,7 @@ int main()
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<boost_unordered_node_map>( "boost::unordered_node_map" );
|
||||
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 51 KiB |
@@ -278,13 +278,14 @@ max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
== boost::unordered_flat_map
|
||||
== boost::unordered_(flat|node)_map
|
||||
|
||||
All benchmarks were created using:
|
||||
|
||||
* `https://abseil.io/docs/cpp/guides/container[absl::flat_hash_map^]<uint64_t, uint64_t>`
|
||||
* `boost::unordered_flat_map<uint64_t, uint64_t>`
|
||||
* `boost::unordered_map<uint64_t, uint64_t>`
|
||||
* `boost::unordered_flat_map<uint64_t, uint64_t>`
|
||||
* `boost::unordered_node_map<uint64_t, uint64_t>`
|
||||
|
||||
The source code can be https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_unordered_flat_map[found here^].
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ h|*Method* h|*Description*
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
||||
2+^h| *Open-addressing containers only* +
|
||||
`boost::unordered_flat_set`, `boost::unordered_flat_map`
|
||||
`boost::unordered_flat_set`, `boost::unordered_flat_map` +
|
||||
`boost::unordered_node_set`, `boost::unordered_node_map` +
|
||||
h|*Method* h|*Description*
|
||||
|
||||
|`size_type max_load() const`
|
||||
@@ -160,8 +161,9 @@ change the number of buckets when this happens. Iterators can be
|
||||
invalidated by calls to `insert`, `rehash` and `reserve`.
|
||||
|
||||
As for pointers and references,
|
||||
they are never invalidated for closed-addressing containers (`boost::unordered_[multi]set`, `boost::unordered_[multi]map`),
|
||||
but they will when rehashing occurs for open-addressing
|
||||
they are never invalidated for node-based containers
|
||||
(`boost::unordered_[multi]set`, `boost::unordered_[multi]map`, `boost::unordered_node_set`, `boost::unordered_node_map`),
|
||||
but they will when rehashing occurs for
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map`: this is because
|
||||
these containers store elements directly into their holding buckets, so
|
||||
when allocating a new bucket array the elements must be transferred by means of move construction.
|
||||
@@ -252,15 +254,16 @@ xref:#rationale_boostunordered_multiset_and_boostunordered_multimap[correspondin
|
||||
|
||||
== Open Addressing Implementation
|
||||
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_map` and
|
||||
`boost:unordered_flat_set`.
|
||||
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]
|
||||
.Open-addressing layout used by Boost.Unordered.
|
||||
image::foa.png[align=center]
|
||||
|
||||
As with all open-addressing containers, elements are stored directly in the bucket array.
|
||||
As with all open-addressing containers, elements (or pointers to the element nodes in the case of
|
||||
`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.
|
||||
|
||||
@@ -6,10 +6,23 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.82.0
|
||||
== Release 1.83.0
|
||||
|
||||
* Sped up iteration of open-addressing containers.
|
||||
|
||||
== Release 1.82.0 - Major update
|
||||
|
||||
* {cpp}03 support is planned for deprecation. Boost 1.84.0 will no longer support
|
||||
{cpp}03 mode and {cpp}11 will become the new minimum for using the library.
|
||||
* Added node-based, open-addressing containers
|
||||
`boost::unordered_node_map` and `boost::unordered_node_set`.
|
||||
* Extended heterogeneous lookup to more member functions as specified in
|
||||
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2363r3.html[P2363].
|
||||
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363].
|
||||
* Replaced the previous post-mixing process for open-addressing containers with
|
||||
a new algorithm based on extended multiplication by a constant.
|
||||
* Fixed bug in internal emplace() impl where stack-local types were not properly
|
||||
constructed using the Allocator of the container which breaks uses-allocator
|
||||
construction.
|
||||
|
||||
== Release 1.81.0 - Major update
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
|
||||
|Iterators, pointers and references to the container's elements are never invalidated.
|
||||
|<<buckets_iterator_invalidation,Iterators can be invalidated by calls to insert or rehash>>. +
|
||||
**Closed-addressing containers:** Pointers and references to the container's elements are never invalidated. +
|
||||
**Open-addressing containers:** Pointers and references to the container's elements are invalidated when rehashing occurs.
|
||||
**Node-based containers:** Pointers and references to the container's elements are never invalidated. +
|
||||
**Flat containers:** Pointers and references to the container's elements are invalidated when rehashing occurs.
|
||||
|
||||
|Iterators iterate through the container in the order defined by the comparison object.
|
||||
|Iterators iterate through the container in an arbitrary order, that can change as elements are inserted, although equivalent elements are always adjacent.
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
:cpp: C++
|
||||
|
||||
== Closed-addressing containers: unordered_[multi]set, unordered_[multi]map
|
||||
== Closed-addressing containers
|
||||
|
||||
The intent of Boost.Unordered is to provide a conformant
|
||||
`unordered_[multi]set` and `unordered_[multi]map` are intended to provide a conformant
|
||||
implementation of the {cpp}20 standard that will work with {cpp}98 upwards.
|
||||
This wide compatibility does mean some compromises have to be made.
|
||||
With a compiler and library that fully support {cpp}11, the differences should
|
||||
@@ -117,27 +117,31 @@ Variadic constructor arguments for `emplace` are only used when both
|
||||
rvalue references and variadic template parameters are available.
|
||||
Otherwise `emplace` can only take up to 10 constructors arguments.
|
||||
|
||||
== Open-addressing containers: unordered_flat_set, unordered_flat_map
|
||||
== Open-addressing containers
|
||||
|
||||
The C++ standard does not currently provide any open-addressing container
|
||||
specification to adhere to, so `boost::unordered_flat_set` and
|
||||
`boost::unordered_flat_map` take inspiration from `std::unordered_set` and
|
||||
specification to adhere to, so `boost::unordered_flat_set`/`unordered_node_set` and
|
||||
`boost::unordered_flat_map`/`unordered_node_map` take inspiration from `std::unordered_set` and
|
||||
`std::unordered_map`, respectively, and depart from their interface where
|
||||
convenient or as dictated by their internal data structure, which is
|
||||
radically different from that imposed by the standard (closed addressing, node based).
|
||||
radically different from that imposed by the standard (closed addressing).
|
||||
|
||||
`unordered_flat_set` and `unordered_flat_map` only work with reasonably
|
||||
Open-addressing containers provided by Boost.Unordered only work with reasonably
|
||||
compliant C++11 (or later) compilers. Language-level features such as move semantics
|
||||
and variadic template parameters are then not emulated.
|
||||
`unordered_flat_set` and `unordered_flat_map` are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
|
||||
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
|
||||
|
||||
The main differences with C++ unordered associative containers are:
|
||||
|
||||
* `value_type` must be move-constructible.
|
||||
* Pointer stability is not kept under rehashing.
|
||||
* `begin()` is not constant-time.
|
||||
* `erase(iterator)` returns `void` instead of an iterator to the following element.
|
||||
* There is no API for bucket handling (except `bucket_count`) or node extraction/insertion.
|
||||
* The maximum load factor of the container is managed internally and can't be set by the user. The maximum load,
|
||||
exposed through the public function `max_load`, may decrease on erasure under high-load conditions.
|
||||
* In general:
|
||||
** `begin()` is not constant-time.
|
||||
** `erase(iterator)` returns `void` instead of an iterator to the following element.
|
||||
** There is no API for bucket handling (except `bucket_count`).
|
||||
** The maximum load factor of the container is managed internally and can't be set by the user. The maximum load,
|
||||
exposed through the public function `max_load`, may decrease on erasure under high-load conditions.
|
||||
* Flat containers (`boost::unordered_flat_set` and `boost::unordered_flat_map`):
|
||||
** `value_type` must be move-constructible.
|
||||
** Pointer stability is not kept under rehashing.
|
||||
** There is no API for node extraction/insertion.
|
||||
|
||||
//-
|
||||
|
||||
@@ -9,10 +9,10 @@ Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
|
||||
|
||||
Copyright (C) 2005-2008 Daniel James
|
||||
|
||||
Copyright (C) 2022 Christian Mazakas
|
||||
Copyright (C) 2022-2023 Christian Mazakas
|
||||
|
||||
Copyright (C) 2022 Joaquín M López Muñoz
|
||||
Copyright (C) 2022-2023 Joaquín M López Muñoz
|
||||
|
||||
Copyright (C) 2022 Peter Dimov
|
||||
Copyright (C) 2022-2023 Peter Dimov
|
||||
|
||||
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)
|
||||
|
||||
@@ -106,8 +106,69 @@ namespace boost {
|
||||
}
|
||||
----
|
||||
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map` require a
|
||||
reasonably compliant C++11 compiler.
|
||||
Starting in Boost 1.82, the containers `boost::unordered_node_set` and `boost::unordered_node_map`
|
||||
are introduced: they use open addressing like `boost::unordered_flat_set` and `boost::unordered_flat_map`,
|
||||
but internally store element _nodes_, like `boost::unordered_set` and `boost::unordered_map`,
|
||||
which provide stability of pointers and references to the elements:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
// #include <boost/unordered/unordered_node_set.hpp>
|
||||
//
|
||||
// Note: no multiset version
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_node_set;
|
||||
}
|
||||
----
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
// #include <boost/unordered/unordered_node_map.hpp>
|
||||
//
|
||||
// Note: no multimap version
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_node_map;
|
||||
}
|
||||
----
|
||||
|
||||
These are all the containers provided by Boost.Unordered:
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Boost.Unordered containers']
|
||||
[cols="1,1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
^h|
|
||||
^h|*Node-based*
|
||||
^h|*Flat*
|
||||
|
||||
^.^h|*Closed addressing*
|
||||
^| `boost::unordered_set` +
|
||||
`boost::unordered_map` +
|
||||
`boost::unordered_multiset` +
|
||||
`boost::unordered_multimap`
|
||||
^|
|
||||
|
||||
^.^h|*Open addressing*
|
||||
^| `boost::unordered_node_set` +
|
||||
`boost::unordered_node_map`
|
||||
^| `boost::unordered_flat_set` +
|
||||
`boost::unordered_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
Closed-addressing containers are pass:[C++]98-compatible. Open-addressing containers require a
|
||||
reasonably compliant pass:[C++]11 compiler.
|
||||
|
||||
Boost.Unordered containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
= Implementation Rationale
|
||||
|
||||
== boost::unordered_[multi]set and boost::unordered_[multi]map
|
||||
== Closed-addressing containers
|
||||
|
||||
These containers adhere to the standard requirements for unordered associative
|
||||
`boost::unordered_[multi]set` and `boost::unordered_[multi]map`
|
||||
adhere to the standard requirements for unordered associative
|
||||
containers, so the interface was fixed. But there are
|
||||
still some implementation decisions to make. The priorities are
|
||||
conformance to the standard and portability.
|
||||
@@ -64,8 +65,8 @@ of bits in the hash value, so it was only used when `size_t` was 64 bit.
|
||||
|
||||
Since release 1.79.0, https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing[Fibonacci hashing]
|
||||
is used instead. With this implementation, the bucket number is determined
|
||||
by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is the golden
|
||||
ratio multiplied by `2^w`, `w` is the word size (32 or 64), and `2^k` is the
|
||||
by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is `2^w` divided
|
||||
by the golden ratio, `w` is the word size (32 or 64), and `2^k` is the
|
||||
number of buckets. This provides a good compromise between speed and
|
||||
distribution.
|
||||
|
||||
@@ -73,7 +74,7 @@ Since release 1.80.0, prime numbers are chosen for the number of buckets in
|
||||
tandem with sophisticated modulo arithmetic. This removes the need for "mixing"
|
||||
the result of the user's hash function as was used for release 1.79.0.
|
||||
|
||||
== boost::unordered_flat_set and boost::unordered_flat_map
|
||||
== Open-addresing containers
|
||||
|
||||
The C++ standard specification of unordered associative containers impose
|
||||
severe limitations on permissible implementations, the most important being
|
||||
@@ -81,29 +82,32 @@ that closed addressing is implicitly assumed. Slightly relaxing this specificati
|
||||
opens up the possibility of providing container variations taking full
|
||||
advantage of open-addressing techniques.
|
||||
|
||||
The design of `boost::unordered_flat_set` and `boost::unordered_flat_map` has been
|
||||
The design of `boost::unordered_flat_set`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` has been
|
||||
guided by Peter Dimov's https://pdimov.github.io/articles/unordered_dev_plan.html[Development Plan for Boost.Unordered^].
|
||||
We discuss here the most relevant principles.
|
||||
|
||||
=== Hash function
|
||||
|
||||
Given its rich functionality and cross-platform interoperability,
|
||||
`boost::hash` remains the default hash function of `boost::unordered_flat_set` and `boost::unordered_flat_map`.
|
||||
`boost::hash` remains the default hash function of open-addressing containers.
|
||||
As it happens, `boost::hash` for integral and other basic types does not possess
|
||||
the statistical properties required by open addressing; to cope with this,
|
||||
we implement a post-mixing stage:
|
||||
|
||||
* 64-bit architectures: we use the `xmx` function defined in
|
||||
Jon Maiga's http://jonkagstrom.com/bit-mixer-construction/index.html[The construct of a bit mixer^].
|
||||
* 32-bit architectures: the mixer used was selected from a set generated with https://github.com/skeeto/hash-prospector[Hash Function Prospector^]
|
||||
as the best overall performer in our internal benchmarks. Score assigned by Hash Prospector is 333.7934929677524.
|
||||
{nbsp}{nbsp}{nbsp}{nbsp} _a_ <- _h_ *mulx* _C_, +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp} _h_ <- *high*(_a_) *xor* *low*(_a_),
|
||||
|
||||
where *mulx* is an _extended multiplication_ (128 bits in 64-bit architectures, 64 bits in 32-bit environments),
|
||||
and *high* and *low* are the upper and lower halves of an extended word, respectively.
|
||||
In 64-bit architectures, _C_ is the integer part of 2^64^∕https://en.wikipedia.org/wiki/Golden_ratio[_φ_],
|
||||
whereas in 32 bits _C_ = 0xE817FB2Du has been obtained from https://arxiv.org/abs/2001.05304[Steele and Vigna (2021)^].
|
||||
|
||||
When using a hash function directly suitable for open addressing, post-mixing can be opted out by via a dedicated <<hash_traits_hash_is_avalanching,`hash_is_avalanching`>>trait.
|
||||
`boost::hash` specializations for string types are marked as avalanching.
|
||||
|
||||
=== Platform interoperability
|
||||
|
||||
The observable behavior of `boost::unordered_flat_set` and `boost::unordered_flat_map` is deterministically
|
||||
The observable behavior of `boost::unordered_flat_set`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` is deterministically
|
||||
identical across different compilers as long as their ``std::size_type``s are the same size and the user-provided
|
||||
hash function and equality predicate are also interoperable
|
||||
—this includes elements being ordered in exactly the same way for the same sequence of
|
||||
|
||||
@@ -8,3 +8,5 @@ include::unordered_multiset.adoc[]
|
||||
include::hash_traits.adoc[]
|
||||
include::unordered_flat_map.adoc[]
|
||||
include::unordered_flat_set.adoc[]
|
||||
include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
|
||||
@@ -0,0 +1,685 @@
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Reference:
|
||||
* https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.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&`");
|
||||
|
||||
#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> >
|
||||
{
|
||||
};
|
||||
|
||||
template <class Key, class T> struct concurrent_map_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using raw_key_type = typename std::remove_const<Key>::type;
|
||||
using raw_mapped_type = typename std::remove_const<T>::type;
|
||||
|
||||
using init_type = std::pair<raw_key_type, raw_mapped_type>;
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
using value_type = std::pair<Key const, T>;
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
static value_type& value_from(element_type& x) { return x; }
|
||||
|
||||
template <class K, class V>
|
||||
static raw_key_type const& extract(std::pair<K, V> const& kv)
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
|
||||
static moved_type move(init_type& x)
|
||||
{
|
||||
return {std::move(x.first), std::move(x.second)};
|
||||
}
|
||||
|
||||
static moved_type move(element_type& x)
|
||||
{
|
||||
// TODO: we probably need to launder here
|
||||
return {std::move(const_cast<raw_key_type&>(x.first)),
|
||||
std::move(const_cast<raw_mapped_type&>(x.second))};
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class concurrent_flat_map
|
||||
{
|
||||
private:
|
||||
using type_policy = detail::concurrent_map_types<Key, T>;
|
||||
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using value_type = typename type_policy::value_type;
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::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_map()
|
||||
: concurrent_flat_map(detail::foa::default_bucket_count)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_map(InputIterator f, InputIterator l,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
this->insert(f, l);
|
||||
}
|
||||
|
||||
concurrent_flat_map(concurrent_flat_map const& rhs)
|
||||
: table_(rhs.table_,
|
||||
boost::allocator_select_on_container_copy_construction(
|
||||
rhs.get_allocator()))
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(concurrent_flat_map&& rhs)
|
||||
: table_(std::move(rhs.table_))
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_map(InputIterator f, InputIterator l, allocator_type a)
|
||||
: concurrent_flat_map(f, l, 0, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_map(allocator_type a)
|
||||
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(concurrent_flat_map const& rhs, allocator_type a)
|
||||
: table_(rhs.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type a)
|
||||
: table_(std::move(rhs.table_), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(std::initializer_list<value_type> il,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: concurrent_flat_map(n, hf, eql, a)
|
||||
{
|
||||
this->insert(il.begin(), il.end());
|
||||
}
|
||||
|
||||
concurrent_flat_map(size_type n, const allocator_type& a)
|
||||
: concurrent_flat_map(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(
|
||||
size_type n, const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_map(n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_map(
|
||||
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
|
||||
: concurrent_flat_map(f, l, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_map(InputIterator f, InputIterator l, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_map(f, l, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(
|
||||
std::initializer_list<value_type> il, const allocator_type& a)
|
||||
: concurrent_flat_map(
|
||||
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
|
||||
const allocator_type& a)
|
||||
: concurrent_flat_map(il, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_map(il, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_flat_map() = default;
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
|
||||
{
|
||||
table_ = rhs.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE std::size_t visit(key_type const& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE std::size_t 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 std::size_t 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, std::size_t>::type
|
||||
visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, std::size_t>::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, std::size_t>::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> BOOST_FORCEINLINE std::size_t visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> BOOST_FORCEINLINE std::size_t visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> BOOST_FORCEINLINE std::size_t 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>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
BOOST_FORCEINLINE
|
||||
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)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
BOOST_FORCEINLINE
|
||||
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)
|
||||
table_.cvisit_all(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));
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool insert(init_type const& obj)
|
||||
{
|
||||
return table_.insert(obj);
|
||||
}
|
||||
BOOST_FORCEINLINE bool insert(init_type&& obj)
|
||||
{
|
||||
return table_.insert(std::move(obj));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
BOOST_FORCEINLINE void insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
table_.insert(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template <class M>
|
||||
BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class M>
|
||||
BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class K, class M>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
insert_or_assign(K&& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k),
|
||||
std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(init_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
BOOST_FORCEINLINE void insert_or_visit(
|
||||
InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.insert_or_visit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE void insert_or_visit(
|
||||
std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
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 F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(init_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(init_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
BOOST_FORCEINLINE void insert_or_cvisit(
|
||||
InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.insert_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE void insert_or_cvisit(
|
||||
std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class... Args>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
try_emplace(K&& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(
|
||||
std::forward<K>(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
key_type const& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(
|
||||
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
key_type const& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(
|
||||
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
key_type&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(
|
||||
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
key_type&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(
|
||||
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
K&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k),
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
K&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k),
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
erase(K&& k)
|
||||
{
|
||||
return table_.erase(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
|
||||
{
|
||||
return table_.erase_if(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value &&
|
||||
!detail::is_execution_policy<K>::value,
|
||||
size_type>::type
|
||||
erase_if(K&& k, F f)
|
||||
{
|
||||
return table_.erase_if(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
erase_if(ExecPolicy p, F f)
|
||||
{
|
||||
table_.erase_if(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> BOOST_FORCEINLINE size_type erase_if(F f)
|
||||
{
|
||||
return table_.erase_if(f);
|
||||
}
|
||||
|
||||
/// Hash Policy
|
||||
///
|
||||
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(); }
|
||||
};
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE
|
||||
#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
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<class T>
|
||||
struct element_type
|
||||
{
|
||||
using value_type=T;
|
||||
value_type* p;
|
||||
|
||||
/*
|
||||
* we use a deleted copy constructor here so the type is no longer
|
||||
* trivially copy-constructible which inhibits our memcpy
|
||||
* optimizations when copying the tables
|
||||
*/
|
||||
element_type() = default;
|
||||
element_type(value_type* p_):p(p_){}
|
||||
element_type(element_type const&) = delete;
|
||||
element_type(element_type&& rhs) noexcept
|
||||
{
|
||||
p = rhs.p;
|
||||
rhs.p = nullptr;
|
||||
}
|
||||
|
||||
element_type& operator=(element_type const&)=delete;
|
||||
element_type& operator=(element_type&& rhs)noexcept
|
||||
{
|
||||
if (this!=&rhs){
|
||||
p=rhs.p;
|
||||
rhs.p=nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(element_type& rhs)noexcept
|
||||
{
|
||||
auto tmp=p;
|
||||
p=rhs.p;
|
||||
rhs.p=tmp;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
@@ -0,0 +1,35 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#if !defined(BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW)
|
||||
/* GCC's -Wshadow triggers at scenarios like this:
|
||||
*
|
||||
* struct foo{};
|
||||
* template<typename Base>
|
||||
* struct derived:Base
|
||||
* {
|
||||
* void f(){int foo;}
|
||||
* };
|
||||
*
|
||||
* derived<foo>x;
|
||||
* x.f(); // declaration of "foo" in derived::f shadows base type "foo"
|
||||
*
|
||||
* This makes shadowing warnings unavoidable in general when a class template
|
||||
* derives from user-provided classes, as is the case with foa::table_core
|
||||
* deriving from empty_value.
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,210 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template <class Iterator,class NodeType>
|
||||
struct insert_return_type
|
||||
{
|
||||
Iterator position;
|
||||
bool inserted;
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
union opt_storage {
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
|
||||
|
||||
opt_storage(){}
|
||||
~opt_storage(){}
|
||||
};
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
struct node_handle_base
|
||||
{
|
||||
protected:
|
||||
using type_policy=TypePolicy;
|
||||
using element_type=typename type_policy::element_type;
|
||||
|
||||
public:
|
||||
using allocator_type = Allocator;
|
||||
|
||||
private:
|
||||
using node_value_type=typename type_policy::value_type;
|
||||
element_type p_;
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
|
||||
|
||||
protected:
|
||||
node_value_type& data()noexcept
|
||||
{
|
||||
return *(p_.p);
|
||||
}
|
||||
|
||||
node_value_type const& data()const noexcept
|
||||
{
|
||||
return *(p_.p);
|
||||
}
|
||||
|
||||
element_type& element()noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return p_;
|
||||
}
|
||||
|
||||
element_type const& element()const noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return p_;
|
||||
}
|
||||
|
||||
Allocator& al()noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return a_.t_;
|
||||
}
|
||||
|
||||
Allocator const& al()const noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return a_.t_;
|
||||
}
|
||||
|
||||
void emplace(element_type&& x,Allocator a)
|
||||
{
|
||||
BOOST_ASSERT(empty());
|
||||
auto* p=x.p;
|
||||
p_.p=p;
|
||||
new(&a_.t_)Allocator(a);
|
||||
x.p=nullptr;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
a_.t_.~Allocator();
|
||||
p_.p=nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr node_handle_base()noexcept:p_{nullptr}{}
|
||||
|
||||
node_handle_base(node_handle_base&& nh) noexcept
|
||||
{
|
||||
p_.p = nullptr;
|
||||
if (!nh.empty()){
|
||||
emplace(std::move(nh.p_),nh.al());
|
||||
nh.reset();
|
||||
}
|
||||
}
|
||||
|
||||
node_handle_base& operator=(node_handle_base&& nh)noexcept
|
||||
{
|
||||
if(this!=&nh){
|
||||
if(empty()){
|
||||
if(nh.empty()){ /* empty(), nh.empty() */
|
||||
/* nothing to do */
|
||||
}else{ /* empty(), !nh.empty() */
|
||||
emplace(std::move(nh.p_),std::move(nh.al()));
|
||||
nh.reset();
|
||||
}
|
||||
}else{
|
||||
if(nh.empty()){ /* !empty(), nh.empty() */
|
||||
type_policy::destroy(al(),&p_);
|
||||
reset();
|
||||
}else{ /* !empty(), !nh.empty() */
|
||||
bool const pocma=
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value;
|
||||
|
||||
BOOST_ASSERT(pocma||al()==nh.al());
|
||||
|
||||
type_policy::destroy(al(),&p_);
|
||||
if(pocma){
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
|
||||
p_=std::move(nh.p_);
|
||||
nh.reset();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(empty()){ /* empty(), nh.empty() */
|
||||
/* nothing to do */
|
||||
}else{ /* !empty(), !nh.empty() */
|
||||
type_policy::destroy(al(),&p_);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~node_handle_base()
|
||||
{
|
||||
if(!empty()){
|
||||
type_policy::destroy(al(),&p_);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
allocator_type get_allocator()const noexcept{return al();}
|
||||
explicit operator bool()const noexcept{ return !empty();}
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
|
||||
|
||||
void swap(node_handle_base& nh) noexcept(
|
||||
boost::allocator_is_always_equal<Allocator>::type::value||
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
|
||||
{
|
||||
if(this!=&nh){
|
||||
if(empty()){
|
||||
if(nh.empty()) {
|
||||
/* nothing to do here */
|
||||
} else {
|
||||
emplace(std::move(nh.p_), nh.al());
|
||||
nh.reset();
|
||||
}
|
||||
}else{
|
||||
if(nh.empty()){
|
||||
nh.emplace(std::move(p_),al());
|
||||
reset();
|
||||
}else{
|
||||
bool const pocs=
|
||||
boost::allocator_propagate_on_container_swap<
|
||||
Allocator>::type::value;
|
||||
|
||||
BOOST_ASSERT(pocs || al()==nh.al());
|
||||
|
||||
using std::swap;
|
||||
p_.swap(nh.p_);
|
||||
if(pocs)swap(al(),nh.al());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
friend
|
||||
void swap(node_handle_base& lhs,node_handle_base& rhs)
|
||||
noexcept(noexcept(lhs.swap(rhs)))
|
||||
{
|
||||
return lhs.swap(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
|
||||
@@ -0,0 +1,11 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
|
||||
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
|
||||
#undef BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
|
||||
@@ -0,0 +1,188 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
|
||||
|
||||
// Copyright 2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/smart_ptr/detail/sp_thread_pause.hpp>
|
||||
#include <boost/smart_ptr/detail/sp_thread_sleep.hpp>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
class rw_spinlock
|
||||
{
|
||||
private:
|
||||
|
||||
// bit 31: locked exclusive
|
||||
// bit 30: writer pending
|
||||
// bit 29..0: reader lock count
|
||||
|
||||
static constexpr std::uint32_t locked_exclusive_mask = 1u << 31; // 0x8000'0000
|
||||
static constexpr std::uint32_t writer_pending_mask = 1u << 30; // 0x4000'0000
|
||||
static constexpr std::uint32_t reader_lock_count_mask = writer_pending_mask - 1; // 0x3FFF'FFFF
|
||||
|
||||
std::atomic<std::uint32_t> state_ = {};
|
||||
|
||||
private:
|
||||
|
||||
// number of times to spin before sleeping
|
||||
static constexpr int spin_count = 24576;
|
||||
|
||||
public:
|
||||
|
||||
bool try_lock_shared() noexcept
|
||||
{
|
||||
std::uint32_t st = state_.load( std::memory_order_relaxed );
|
||||
|
||||
if( st >= reader_lock_count_mask )
|
||||
{
|
||||
// either bit 31 set, bit 30 set, or reader count is max
|
||||
return false;
|
||||
}
|
||||
|
||||
std::uint32_t newst = st + 1;
|
||||
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
|
||||
}
|
||||
|
||||
void lock_shared() noexcept
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
for( int k = 0; k < spin_count; ++k )
|
||||
{
|
||||
std::uint32_t st = state_.load( std::memory_order_relaxed );
|
||||
|
||||
if( st < reader_lock_count_mask )
|
||||
{
|
||||
std::uint32_t newst = st + 1;
|
||||
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
|
||||
}
|
||||
|
||||
boost::detail::sp_thread_pause();
|
||||
}
|
||||
|
||||
boost::detail::sp_thread_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared() noexcept
|
||||
{
|
||||
// pre: locked shared, not locked exclusive
|
||||
|
||||
state_.fetch_sub( 1, std::memory_order_release );
|
||||
|
||||
// if the writer pending bit is set, there's a writer waiting
|
||||
// let it acquire the lock; it will clear the bit on unlock
|
||||
}
|
||||
|
||||
bool try_lock() noexcept
|
||||
{
|
||||
std::uint32_t st = state_.load( std::memory_order_relaxed );
|
||||
|
||||
if( st & locked_exclusive_mask )
|
||||
{
|
||||
// locked exclusive
|
||||
return false;
|
||||
}
|
||||
|
||||
if( st & reader_lock_count_mask )
|
||||
{
|
||||
// locked shared
|
||||
return false;
|
||||
}
|
||||
|
||||
std::uint32_t newst = locked_exclusive_mask;
|
||||
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
|
||||
}
|
||||
|
||||
void lock() noexcept
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
for( int k = 0; k < spin_count; ++k )
|
||||
{
|
||||
std::uint32_t st = state_.load( std::memory_order_relaxed );
|
||||
|
||||
if( st & locked_exclusive_mask )
|
||||
{
|
||||
// locked exclusive, spin
|
||||
}
|
||||
else if( ( st & reader_lock_count_mask ) == 0 )
|
||||
{
|
||||
// not locked exclusive, not locked shared, try to lock
|
||||
|
||||
std::uint32_t newst = locked_exclusive_mask;
|
||||
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
|
||||
}
|
||||
else if( st & writer_pending_mask )
|
||||
{
|
||||
// writer pending bit already set, nothing to do
|
||||
}
|
||||
else
|
||||
{
|
||||
// locked shared, set writer pending bit
|
||||
|
||||
std::uint32_t newst = st | writer_pending_mask;
|
||||
state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed );
|
||||
}
|
||||
|
||||
boost::detail::sp_thread_pause();
|
||||
}
|
||||
|
||||
// clear writer pending bit before going to sleep
|
||||
|
||||
{
|
||||
std::uint32_t st = state_.load( std::memory_order_relaxed );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
if( st & locked_exclusive_mask )
|
||||
{
|
||||
// locked exclusive, nothing to do
|
||||
break;
|
||||
}
|
||||
else if( ( st & reader_lock_count_mask ) == 0 )
|
||||
{
|
||||
// lock free, try to take it
|
||||
|
||||
std::uint32_t newst = locked_exclusive_mask;
|
||||
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
|
||||
}
|
||||
else if( ( st & writer_pending_mask ) == 0 )
|
||||
{
|
||||
// writer pending bit already clear, nothing to do
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear writer pending bit
|
||||
|
||||
std::uint32_t newst = st & ~writer_pending_mask;
|
||||
if( state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed ) ) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::detail::sp_thread_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() noexcept
|
||||
{
|
||||
// pre: locked exclusive, not locked shared
|
||||
state_.store( 0, std::memory_order_release );
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace foa */
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
|
||||
@@ -0,0 +1,540 @@
|
||||
/* Fast open-addressing hash table.
|
||||
*
|
||||
* Copyright 2022-2023 Joaquin M Lopez Munoz.
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/unordered/detail/foa/core.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
/* use plain integrals for group metadata storage */
|
||||
|
||||
template<typename Integral>
|
||||
struct plain_integral
|
||||
{
|
||||
operator Integral()const{return n;}
|
||||
void operator=(Integral m){n=m;}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_GCC,>=50000 && BOOST_GCC<60000)
|
||||
void operator|=(Integral m){n=static_cast<Integral>(n|m);}
|
||||
void operator&=(Integral m){n=static_cast<Integral>(n&m);}
|
||||
#else
|
||||
void operator|=(Integral m){n|=m;}
|
||||
void operator&=(Integral m){n&=m;}
|
||||
#endif
|
||||
|
||||
Integral n;
|
||||
};
|
||||
|
||||
template<typename,typename,typename,typename>
|
||||
class table;
|
||||
|
||||
/* table_iterator keeps two pointers:
|
||||
*
|
||||
* - A pointer p to the element slot.
|
||||
* - A pointer pc to the n-th byte of the associated group metadata, where n
|
||||
* is the position of the element in the group.
|
||||
*
|
||||
* A simpler solution would have been to keep a pointer p to the element, a
|
||||
* pointer pg to the group, and the position n, but that would increase
|
||||
* sizeof(table_iterator) by 4/8 bytes. In order to make this compact
|
||||
* representation feasible, it is required that group objects are aligned
|
||||
* to their size, so that we can recover pg and n as
|
||||
*
|
||||
* - n = pc%sizeof(group)
|
||||
* - pg = pc-n
|
||||
*
|
||||
* (for explanatory purposes pg and pc are treated above as if they were memory
|
||||
* addresses rather than pointers).
|
||||
*
|
||||
* p = nullptr is conventionally used to mark end() iterators.
|
||||
*/
|
||||
|
||||
/* internal conversion from const_iterator to iterator */
|
||||
struct const_iterator_cast_tag{};
|
||||
|
||||
template<typename TypePolicy,typename Group,bool Const>
|
||||
class table_iterator
|
||||
{
|
||||
using type_policy=TypePolicy;
|
||||
using table_element_type=typename type_policy::element_type;
|
||||
using group_type=Group;
|
||||
static constexpr auto N=group_type::N;
|
||||
static constexpr auto regular_layout=group_type::regular_layout;
|
||||
|
||||
public:
|
||||
using difference_type=std::ptrdiff_t;
|
||||
using value_type=typename type_policy::value_type;
|
||||
using pointer=
|
||||
typename std::conditional<Const,value_type const*,value_type*>::type;
|
||||
using reference=
|
||||
typename std::conditional<Const,value_type const&,value_type&>::type;
|
||||
using iterator_category=std::forward_iterator_tag;
|
||||
using element_type=
|
||||
typename std::conditional<Const,value_type const,value_type>::type;
|
||||
|
||||
table_iterator()=default;
|
||||
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
|
||||
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
table_iterator(
|
||||
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
|
||||
inline reference operator*()const noexcept
|
||||
{return type_policy::value_from(*p);}
|
||||
inline pointer operator->()const noexcept
|
||||
{return std::addressof(type_policy::value_from(*p));}
|
||||
inline table_iterator& operator++()noexcept{increment();return *this;}
|
||||
inline table_iterator operator++(int)noexcept
|
||||
{auto x=*this;increment();return x;}
|
||||
friend inline bool operator==(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return x.p==y.p;}
|
||||
friend inline bool operator!=(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return !(x==y);}
|
||||
|
||||
private:
|
||||
template<typename,typename,bool> friend class table_iterator;
|
||||
template<typename,typename,typename,typename> friend class table;
|
||||
|
||||
table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
|
||||
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n},
|
||||
p{const_cast<table_element_type*>(p_)}
|
||||
{}
|
||||
|
||||
inline void increment()noexcept
|
||||
{
|
||||
BOOST_ASSERT(p!=nullptr);
|
||||
increment(std::integral_constant<bool,regular_layout>{});
|
||||
}
|
||||
|
||||
inline void increment(std::true_type /* regular layout */)noexcept
|
||||
{
|
||||
for(;;){
|
||||
++p;
|
||||
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
|
||||
pc+=sizeof(group_type)-(N-1);
|
||||
break;
|
||||
}
|
||||
++pc;
|
||||
if(!group_type::is_occupied(pc))continue;
|
||||
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
int mask=reinterpret_cast<group_type*>(pc)->match_occupied();
|
||||
if(mask!=0){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
}
|
||||
else{
|
||||
pc+=n;
|
||||
p+=n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
}
|
||||
}
|
||||
|
||||
inline void increment(std::false_type /* interleaved */)noexcept
|
||||
{
|
||||
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
|
||||
pc-=n0;
|
||||
|
||||
int mask=(
|
||||
reinterpret_cast<group_type*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
|
||||
if(!mask){
|
||||
do{
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
}
|
||||
while((mask=reinterpret_cast<group_type*>(pc)->match_occupied())==0);
|
||||
}
|
||||
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
}
|
||||
else{
|
||||
pc+=n;
|
||||
p-=n0;
|
||||
p+=n;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *pc=nullptr;
|
||||
table_element_type *p=nullptr;
|
||||
};
|
||||
|
||||
/* foa::table interface departs in a number of ways from that of C++ unordered
|
||||
* associative containers because it's not for end-user consumption
|
||||
* (boost::unordered_[flat|node]_[map|set] wrappers complete it as
|
||||
* appropriate).
|
||||
*
|
||||
* The table supports two main modes of operation: flat and node-based. In the
|
||||
* flat case, buckets directly store elements. For node-based, buckets store
|
||||
* pointers to individually heap-allocated elements.
|
||||
*
|
||||
* For both flat and node-based:
|
||||
*
|
||||
* - begin() is not O(1).
|
||||
* - No bucket API.
|
||||
* - Load factor is fixed and can't be set by the user.
|
||||
*
|
||||
* For flat only:
|
||||
*
|
||||
* - value_type must be moveable.
|
||||
* - Pointer stability is not kept under rehashing.
|
||||
* - No extract API.
|
||||
*
|
||||
* try_emplace, erase and find support heterogeneous lookup by default,
|
||||
* that is, without checking for any ::is_transparent typedefs --the
|
||||
* checking is done by boost::unordered_[flat|node]_[map|set].
|
||||
*/
|
||||
|
||||
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
using table_core_impl=
|
||||
table_core<TypePolicy,group15<plain_integral>,table_arrays,
|
||||
std::size_t,Hash,Pred,Allocator>;
|
||||
|
||||
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
|
||||
{
|
||||
using super=table_core_impl<TypePolicy,Hash,Pred,Allocator>;
|
||||
using type_policy=typename super::type_policy;
|
||||
using group_type=typename super::group_type;
|
||||
using super::N;
|
||||
using prober=typename super::prober;
|
||||
using locator=typename super::locator;
|
||||
|
||||
public:
|
||||
using key_type=typename super::key_type;
|
||||
using init_type=typename super::init_type;
|
||||
using value_type=typename super::value_type;
|
||||
using element_type=typename super::element_type;
|
||||
|
||||
private:
|
||||
static constexpr bool has_mutable_iterator=
|
||||
!std::is_same<key_type,value_type>::value;
|
||||
|
||||
public:
|
||||
using hasher=typename super::hasher;
|
||||
using key_equal=typename super::key_equal;
|
||||
using allocator_type=typename super::allocator_type;
|
||||
using pointer=typename super::pointer;
|
||||
using const_pointer=typename super::const_pointer;
|
||||
using reference=typename super::reference;
|
||||
using const_reference=typename super::const_reference;
|
||||
using size_type=typename super::size_type;
|
||||
using difference_type=typename super::difference_type;
|
||||
using const_iterator=table_iterator<type_policy,group_type,true>;
|
||||
using iterator=typename std::conditional<
|
||||
has_mutable_iterator,
|
||||
table_iterator<type_policy,group_type,false>,
|
||||
const_iterator>::type;
|
||||
|
||||
table(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
super{n,h_,pred_,al_}
|
||||
{}
|
||||
|
||||
table(const table& x)=default;
|
||||
table(table&& x)=default;
|
||||
table(const table& x,const Allocator& al_):super{x,al_}{}
|
||||
table(table&& x,const Allocator& al_):super{std::move(x),al_}{}
|
||||
~table()=default;
|
||||
|
||||
table& operator=(const table& x)=default;
|
||||
table& operator=(table&& x)=default;
|
||||
|
||||
using super::get_allocator;
|
||||
|
||||
iterator begin()noexcept
|
||||
{
|
||||
iterator it{this->arrays.groups,0,this->arrays.elements};
|
||||
if(this->arrays.elements&&
|
||||
!(this->arrays.groups[0].match_occupied()&0x1))++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
const_iterator begin()const noexcept
|
||||
{return const_cast<table*>(this)->begin();}
|
||||
iterator end()noexcept{return {};}
|
||||
const_iterator end()const noexcept{return const_cast<table*>(this)->end();}
|
||||
const_iterator cbegin()const noexcept{return begin();}
|
||||
const_iterator cend()const noexcept{return end();}
|
||||
|
||||
using super::empty;
|
||||
using super::size;
|
||||
using super::max_size;
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
|
||||
{
|
||||
auto x=alloc_make_insert_type<type_policy>(
|
||||
this->al(),std::forward<Args>(args)...);
|
||||
return emplace_impl(type_policy::move(x.value()));
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
|
||||
Key&& x,Args&&... args)
|
||||
{
|
||||
return emplace_impl(
|
||||
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool>
|
||||
insert(const init_type& x){return emplace_impl(x);}
|
||||
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool>
|
||||
insert(init_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
/* template<typename=void> tilts call ambiguities in favor of init_type */
|
||||
|
||||
template<typename=void>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool>
|
||||
insert(const value_type& x){return emplace_impl(x);}
|
||||
|
||||
template<typename=void>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool>
|
||||
insert(value_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
std::pair<iterator,bool>
|
||||
>::type
|
||||
insert(element_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<
|
||||
bool dependent_value=false,
|
||||
typename std::enable_if<
|
||||
has_mutable_iterator||dependent_value>::type* =nullptr
|
||||
>
|
||||
void erase(iterator pos)noexcept{return erase(const_iterator(pos));}
|
||||
|
||||
BOOST_FORCEINLINE
|
||||
void erase(const_iterator pos)noexcept
|
||||
{
|
||||
super::erase(pos.pc,pos.p);
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE
|
||||
auto erase(Key&& x) -> typename std::enable_if<
|
||||
!std::is_convertible<Key,iterator>::value&&
|
||||
!std::is_convertible<Key,const_iterator>::value, std::size_t>::type
|
||||
{
|
||||
auto it=find(x);
|
||||
if(it!=end()){
|
||||
erase(it);
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void swap(table& x)
|
||||
noexcept(noexcept(std::declval<super&>().swap(std::declval<super&>())))
|
||||
{
|
||||
super::swap(x);
|
||||
}
|
||||
|
||||
using super::clear;
|
||||
|
||||
element_type extract(const_iterator pos)
|
||||
{
|
||||
BOOST_ASSERT(pos!=end());
|
||||
erase_on_exit e{*this,pos};
|
||||
(void)e;
|
||||
return std::move(*pos.p);
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
template<typename Hash2,typename Pred2>
|
||||
void merge(table<TypePolicy,Hash2,Pred2,Allocator>& x)
|
||||
{
|
||||
x.for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
|
||||
erase_on_exit e{x,{pg,n,p}};
|
||||
if(!emplace_impl(type_policy::move(*p)).second)e.rollback();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Hash2,typename Pred2>
|
||||
void merge(table<TypePolicy,Hash2,Pred2,Allocator>&& x){merge(x);}
|
||||
|
||||
using super::hash_function;
|
||||
using super::key_eq;
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE iterator find(const Key& x)
|
||||
{
|
||||
auto hash=this->hash_for(x);
|
||||
return find_impl(x,this->position_for(hash),hash);
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE const_iterator find(const Key& x)const
|
||||
{
|
||||
return const_cast<table*>(this)->find(x);
|
||||
}
|
||||
|
||||
using super::capacity;
|
||||
using super::load_factor;
|
||||
using super::max_load_factor;
|
||||
using super::max_load;
|
||||
using super::rehash;
|
||||
using super::reserve;
|
||||
|
||||
template<typename Predicate>
|
||||
friend std::size_t erase_if(table& x,Predicate& pr)
|
||||
{
|
||||
using value_reference=typename std::conditional<
|
||||
std::is_same<key_type,value_type>::value,
|
||||
const_reference,
|
||||
reference
|
||||
>::type;
|
||||
|
||||
std::size_t s=x.size();
|
||||
x.for_all_elements(
|
||||
[&](group_type* pg,unsigned int n,element_type* p){
|
||||
if(pr(const_cast<value_reference>(type_policy::value_from(*p)))){
|
||||
x.super::erase(pg,n,p);
|
||||
}
|
||||
});
|
||||
return std::size_t(s-x.size());
|
||||
}
|
||||
|
||||
private:
|
||||
struct erase_on_exit
|
||||
{
|
||||
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
|
||||
~erase_on_exit(){if(!rollback_)x.erase(it);}
|
||||
|
||||
void rollback(){rollback_=true;}
|
||||
|
||||
table& x;
|
||||
const_iterator it;
|
||||
bool rollback_=false;
|
||||
};
|
||||
|
||||
static inline iterator make_iterator(const locator& l)noexcept
|
||||
{
|
||||
return {l.pg,l.n,l.p};
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
/* warning: forcing value to bool 'true' or 'false' in bool(pred()...) */
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4800)
|
||||
#endif
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE iterator find_impl(
|
||||
const Key& x,std::size_t pos0,std::size_t hash)const
|
||||
{
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
BOOST_UNORDERED_ASSUME(this->arrays.elements!=nullptr);
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
this->prefetch_elements(p);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(bool(this->pred()(x,this->key_from(p[n]))))){
|
||||
return {pg,n,p+n};
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
|
||||
return {}; /* end() */
|
||||
}
|
||||
}
|
||||
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
|
||||
return {}; /* end() */
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4800 */
|
||||
#endif
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
|
||||
{
|
||||
const auto &k=this->key_from(std::forward<Args>(args)...);
|
||||
auto hash=this->hash_for(k);
|
||||
auto pos0=this->position_for(hash);
|
||||
auto it=find_impl(k,pos0,hash);
|
||||
|
||||
if(it!=end()){
|
||||
return {it,false};
|
||||
}
|
||||
if(BOOST_LIKELY(this->size_<this->ml)){
|
||||
return {
|
||||
make_iterator(
|
||||
this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...)),
|
||||
true
|
||||
};
|
||||
}
|
||||
else{
|
||||
return {
|
||||
make_iterator(
|
||||
this->unchecked_emplace_with_rehash(
|
||||
hash,std::forward<Args>(args)...)),
|
||||
true
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4714 */
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa/restore_wshadow.hpp>
|
||||
|
||||
} /* namespace foa */
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/* 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_FOA_TUPLE_ROTATE_RIGHT_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_TUPLE_ROTATE_RIGHT_HPP
|
||||
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<typename Tuple>
|
||||
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
|
||||
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
|
||||
1
|
||||
>;
|
||||
|
||||
template<std::size_t... Is,typename Tuple>
|
||||
tuple_rotate_right_return_type<Tuple>
|
||||
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
|
||||
{
|
||||
return tuple_rotate_right_return_type<Tuple>{
|
||||
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
|
||||
}
|
||||
|
||||
template<typename Tuple>
|
||||
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
|
||||
{
|
||||
using RawTuple=typename std::remove_cv<
|
||||
typename std::remove_reference<Tuple>::type>::type;
|
||||
|
||||
return tuple_rotate_right_aux(
|
||||
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
|
||||
std::forward<Tuple>(x));
|
||||
}
|
||||
|
||||
} /* namespace foa */
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -61,4 +61,90 @@ namespace boost {
|
||||
}
|
||||
}
|
||||
|
||||
// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in
|
||||
// emplace (not including things like hints). Don't set it to a lower value, as
|
||||
// that might break something.
|
||||
|
||||
#if !defined BOOST_UNORDERED_EMPLACE_LIMIT
|
||||
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration
|
||||
//
|
||||
// Unless documented elsewhere these configuration macros should be considered
|
||||
// an implementation detail, I'll try not to break them, but you never know.
|
||||
|
||||
// Use Sun C++ workarounds
|
||||
// I'm not sure which versions of the compiler require these workarounds, so
|
||||
// I'm just using them of everything older than the current test compilers
|
||||
// (as of May 2017).
|
||||
|
||||
#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1)
|
||||
#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0)
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1
|
||||
#else
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_TUPLE_ARGS
|
||||
//
|
||||
// Maximum number of std::tuple members to support, or 0 if std::tuple
|
||||
// isn't avaiable. More are supported when full C++11 is used.
|
||||
|
||||
// Already defined, so do nothing
|
||||
#if defined(BOOST_UNORDERED_TUPLE_ARGS)
|
||||
|
||||
// Assume if we have C++11 tuple it's properly variadic,
|
||||
// and just use a max number of 10 arguments.
|
||||
#elif !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 10
|
||||
|
||||
// Visual C++ has a decent enough tuple for piecewise construction,
|
||||
// so use that if available, using _VARIADIC_MAX for the maximum
|
||||
// number of parameters. Note that this comes after the check
|
||||
// for a full C++11 tuple.
|
||||
#elif defined(BOOST_MSVC)
|
||||
#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#elif defined(_VARIADIC_MAX)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 5
|
||||
#endif
|
||||
|
||||
// Assume that we don't have std::tuple
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_TUPLE_ARGS
|
||||
#include <tuple>
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_CXX11_CONSTRUCTION
|
||||
//
|
||||
// Use C++11 construction, requires variadic arguments, good construct support
|
||||
// in allocator_traits and piecewise construction of std::pair
|
||||
// Otherwise allocators aren't used for construction/destruction
|
||||
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \
|
||||
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && BOOST_UNORDERED_TUPLE_ARGS
|
||||
#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU
|
||||
// Sun C++ std::pair piecewise construction doesn't seem to be exception safe.
|
||||
// (At least for Sun C++ 12.5 using libstdc++).
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0)
|
||||
// Piecewise construction in GCC 4.6 doesn't work for uncopyable types.
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,92 +60,6 @@
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration
|
||||
//
|
||||
// Unless documented elsewhere these configuration macros should be considered
|
||||
// an implementation detail, I'll try not to break them, but you never know.
|
||||
|
||||
// Use Sun C++ workarounds
|
||||
// I'm not sure which versions of the compiler require these workarounds, so
|
||||
// I'm just using them of everything older than the current test compilers
|
||||
// (as of May 2017).
|
||||
|
||||
#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1)
|
||||
#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0)
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1
|
||||
#else
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in
|
||||
// emplace (not including things like hints). Don't set it to a lower value, as
|
||||
// that might break something.
|
||||
|
||||
#if !defined BOOST_UNORDERED_EMPLACE_LIMIT
|
||||
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_TUPLE_ARGS
|
||||
//
|
||||
// Maximum number of std::tuple members to support, or 0 if std::tuple
|
||||
// isn't avaiable. More are supported when full C++11 is used.
|
||||
|
||||
// Already defined, so do nothing
|
||||
#if defined(BOOST_UNORDERED_TUPLE_ARGS)
|
||||
|
||||
// Assume if we have C++11 tuple it's properly variadic,
|
||||
// and just use a max number of 10 arguments.
|
||||
#elif !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 10
|
||||
|
||||
// Visual C++ has a decent enough tuple for piecewise construction,
|
||||
// so use that if available, using _VARIADIC_MAX for the maximum
|
||||
// number of parameters. Note that this comes after the check
|
||||
// for a full C++11 tuple.
|
||||
#elif defined(BOOST_MSVC)
|
||||
#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#elif defined(_VARIADIC_MAX)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 5
|
||||
#endif
|
||||
|
||||
// Assume that we don't have std::tuple
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_TUPLE_ARGS
|
||||
#include <tuple>
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_CXX11_CONSTRUCTION
|
||||
//
|
||||
// Use C++11 construction, requires variadic arguments, good construct support
|
||||
// in allocator_traits and piecewise construction of std::pair
|
||||
// Otherwise allocators aren't used for construction/destruction
|
||||
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \
|
||||
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && BOOST_UNORDERED_TUPLE_ARGS
|
||||
#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU
|
||||
// Sun C++ std::pair piecewise construction doesn't seem to be exception safe.
|
||||
// (At least for Sun C++ 12.5 using libstdc++).
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0)
|
||||
// Piecewise construction in GCC 4.6 doesn't work for uncopyable types.
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_CXX11_CONSTRUCTION
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
|
||||
// Copyright 2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
|
||||
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
|
||||
defined(BOOST_NO_CXX11_DECLTYPE) || \
|
||||
defined(BOOST_NO_CXX11_CONSTEXPR) || \
|
||||
defined(BOOST_NO_CXX11_NOEXCEPT)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_map_fwd.hpp>
|
||||
|
||||
@@ -32,10 +32,8 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_map
|
||||
{
|
||||
struct map_types
|
||||
namespace detail {
|
||||
template <class Key, class T> struct flat_map_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using raw_key_type = typename std::remove_const<Key>::type;
|
||||
@@ -45,19 +43,56 @@ namespace boost {
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
using value_type = std::pair<Key const, T>;
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
static value_type& value_from(element_type& x) { return x; }
|
||||
|
||||
template <class K, class V>
|
||||
static raw_key_type const& extract(std::pair<K, V> const& kv)
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
|
||||
static moved_type move(value_type& x)
|
||||
static moved_type move(init_type& x)
|
||||
{
|
||||
return {std::move(x.first), std::move(x.second)};
|
||||
}
|
||||
|
||||
static moved_type move(element_type& x)
|
||||
{
|
||||
// TODO: we probably need to launder here
|
||||
return {std::move(const_cast<raw_key_type&>(x.first)),
|
||||
std::move(const_cast<raw_mapped_type&>(x.second))};
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_map
|
||||
{
|
||||
using map_types = detail::flat_map_types<Key, T>;
|
||||
|
||||
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_set_fwd.hpp>
|
||||
|
||||
@@ -30,17 +30,38 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_set
|
||||
{
|
||||
struct set_types
|
||||
namespace detail {
|
||||
template <class Key> struct flat_set_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using init_type = Key;
|
||||
using value_type = Key;
|
||||
|
||||
static Key const& extract(value_type const& key) { return key; }
|
||||
static Key&& move(value_type& x) { return std::move(x); }
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
static Key& value_from(element_type& x) { return x; }
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_set
|
||||
{
|
||||
using set_types = detail::flat_set_types<Key>;
|
||||
|
||||
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||
#include <boost/core/explicit_operator_bool.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <boost/unordered/detail/fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T> > >
|
||||
class unordered_node_map;
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
|
||||
noexcept(noexcept(lhs.swap(rhs)));
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::unordered_node_map;
|
||||
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,764 @@
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class Key> struct node_set_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using init_type = Key;
|
||||
using value_type = Key;
|
||||
|
||||
static Key const& extract(value_type const& key) { return key; }
|
||||
|
||||
using element_type=foa::element_type<value_type>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
static value_type&& move(value_type& x) { return std::move(x); }
|
||||
|
||||
template <class A>
|
||||
static void construct(A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
construct(al, p, *copy.p);
|
||||
}
|
||||
|
||||
template <typename Allocator>
|
||||
static void construct(
|
||||
Allocator&, element_type* p, element_type&& x) noexcept
|
||||
{
|
||||
p->p = x.p;
|
||||
x.p = nullptr;
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
p->p = boost::to_address(boost::allocator_allocate(al, 1));
|
||||
BOOST_TRY
|
||||
{
|
||||
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<
|
||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
|
||||
1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<typename boost::allocator_pointer<
|
||||
A>::type>::pointer_to(*(p->p)),
|
||||
1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_set;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_set
|
||||
{
|
||||
using set_types = detail::node_set_types<Key>;
|
||||
|
||||
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename set_types::value_type>::type>;
|
||||
|
||||
table_type table_;
|
||||
|
||||
template <class K, class H, class KE, class A, class Pred>
|
||||
typename unordered_node_set<K, H, KE, A>::size_type friend erase_if(
|
||||
unordered_node_set<K, H, KE, A>& set, Pred pred);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = typename set_types::value_type;
|
||||
using init_type = typename set_types::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = Hash;
|
||||
using key_equal = KeyEqual;
|
||||
using allocator_type = Allocator;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
using const_pointer =
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_set_handle<set_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename set_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
detail::foa::insert_return_type<iterator, node_type>;
|
||||
|
||||
unordered_node_set() : unordered_node_set(0) {}
|
||||
|
||||
explicit unordered_node_set(size_type n, hasher const& h = hasher(),
|
||||
key_equal const& pred = key_equal(),
|
||||
allocator_type const& a = allocator_type())
|
||||
: table_(n, h, pred, a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(size_type n, allocator_type const& a)
|
||||
: unordered_node_set(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(size_type n, hasher const& h, allocator_type const& a)
|
||||
: unordered_node_set(n, h, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
unordered_node_set(
|
||||
InputIterator f, InputIterator l, allocator_type const& a)
|
||||
: unordered_node_set(f, l, size_type(0), hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit unordered_node_set(allocator_type const& a)
|
||||
: unordered_node_set(0, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Iterator>
|
||||
unordered_node_set(Iterator first, Iterator last, size_type n = 0,
|
||||
hasher const& h = hasher(), key_equal const& pred = key_equal(),
|
||||
allocator_type const& a = allocator_type())
|
||||
: unordered_node_set(n, h, pred, a)
|
||||
{
|
||||
this->insert(first, last);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
unordered_node_set(
|
||||
InputIt first, InputIt last, size_type n, allocator_type const& a)
|
||||
: unordered_node_set(first, last, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Iterator>
|
||||
unordered_node_set(Iterator first, Iterator last, size_type n,
|
||||
hasher const& h, allocator_type const& a)
|
||||
: unordered_node_set(first, last, n, h, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(unordered_node_set const& other) : table_(other.table_)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(
|
||||
unordered_node_set const& other, allocator_type const& a)
|
||||
: table_(other.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(unordered_node_set&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(unordered_node_set&& other, allocator_type const& al)
|
||||
: table_(std::move(other.table_), al)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(std::initializer_list<value_type> ilist,
|
||||
size_type n = 0, hasher const& h = hasher(),
|
||||
key_equal const& pred = key_equal(),
|
||||
allocator_type const& a = allocator_type())
|
||||
: unordered_node_set(ilist.begin(), ilist.end(), n, h, pred, a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(
|
||||
std::initializer_list<value_type> il, allocator_type const& a)
|
||||
: unordered_node_set(il, size_type(0), hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(std::initializer_list<value_type> init, size_type n,
|
||||
allocator_type const& a)
|
||||
: unordered_node_set(init, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(std::initializer_list<value_type> init, size_type n,
|
||||
hasher const& h, allocator_type const& a)
|
||||
: unordered_node_set(init, n, h, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_set() = default;
|
||||
|
||||
unordered_node_set& operator=(unordered_node_set const& other)
|
||||
{
|
||||
table_ = other.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_node_set& operator=(unordered_node_set&& other) noexcept(
|
||||
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
|
||||
{
|
||||
table_ = std::move(other.table_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
}
|
||||
|
||||
/// Iterators
|
||||
///
|
||||
|
||||
iterator begin() noexcept { return table_.begin(); }
|
||||
const_iterator begin() const noexcept { return table_.begin(); }
|
||||
const_iterator cbegin() const noexcept { return table_.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return table_.end(); }
|
||||
const_iterator end() const noexcept { return table_.end(); }
|
||||
const_iterator cend() const noexcept { return table_.cend(); }
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return table_.empty();
|
||||
}
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
|
||||
size_type max_size() const noexcept { return table_.max_size(); }
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
void clear() noexcept { table_.clear(); }
|
||||
|
||||
BOOST_FORCEINLINE std::pair<iterator, bool> insert(
|
||||
value_type const& value)
|
||||
{
|
||||
return table_.insert(value);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
|
||||
{
|
||||
return table_.insert(std::move(value));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::transparent_non_iterable<K, unordered_node_set>::value,
|
||||
std::pair<iterator, bool> >::type
|
||||
insert(K&& k)
|
||||
{
|
||||
return table_.try_emplace(std::forward<K>(k));
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
|
||||
{
|
||||
return table_.insert(value).first;
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
|
||||
{
|
||||
return table_.insert(std::move(value)).first;
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::transparent_non_iterable<K, unordered_node_set>::value,
|
||||
iterator>::type
|
||||
insert(const_iterator, K&& k)
|
||||
{
|
||||
return table_.try_emplace(std::forward<K>(k)).first;
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator first, InputIterator last)
|
||||
{
|
||||
for (auto pos = first; pos != last; ++pos) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
if (nh.empty()) {
|
||||
return {end(), false, node_type{}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
iterator insert(const_iterator, node_type&& nh)
|
||||
{
|
||||
if (nh.empty()) {
|
||||
return end();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
return itp.first;
|
||||
} else {
|
||||
return itp.first;
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...).first;
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE void erase(const_iterator pos)
|
||||
{
|
||||
return table_.erase(pos);
|
||||
}
|
||||
iterator erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
while (first != last) {
|
||||
this->erase(first++);
|
||||
}
|
||||
return iterator{detail::foa::const_iterator_cast_tag{}, last};
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& key)
|
||||
{
|
||||
return table_.erase(key);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::transparent_non_iterable<K, unordered_node_set>::value,
|
||||
size_type>::type
|
||||
erase(K const& key)
|
||||
{
|
||||
return table_.erase(key);
|
||||
}
|
||||
|
||||
void swap(unordered_node_set& rhs) noexcept(
|
||||
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
|
||||
{
|
||||
table_.swap(rhs.table_);
|
||||
}
|
||||
|
||||
node_type extract(const_iterator pos)
|
||||
{
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
node_type extract(key_type const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
}
|
||||
|
||||
template <class K>
|
||||
typename std::enable_if<
|
||||
boost::unordered::detail::transparent_non_iterable<K,
|
||||
unordered_node_set>::value,
|
||||
node_type>::type
|
||||
extract(K const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
}
|
||||
|
||||
template <class H2, class P2>
|
||||
void merge(unordered_node_set<key_type, H2, P2, allocator_type>& source)
|
||||
{
|
||||
table_.merge(source.table_);
|
||||
}
|
||||
|
||||
template <class H2, class P2>
|
||||
void merge(unordered_node_set<key_type, H2, P2, allocator_type>&& source)
|
||||
{
|
||||
table_.merge(std::move(source.table_));
|
||||
}
|
||||
|
||||
/// Lookup
|
||||
///
|
||||
|
||||
BOOST_FORCEINLINE size_type count(key_type const& key) const
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
return pos != table_.end() ? 1 : 0;
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
count(K const& key) const
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
return pos != table_.end() ? 1 : 0;
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE iterator find(key_type const& key)
|
||||
{
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
|
||||
{
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
|
||||
iterator>::type
|
||||
find(K const& key)
|
||||
{
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
|
||||
const_iterator>::type
|
||||
find(K const& key) const
|
||||
{
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool contains(key_type const& key) const
|
||||
{
|
||||
return this->find(key) != this->end();
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool>::type
|
||||
contains(K const& key) const
|
||||
{
|
||||
return this->find(key) != this->end();
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(key_type const& key)
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
if (pos == table_.end()) {
|
||||
return {pos, pos};
|
||||
}
|
||||
|
||||
auto next = pos;
|
||||
++next;
|
||||
return {pos, next};
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
key_type const& key) const
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
if (pos == table_.end()) {
|
||||
return {pos, pos};
|
||||
}
|
||||
|
||||
auto next = pos;
|
||||
++next;
|
||||
return {pos, next};
|
||||
}
|
||||
|
||||
template <class K>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
std::pair<iterator, iterator> >::type
|
||||
equal_range(K const& key)
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
if (pos == table_.end()) {
|
||||
return {pos, pos};
|
||||
}
|
||||
|
||||
auto next = pos;
|
||||
++next;
|
||||
return {pos, next};
|
||||
}
|
||||
|
||||
template <class K>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
std::pair<const_iterator, const_iterator> >::type
|
||||
equal_range(K const& key) const
|
||||
{
|
||||
auto pos = table_.find(key);
|
||||
if (pos == table_.end()) {
|
||||
return {pos, pos};
|
||||
}
|
||||
|
||||
auto next = pos;
|
||||
++next;
|
||||
return {pos, next};
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
|
||||
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==(
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
if (&lhs == &rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (lhs.size() == rhs.size()) && ([&] {
|
||||
for (auto const& key : lhs) {
|
||||
auto pos = rhs.find(key);
|
||||
if ((pos == rhs.end()) || (key != *pos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
|
||||
noexcept(noexcept(lhs.swap(rhs)))
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator,
|
||||
class Pred>
|
||||
typename unordered_node_set<Key, Hash, KeyEqual, Allocator>::size_type
|
||||
erase_if(unordered_node_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
|
||||
{
|
||||
return erase_if(set.table_, pred);
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4714 */
|
||||
#endif
|
||||
|
||||
#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> > >
|
||||
unordered_node_set(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> unordered_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Hash = boost::hash<T>,
|
||||
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
|
||||
class = 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> > >
|
||||
unordered_node_set(std::initializer_list<T>,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> unordered_node_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> > >
|
||||
unordered_node_set(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> unordered_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = 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> > >
|
||||
unordered_node_set(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> unordered_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_node_set(std::initializer_list<T>, std::size_t, Allocator)
|
||||
-> unordered_node_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> > >
|
||||
unordered_node_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
|
||||
-> unordered_node_set<T, Hash, std::equal_to<T>, 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> > >
|
||||
unordered_node_set(InputIterator, InputIterator, Allocator)
|
||||
-> unordered_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_node_set(std::initializer_list<T>, Allocator)
|
||||
-> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <boost/unordered/detail/fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key> >
|
||||
class unordered_node_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
|
||||
noexcept(noexcept(lhs.swap(rhs)));
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::unordered_node_set;
|
||||
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||
#include <boost/core/explicit_operator_bool.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Copyright 2006-2008 Daniel James.
|
||||
# Copyright 2022 Christian Mazakas
|
||||
# Copyright 2022-2023 Christian Mazakas
|
||||
# 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)
|
||||
|
||||
@@ -26,6 +26,7 @@ project
|
||||
|
||||
<toolset>gcc-4.4:<cxxflags>-Wno-strict-aliasing
|
||||
<toolset>gcc-4.4:<cxxflags>-fno-deduce-init-list
|
||||
<toolset>clang-14:<cxxflags>-Wunused-template
|
||||
|
||||
<toolset>gcc:<warnings-as-errors>on
|
||||
<toolset>clang:<warnings-as-errors>on
|
||||
@@ -94,6 +95,7 @@ run exception/erase_exception_tests.cpp ;
|
||||
run exception/rehash_exception_tests.cpp ;
|
||||
run exception/swap_exception_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
|
||||
run exception/merge_exception_tests.cpp ;
|
||||
run exception/less_tests.cpp ;
|
||||
|
||||
run unordered/narrow_cast_tests.cpp ;
|
||||
run quick.cpp ;
|
||||
@@ -102,47 +104,51 @@ import ../../config/checks/config : requires ;
|
||||
|
||||
CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ;
|
||||
|
||||
rule build_foa ( name )
|
||||
local FOA_TESTS =
|
||||
fwd_set_test
|
||||
fwd_map_test
|
||||
compile_set
|
||||
compile_map
|
||||
noexcept_tests
|
||||
incomplete_test
|
||||
simple_tests
|
||||
equivalent_keys_tests
|
||||
constructor_tests
|
||||
copy_tests
|
||||
move_tests
|
||||
post_move_tests
|
||||
assign_tests
|
||||
insert_tests
|
||||
insert_hint_tests
|
||||
emplace_tests
|
||||
erase_tests
|
||||
merge_tests
|
||||
find_tests
|
||||
at_tests
|
||||
load_factor_tests
|
||||
rehash_tests
|
||||
equality_tests
|
||||
swap_tests
|
||||
transparent_tests
|
||||
reserve_tests
|
||||
contains_tests
|
||||
erase_if
|
||||
scary_tests
|
||||
init_type_insert_tests
|
||||
max_load_tests
|
||||
extract_tests
|
||||
node_handle_tests
|
||||
uses_allocator
|
||||
;
|
||||
|
||||
for local test in $(FOA_TESTS)
|
||||
{
|
||||
run unordered/$(name).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(name) ;
|
||||
run unordered/$(test).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
|
||||
}
|
||||
|
||||
build_foa fwd_set_test ;
|
||||
build_foa fwd_map_test ;
|
||||
build_foa compile_set ;
|
||||
build_foa compile_map ;
|
||||
build_foa noexcept_tests ;
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
|
||||
build_foa incomplete_test ;
|
||||
build_foa simple_tests ;
|
||||
build_foa equivalent_keys_tests ;
|
||||
build_foa constructor_tests ;
|
||||
build_foa copy_tests ;
|
||||
build_foa move_tests ;
|
||||
build_foa post_move_tests ;
|
||||
build_foa assign_tests ;
|
||||
build_foa insert_tests ;
|
||||
build_foa insert_hint_tests ;
|
||||
build_foa emplace_tests ;
|
||||
build_foa erase_tests ;
|
||||
build_foa merge_tests ;
|
||||
build_foa find_tests ;
|
||||
build_foa at_tests ;
|
||||
build_foa load_factor_tests ;
|
||||
build_foa rehash_tests ;
|
||||
build_foa equality_tests ;
|
||||
build_foa swap_tests ;
|
||||
run unordered/scoped_allocator.cpp : : : $(CPP11) <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
|
||||
build_foa transparent_tests ;
|
||||
build_foa reserve_tests ;
|
||||
build_foa contains_tests ;
|
||||
build_foa erase_if ;
|
||||
build_foa scary_tests ;
|
||||
build_foa init_type_insert_tests ;
|
||||
build_foa max_load_tests ;
|
||||
|
||||
run unordered/hash_is_avalanching_test.cpp ;
|
||||
|
||||
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
|
||||
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
|
||||
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
|
||||
@@ -152,3 +158,39 @@ run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORD
|
||||
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
|
||||
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
|
||||
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
|
||||
|
||||
alias foa_tests :
|
||||
foa_$(FOA_TESTS)
|
||||
foa_link_test
|
||||
foa_scoped_allocator
|
||||
hash_is_avalanching_test
|
||||
foa_constructor_exception_tests
|
||||
foa_copy_exception_tests
|
||||
foa_assign_exception_tests
|
||||
foa_move_assign_exception_tests
|
||||
foa_insert_exception_tests
|
||||
foa_erase_exception_tests
|
||||
foa_rehash_exception_tests
|
||||
foa_swap_exception_tests
|
||||
foa_merge_exception_tests
|
||||
;
|
||||
|
||||
local CFOA_TESTS =
|
||||
insert_tests
|
||||
erase_tests
|
||||
try_emplace_tests
|
||||
emplace_tests
|
||||
visit_tests
|
||||
constructor_tests
|
||||
assign_tests
|
||||
;
|
||||
|
||||
for local test in $(CFOA_TESTS)
|
||||
{
|
||||
run cfoa/$(test).cpp
|
||||
: requirements $(CPP11)
|
||||
: target-name cfoa_$(test)
|
||||
;
|
||||
}
|
||||
|
||||
alias cfoa_tests : cfoa_$(CFOA_TESTS) ;
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
test::seed_t initialize_seed{2762556623};
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
|
||||
namespace {
|
||||
template <class G> void copy_assign(G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
// to test:
|
||||
// self-assign
|
||||
// propagation
|
||||
//
|
||||
|
||||
// lhs empty, rhs empty
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
|
||||
map_type y;
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST(y.empty());
|
||||
y = x;
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
|
||||
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
|
||||
BOOST_TEST(x.get_allocator() != y.get_allocator());
|
||||
}
|
||||
|
||||
// lhs non-empty, rhs empty
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type y(values.begin(), values.end(), values.size());
|
||||
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_size = y.size();
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST(!y.empty());
|
||||
y = x;
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, 2 * old_size);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
|
||||
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
|
||||
BOOST_TEST(x.get_allocator() != y.get_allocator());
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
// lhs empty, rhs non-empty
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
map_type y;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
|
||||
BOOST_TEST(!x.empty());
|
||||
BOOST_TEST(y.empty());
|
||||
y = x;
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc + (2 * x.size()));
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
|
||||
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
|
||||
BOOST_TEST(x.get_allocator() != y.get_allocator());
|
||||
|
||||
test_matches_reference(y, reference_map);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
// lhs non-empty, rhs non-empty
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
map_type y(values.begin(), values.end(), values.size());
|
||||
|
||||
auto const old_size = y.size();
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
|
||||
BOOST_TEST(!x.empty());
|
||||
BOOST_TEST(!y.empty());
|
||||
y = x;
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, 2 * old_size);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc + (2 * x.size()));
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
|
||||
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
|
||||
BOOST_TEST(x.get_allocator() != y.get_allocator());
|
||||
}
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
copy_assign,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,814 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
test::seed_t initialize_seed(4122023);
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
template <class T> struct soccc_allocator
|
||||
{
|
||||
int x_ = -1;
|
||||
|
||||
using value_type = T;
|
||||
|
||||
soccc_allocator() = default;
|
||||
soccc_allocator(soccc_allocator const&) = default;
|
||||
soccc_allocator(soccc_allocator&&) = default;
|
||||
|
||||
soccc_allocator(int const x) : x_{x} {}
|
||||
|
||||
template <class U> soccc_allocator(soccc_allocator<U> const& rhs) : x_{rhs.x_}
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
soccc_allocator select_on_container_copy_construction() const
|
||||
{
|
||||
return {x_ + 1};
|
||||
}
|
||||
|
||||
bool operator==(soccc_allocator const& rhs) const { return x_ == rhs.x_; }
|
||||
bool operator!=(soccc_allocator const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
|
||||
using map_value_type = typename map_type::value_type;
|
||||
|
||||
UNORDERED_AUTO_TEST (default_constructor) {
|
||||
boost::unordered::concurrent_flat_map<raii, raii> x;
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (bucket_count_with_hasher_key_equal_and_allocator) {
|
||||
raii::reset_counts();
|
||||
{
|
||||
map_type x(0);
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1));
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2));
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type{});
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (soccc) {
|
||||
raii::reset_counts();
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, hasher, key_equal,
|
||||
soccc_allocator<std::pair<raii const, raii> > >
|
||||
x;
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, hasher, key_equal,
|
||||
soccc_allocator<std::pair<raii const, raii> > >
|
||||
y(x);
|
||||
|
||||
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
|
||||
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
|
||||
BOOST_TEST(y.get_allocator() != x.get_allocator());
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <class G> void from_iterator_range(G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end());
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0);
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1));
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2));
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
|
||||
allocator_type{});
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type{});
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G> void copy_constructor(G gen, test::random_generator rg)
|
||||
{
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type{});
|
||||
map_type y(x);
|
||||
|
||||
BOOST_TEST_EQ(y.size(), x.size());
|
||||
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
|
||||
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
}
|
||||
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
|
||||
allocator_type{});
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
map_type y(x);
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
test_matches_reference(y, reference_map);
|
||||
BOOST_TEST_EQ(y.size(), x.size());
|
||||
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
|
||||
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
});
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
allocator_type a;
|
||||
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), a);
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, a](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
map_type y(x, a);
|
||||
|
||||
test_matches_reference(x, reference_map);
|
||||
test_matches_reference(y, reference_map);
|
||||
BOOST_TEST_EQ(y.size(), x.size());
|
||||
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
|
||||
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
});
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void copy_constructor_with_insertion(G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool ready = false;
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type{});
|
||||
|
||||
auto f = [&x, &values, &m, &cv, &ready] {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m);
|
||||
ready = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
|
||||
for (auto const& val : values) {
|
||||
x.insert(val);
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t1(f);
|
||||
std::thread t2(f);
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, &values, rg, &m, &cv, &ready](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
cv.wait(lk, [&] { return ready; });
|
||||
}
|
||||
|
||||
map_type y(x);
|
||||
|
||||
BOOST_TEST_LE(y.size(), values.size());
|
||||
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
|
||||
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
|
||||
x.visit_all([&reference_map, rg](
|
||||
typename map_type::value_type const& val) {
|
||||
BOOST_TEST(reference_map.contains(val.first));
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(val.second, reference_map.find(val.first)->second);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G> void move_constructor(G gen, test::random_generator rg)
|
||||
{
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type{});
|
||||
auto const old_size = x.size();
|
||||
|
||||
map_type y(std::move(x));
|
||||
|
||||
BOOST_TEST_EQ(y.size(), old_size);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
}
|
||||
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
|
||||
allocator_type{});
|
||||
|
||||
std::atomic_uint num_transfers{0};
|
||||
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, &num_transfers](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
|
||||
auto const old_size = x.size();
|
||||
map_type y(std::move(x));
|
||||
|
||||
if (!y.empty()) {
|
||||
++num_transfers;
|
||||
|
||||
test_matches_reference(y, reference_map);
|
||||
BOOST_TEST_EQ(y.size(), old_size);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
|
||||
} else {
|
||||
BOOST_TEST_EQ(y.size(), 0u);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_transfers, 1u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
|
||||
// allocator-aware move constructor, unequal allocators
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
|
||||
allocator_type{1});
|
||||
|
||||
std::atomic_uint num_transfers{0};
|
||||
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
auto const old_size = x.size();
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, &num_transfers, old_size](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
|
||||
auto a = allocator_type{2};
|
||||
BOOST_TEST(a != x.get_allocator());
|
||||
|
||||
map_type y(std::move(x), a);
|
||||
|
||||
if (!y.empty()) {
|
||||
++num_transfers;
|
||||
|
||||
test_matches_reference(y, reference_map);
|
||||
BOOST_TEST_EQ(y.size(), old_size);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
|
||||
} else {
|
||||
BOOST_TEST_EQ(y.size(), 0u);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(y.get_allocator() != x.get_allocator());
|
||||
BOOST_TEST(y.get_allocator() == a);
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_transfers, 1u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc + (2 * old_size));
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
|
||||
// allocator-aware move constructor, equal allocators
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
|
||||
allocator_type{1});
|
||||
|
||||
std::atomic_uint num_transfers{0};
|
||||
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
auto const old_size = x.size();
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, &num_transfers, old_size](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
|
||||
auto a = allocator_type{1};
|
||||
BOOST_TEST(a == x.get_allocator());
|
||||
|
||||
map_type y(std::move(x), a);
|
||||
|
||||
if (!y.empty()) {
|
||||
++num_transfers;
|
||||
|
||||
test_matches_reference(y, reference_map);
|
||||
BOOST_TEST_EQ(y.size(), old_size);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
|
||||
} else {
|
||||
BOOST_TEST_EQ(y.size(), 0u);
|
||||
BOOST_TEST_EQ(y.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(y.key_eq(), key_equal());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(y.get_allocator() == x.get_allocator());
|
||||
BOOST_TEST(y.get_allocator() == a);
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_transfers, 1u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void move_constructor_with_insertion(G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool ready = false;
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1), key_equal(2), allocator_type{});
|
||||
|
||||
std::atomic_uint num_transfers{0};
|
||||
|
||||
std::thread t1([&x, &values] {
|
||||
for (auto const& val : values) {
|
||||
x.insert(val);
|
||||
}
|
||||
});
|
||||
|
||||
std::thread t2([&x, &m, &cv, &ready] {
|
||||
while (x.empty()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m);
|
||||
ready = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
thread_runner(
|
||||
values, [&x, &reference_map, &num_transfers, rg, &m, &ready, &cv](
|
||||
boost::span<span_value_type<decltype(values)> > s) {
|
||||
(void)s;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
cv.wait(lk, [&] { return ready; });
|
||||
}
|
||||
|
||||
map_type y(std::move(x));
|
||||
|
||||
if (!y.empty()) {
|
||||
++num_transfers;
|
||||
y.cvisit_all([&reference_map, rg](map_value_type const& val) {
|
||||
BOOST_TEST(reference_map.contains(val.first));
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(
|
||||
val.second, reference_map.find(val.first)->second);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
BOOST_TEST_GE(num_transfers, 1u);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void iterator_range_with_allocator(G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
allocator_type a;
|
||||
map_type x(values.begin(), values.end(), a);
|
||||
|
||||
BOOST_TEST_GT(x.size(), 0u);
|
||||
BOOST_TEST_LE(x.size(), values.size());
|
||||
if (rg == sequential) {
|
||||
BOOST_TEST_EQ(x.size(), values.size());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(x.get_allocator() == a);
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_map, rg);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (explicit_allocator) {
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
allocator_type a;
|
||||
map_type x(a);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
|
||||
BOOST_TEST(x.get_allocator() == a);
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (initializer_list_with_all_params) {
|
||||
std::initializer_list<map_value_type> ilist{
|
||||
map_value_type{raii{0}, raii{0}},
|
||||
map_value_type{raii{1}, raii{1}},
|
||||
map_value_type{raii{2}, raii{2}},
|
||||
map_value_type{raii{3}, raii{3}},
|
||||
map_value_type{raii{4}, raii{4}},
|
||||
map_value_type{raii{5}, raii{5}},
|
||||
map_value_type{raii{6}, raii{6}},
|
||||
map_value_type{raii{6}, raii{6}},
|
||||
map_value_type{raii{7}, raii{7}},
|
||||
map_value_type{raii{8}, raii{8}},
|
||||
map_value_type{raii{9}, raii{9}},
|
||||
map_value_type{raii{10}, raii{10}},
|
||||
map_value_type{raii{9}, raii{9}},
|
||||
map_value_type{raii{8}, raii{8}},
|
||||
map_value_type{raii{7}, raii{7}},
|
||||
map_value_type{raii{6}, raii{6}},
|
||||
map_value_type{raii{5}, raii{5}},
|
||||
map_value_type{raii{4}, raii{4}},
|
||||
map_value_type{raii{3}, raii{3}},
|
||||
map_value_type{raii{2}, raii{2}},
|
||||
map_value_type{raii{1}, raii{1}},
|
||||
map_value_type{raii{0}, raii{0}},
|
||||
};
|
||||
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(ilist, 0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 11u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(ilist, allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 11u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(ilist, 0, allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 11u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type x(ilist, 0, hasher(1), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 11u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (bucket_count_and_allocator) {
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(0, allocator_type(3));
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
}
|
||||
|
||||
{
|
||||
map_type x(4096, allocator_type(3));
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (bucket_count_with_hasher_and_allocator) {
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
map_type x(0, hasher(1), allocator_type(3));
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
}
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void iterator_range_with_bucket_count_and_allocator(
|
||||
G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
allocator_type a(3);
|
||||
map_type x(values.begin(), values.end(), 0, a);
|
||||
test_fuzzy_matches_reference(x, reference_map, rg);
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), hasher());
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == a);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G>
|
||||
void iterator_range_with_bucket_count_hasher_and_allocator(
|
||||
G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
allocator_type a(3);
|
||||
hasher hf(1);
|
||||
map_type x(values.begin(), values.end(), 0, hf, a);
|
||||
test_fuzzy_matches_reference(x, reference_map, rg);
|
||||
|
||||
BOOST_TEST_EQ(x.hash_function(), hf);
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal());
|
||||
BOOST_TEST(x.get_allocator() == a);
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
from_iterator_range,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor_with_insertion,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor_with_insertion,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_allocator,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_bucket_count_and_allocator,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_bucket_count_hasher_and_allocator,
|
||||
((value_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(335740237);
|
||||
|
||||
struct lvalue_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto const& r : s) {
|
||||
bool b = x.emplace(r.first.x_, r.second.x_);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_emplacer;
|
||||
|
||||
struct norehash_lvalue_emplacer_type : public lvalue_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
lvalue_emplacer_type::operator()(values, x);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
|
||||
}
|
||||
} norehash_lvalue_emplacer;
|
||||
|
||||
struct lvalue_emplace_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.emplace_or_cvisit(
|
||||
r.first.x_, r.second.x_,
|
||||
[&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
} lvalue_emplace_or_cvisit;
|
||||
|
||||
struct lvalue_emplace_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.emplace_or_visit(
|
||||
r.first.x_, r.second.x_,
|
||||
[&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
} lvalue_emplace_or_visit;
|
||||
|
||||
template <class X, class G, class F>
|
||||
void emplace(X*, G gen, F emplacer, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
|
||||
emplacer(values, x);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
if (rg == test::sequential) {
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
// clang-format off
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace,
|
||||
((map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_emplacer)(norehash_lvalue_emplacer)
|
||||
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,368 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(3292023);
|
||||
|
||||
struct lvalue_eraser_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_erased{0};
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor + 2 * x.size());
|
||||
|
||||
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
|
||||
for (auto const& k : values) {
|
||||
auto count = x.erase(k.first);
|
||||
num_erased += count;
|
||||
BOOST_TEST_LE(count, 1u);
|
||||
BOOST_TEST_GE(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(num_erased, old_size);
|
||||
}
|
||||
} lvalue_eraser;
|
||||
|
||||
struct transp_lvalue_eraser_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_erased{0};
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor + 2 * x.size());
|
||||
|
||||
thread_runner(values, [&num_erased, &x](boost::span<T> s) {
|
||||
for (auto const& k : s) {
|
||||
auto count = x.erase(k.first.x_);
|
||||
num_erased += count;
|
||||
BOOST_TEST_LE(count, 1u);
|
||||
BOOST_TEST_GE(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), 0u);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(num_erased, old_size);
|
||||
}
|
||||
} transp_lvalue_eraser;
|
||||
|
||||
struct lvalue_eraser_if_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_erased{0};
|
||||
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
auto max = 0;
|
||||
x.visit_all([&max](value_type const& v) {
|
||||
if (v.second.x_ > max) {
|
||||
max = v.second.x_;
|
||||
}
|
||||
});
|
||||
|
||||
auto threshold = max / 2;
|
||||
|
||||
auto expected_erasures = 0u;
|
||||
x.visit_all([&expected_erasures, threshold](value_type const& v) {
|
||||
if (v.second.x_ > threshold) {
|
||||
++expected_erasures;
|
||||
}
|
||||
});
|
||||
|
||||
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
|
||||
for (auto const& k : s) {
|
||||
auto count = x.erase_if(k.first,
|
||||
[threshold](value_type& v) { return v.second.x_ > threshold; });
|
||||
num_erased += count;
|
||||
BOOST_TEST_LE(count, 1u);
|
||||
BOOST_TEST_GE(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_erased, expected_erasures);
|
||||
BOOST_TEST_EQ(x.size(), old_size - num_erased);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
|
||||
}
|
||||
} lvalue_eraser_if;
|
||||
|
||||
struct transp_lvalue_eraser_if_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_erased{0};
|
||||
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
auto max = 0;
|
||||
x.visit_all([&max](value_type const& v) {
|
||||
if (v.second.x_ > max) {
|
||||
max = v.second.x_;
|
||||
}
|
||||
});
|
||||
|
||||
auto threshold = max / 2;
|
||||
|
||||
auto expected_erasures = 0u;
|
||||
x.visit_all([&expected_erasures, threshold](value_type const& v) {
|
||||
if (v.second.x_ > threshold) {
|
||||
++expected_erasures;
|
||||
}
|
||||
});
|
||||
|
||||
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
|
||||
for (auto const& k : s) {
|
||||
auto count = x.erase_if(k.first.x_,
|
||||
[threshold](value_type& v) { return v.second.x_ > threshold; });
|
||||
num_erased += count;
|
||||
BOOST_TEST_LE(count, 1u);
|
||||
BOOST_TEST_GE(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_erased, expected_erasures);
|
||||
BOOST_TEST_EQ(x.size(), old_size - num_erased);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
|
||||
}
|
||||
} transp_lvalue_eraser_if;
|
||||
|
||||
struct erase_if_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_erased{0};
|
||||
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
auto max = 0;
|
||||
x.visit_all([&max](value_type const& v) {
|
||||
if (v.second.x_ > max) {
|
||||
max = v.second.x_;
|
||||
}
|
||||
});
|
||||
|
||||
auto threshold = max / 2;
|
||||
|
||||
auto expected_erasures = 0u;
|
||||
x.visit_all([&expected_erasures, threshold](value_type const& v) {
|
||||
if (v.second.x_ > threshold) {
|
||||
++expected_erasures;
|
||||
}
|
||||
});
|
||||
|
||||
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
|
||||
for (auto const& k : s) {
|
||||
(void)k;
|
||||
auto count = x.erase_if(
|
||||
[threshold](value_type& v) { return v.second.x_ > threshold; });
|
||||
num_erased += count;
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_erased, expected_erasures);
|
||||
BOOST_TEST_EQ(x.size(), old_size - num_erased);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
|
||||
}
|
||||
} erase_if;
|
||||
|
||||
struct erase_if_exec_policy_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
|
||||
auto const old_size = x.size();
|
||||
|
||||
auto const old_dc = +raii::default_constructor;
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
auto const old_mc = +raii::move_constructor;
|
||||
|
||||
auto const old_d = +raii::destructor;
|
||||
|
||||
auto max = 0;
|
||||
x.visit_all([&max](value_type const& v) {
|
||||
if (v.second.x_ > max) {
|
||||
max = v.second.x_;
|
||||
}
|
||||
});
|
||||
|
||||
auto threshold = max / 2;
|
||||
|
||||
auto expected_erasures = 0u;
|
||||
x.visit_all([&expected_erasures, threshold](value_type const& v) {
|
||||
if (v.second.x_ > threshold) {
|
||||
++expected_erasures;
|
||||
}
|
||||
});
|
||||
|
||||
thread_runner(values, [&num_invokes, &x, threshold](boost::span<T> s) {
|
||||
(void)s;
|
||||
x.erase_if(
|
||||
std::execution::par_unseq, [&num_invokes, threshold](value_type& v) {
|
||||
++num_invokes;
|
||||
return v.second.x_ > threshold;
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_GE(+num_invokes, old_size);
|
||||
BOOST_TEST_LE(+num_invokes, old_size * num_threads);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
|
||||
BOOST_TEST_EQ(raii::move_constructor, old_mc);
|
||||
|
||||
BOOST_TEST_EQ(raii::destructor, old_d + 2 * expected_erasures);
|
||||
#else
|
||||
(void)values;
|
||||
(void)x;
|
||||
#endif
|
||||
}
|
||||
} erase_if_exec_policy;
|
||||
|
||||
template <class X, class G, class F>
|
||||
void erase(X*, G gen, F eraser, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
|
||||
x.insert(values.begin(), values.end());
|
||||
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
if (rg == test::sequential) {
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}
|
||||
}));
|
||||
|
||||
eraser(values, x);
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((transparent_map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,390 @@
|
||||
#ifndef BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
|
||||
#define BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
|
||||
|
||||
#include "../helpers/generators.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
constexpr std::size_t const num_threads = 16;
|
||||
|
||||
struct transp_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
template <class T> std::size_t operator()(T const& t) const noexcept
|
||||
{
|
||||
std::this_thread::yield();
|
||||
return boost::hash<T>()(t);
|
||||
}
|
||||
};
|
||||
|
||||
struct transp_key_equal
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
template <class T, class U> bool operator()(T const& lhs, U const& rhs) const
|
||||
{
|
||||
std::this_thread::yield();
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct stateful_hash
|
||||
{
|
||||
int x_ = -1;
|
||||
|
||||
stateful_hash() = default;
|
||||
stateful_hash(stateful_hash const&) = default;
|
||||
stateful_hash(stateful_hash&& rhs) noexcept
|
||||
{
|
||||
auto tmp = x_;
|
||||
x_ = rhs.x_;
|
||||
rhs.x_ = tmp;
|
||||
}
|
||||
|
||||
stateful_hash(int const x) : x_{x} {}
|
||||
|
||||
template <class T> std::size_t operator()(T const& t) const noexcept
|
||||
{
|
||||
std::size_t h = static_cast<std::size_t>(x_);
|
||||
boost::hash_combine(h, t);
|
||||
std::this_thread::yield();
|
||||
return h;
|
||||
}
|
||||
|
||||
bool operator==(stateful_hash const& rhs) const { return x_ == rhs.x_; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, stateful_hash const& rhs)
|
||||
{
|
||||
os << "{ x_: " << rhs.x_ << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
friend void swap(stateful_hash& lhs, stateful_hash& rhs) noexcept
|
||||
{
|
||||
if (&lhs != &rhs) {
|
||||
std::swap(lhs.x_, rhs.x_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct stateful_key_equal
|
||||
{
|
||||
int x_ = -1;
|
||||
|
||||
stateful_key_equal() = default;
|
||||
stateful_key_equal(stateful_key_equal const&) = default;
|
||||
stateful_key_equal(stateful_key_equal&& rhs) noexcept
|
||||
{
|
||||
auto tmp = x_;
|
||||
x_ = rhs.x_;
|
||||
rhs.x_ = tmp;
|
||||
}
|
||||
|
||||
stateful_key_equal(int const x) : x_{x} {}
|
||||
|
||||
template <class T, class U> bool operator()(T const& t, U const& u) const
|
||||
{
|
||||
std::this_thread::yield();
|
||||
return t == u;
|
||||
}
|
||||
|
||||
bool operator==(stateful_key_equal const& rhs) const { return x_ == rhs.x_; }
|
||||
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& os, stateful_key_equal const& rhs)
|
||||
{
|
||||
os << "{ x_: " << rhs.x_ << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
friend void swap(stateful_key_equal& lhs, stateful_key_equal& rhs) noexcept
|
||||
{
|
||||
if (&lhs != &rhs) {
|
||||
std::swap(lhs.x_, rhs.x_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct stateful_allocator
|
||||
{
|
||||
int x_ = -1;
|
||||
|
||||
using value_type = T;
|
||||
|
||||
stateful_allocator() = default;
|
||||
stateful_allocator(stateful_allocator const&) = default;
|
||||
stateful_allocator(stateful_allocator&&) = default;
|
||||
|
||||
stateful_allocator(int const x) : x_{x} {}
|
||||
|
||||
template <class U>
|
||||
stateful_allocator(stateful_allocator<U> const& rhs) : x_{rhs.x_}
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
bool operator==(stateful_allocator const& rhs) const { return x_ == rhs.x_; }
|
||||
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
struct raii
|
||||
{
|
||||
static std::atomic<std::uint32_t> default_constructor;
|
||||
static std::atomic<std::uint32_t> copy_constructor;
|
||||
static std::atomic<std::uint32_t> move_constructor;
|
||||
static std::atomic<std::uint32_t> destructor;
|
||||
|
||||
static std::atomic<std::uint32_t> copy_assignment;
|
||||
static std::atomic<std::uint32_t> move_assignment;
|
||||
|
||||
int x_ = -1;
|
||||
|
||||
raii() { ++default_constructor; }
|
||||
raii(int const x) : x_{x} { ++default_constructor; }
|
||||
raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; }
|
||||
raii(raii&& rhs) noexcept : x_{rhs.x_}
|
||||
{
|
||||
rhs.x_ = -1;
|
||||
++move_constructor;
|
||||
}
|
||||
~raii() { ++destructor; }
|
||||
|
||||
raii& operator=(raii const& rhs)
|
||||
{
|
||||
++copy_assignment;
|
||||
if (this != &rhs) {
|
||||
x_ = rhs.x_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
raii& operator=(raii&& rhs) noexcept
|
||||
{
|
||||
++move_assignment;
|
||||
if (this != &rhs) {
|
||||
x_ = rhs.x_;
|
||||
rhs.x_ = -1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==(raii const& lhs, raii const& rhs)
|
||||
{
|
||||
return lhs.x_ == rhs.x_;
|
||||
}
|
||||
|
||||
friend bool operator!=(raii const& lhs, raii const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend bool operator==(raii const& lhs, int const x) { return lhs.x_ == x; }
|
||||
friend bool operator!=(raii const& lhs, int const x)
|
||||
{
|
||||
return !(lhs.x_ == x);
|
||||
}
|
||||
|
||||
friend bool operator==(int const x, raii const& rhs) { return rhs.x_ == x; }
|
||||
|
||||
friend bool operator!=(int const x, raii const& rhs)
|
||||
{
|
||||
return !(rhs.x_ == x);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, raii const& rhs)
|
||||
{
|
||||
os << "{ x_: " << rhs.x_ << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& os, std::pair<raii const, raii> const& rhs)
|
||||
{
|
||||
os << "pair<" << rhs.first << ", " << rhs.second << ">";
|
||||
return os;
|
||||
}
|
||||
|
||||
static void reset_counts()
|
||||
{
|
||||
default_constructor = 0;
|
||||
copy_constructor = 0;
|
||||
move_constructor = 0;
|
||||
destructor = 0;
|
||||
copy_assignment = 0;
|
||||
move_assignment = 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::atomic<std::uint32_t> raii::default_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::copy_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::move_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::destructor{0};
|
||||
std::atomic<std::uint32_t> raii::copy_assignment{0};
|
||||
std::atomic<std::uint32_t> raii::move_assignment{0};
|
||||
|
||||
std::size_t hash_value(raii const& r) noexcept
|
||||
{
|
||||
boost::hash<int> hasher;
|
||||
return hasher(r.x_);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
|
||||
{
|
||||
using vector_type = std::vector<decltype(f())>;
|
||||
|
||||
vector_type v;
|
||||
v.reserve(count);
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
v.emplace_back(f());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_type_generator_type
|
||||
{
|
||||
std::pair<raii const, raii> operator()(test::random_generator rg)
|
||||
{
|
||||
int* p = nullptr;
|
||||
int a = generate(p, rg);
|
||||
int b = generate(p, rg);
|
||||
return std::make_pair(raii{a}, raii{b});
|
||||
}
|
||||
} value_type_generator;
|
||||
|
||||
struct init_type_generator_type
|
||||
{
|
||||
std::pair<raii, raii> operator()(test::random_generator rg)
|
||||
{
|
||||
int* p = nullptr;
|
||||
int a = generate(p, rg);
|
||||
int b = generate(p, rg);
|
||||
return std::make_pair(raii{a}, raii{b});
|
||||
}
|
||||
} init_type_generator;
|
||||
|
||||
template <class T>
|
||||
std::vector<boost::span<T> > split(
|
||||
boost::span<T> s, std::size_t const nt /* num threads*/)
|
||||
{
|
||||
std::vector<boost::span<T> > subslices;
|
||||
subslices.reserve(nt);
|
||||
|
||||
auto a = s.size() / nt;
|
||||
auto b = a;
|
||||
if (s.size() % nt != 0) {
|
||||
++b;
|
||||
}
|
||||
|
||||
auto num_a = nt;
|
||||
auto num_b = std::size_t{0};
|
||||
|
||||
if (nt * b > s.size()) {
|
||||
num_a = nt * b - s.size();
|
||||
num_b = nt - num_a;
|
||||
}
|
||||
|
||||
auto sub_b = s.subspan(0, num_b * b);
|
||||
auto sub_a = s.subspan(num_b * b);
|
||||
|
||||
for (std::size_t i = 0; i < num_b; ++i) {
|
||||
subslices.push_back(sub_b.subspan(i * b, b));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < num_a; ++i) {
|
||||
auto const is_last = i == (num_a - 1);
|
||||
subslices.push_back(
|
||||
sub_a.subspan(i * a, is_last ? boost::dynamic_extent : a));
|
||||
}
|
||||
|
||||
return subslices;
|
||||
}
|
||||
|
||||
template <class T, class F> void thread_runner(std::vector<T>& values, F f)
|
||||
{
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
std::size_t c = 0;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
auto subslices = split<T>(values, num_threads);
|
||||
|
||||
for (std::size_t i = 0; i < num_threads; ++i) {
|
||||
threads.emplace_back([&f, &subslices, i, &m, &cv, &c] {
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
++c;
|
||||
if (c == num_threads) {
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
} else {
|
||||
cv.wait(lk, [&] { return c == num_threads; });
|
||||
}
|
||||
}
|
||||
|
||||
auto s = subslices[i];
|
||||
f(s);
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
void test_matches_reference(X const& x, Y const& reference_map)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
}));
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
void test_fuzzy_matches_reference(
|
||||
X const& x, Y const& reference_map, test::random_generator rg)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
if (rg == test::sequential) {
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
template <class T> using span_value_type = typename T::value_type;
|
||||
|
||||
void check_raii_counts()
|
||||
{
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::default_constructor + raii::copy_constructor + raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
|
||||
@@ -0,0 +1,585 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(78937);
|
||||
|
||||
struct lvalue_inserter_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto const& r : s) {
|
||||
bool b = x.insert(r);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_inserter;
|
||||
|
||||
struct norehash_lvalue_inserter_type : public lvalue_inserter_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
lvalue_inserter_type::operator()(values, x);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
} norehash_lvalue_inserter;
|
||||
|
||||
struct rvalue_inserter_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.insert(std::move(r));
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} rvalue_inserter;
|
||||
|
||||
struct norehash_rvalue_inserter_type : public rvalue_inserter_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
|
||||
rvalue_inserter_type::operator()(values, x);
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
|
||||
}
|
||||
}
|
||||
} norehash_rvalue_inserter;
|
||||
|
||||
struct iterator_range_inserter_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x](boost::span<T> s) { x.insert(s.begin(), s.end()); });
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} iterator_range_inserter;
|
||||
|
||||
struct lvalue_insert_or_assign_copy_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(r.first, r.second);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_assign_copy_assign;
|
||||
|
||||
struct lvalue_insert_or_assign_move_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(r.first, std::move(r.second));
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
} lvalue_insert_or_assign_move_assign;
|
||||
|
||||
struct rvalue_insert_or_assign_copy_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(std::move(r.first), r.second);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} rvalue_insert_or_assign_copy_assign;
|
||||
|
||||
struct rvalue_insert_or_assign_move_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(std::move(r.first), std::move(r.second));
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
} rvalue_insert_or_assign_move_assign;
|
||||
|
||||
struct trans_insert_or_assign_copy_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using is_transparent =
|
||||
typename boost::make_void<typename X::hasher::is_transparent,
|
||||
typename X::key_equal::is_transparent>::type;
|
||||
|
||||
boost::ignore_unused<is_transparent>();
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(r.first.x_, r.second);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} trans_insert_or_assign_copy_assign;
|
||||
|
||||
struct trans_insert_or_assign_move_assign_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using is_transparent =
|
||||
typename boost::make_void<typename X::hasher::is_transparent,
|
||||
typename X::key_equal::is_transparent>::type;
|
||||
|
||||
boost::ignore_unused<is_transparent>();
|
||||
|
||||
thread_runner(values, [&x](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
x.insert_or_assign(r.first.x_, std::move(r.second));
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::move_constructor, 2 * x.size()); // rehashing
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
} trans_insert_or_assign_move_assign;
|
||||
|
||||
struct lvalue_insert_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.insert_or_cvisit(
|
||||
r, [&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_cvisit;
|
||||
|
||||
struct lvalue_insert_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b =
|
||||
x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_visit;
|
||||
|
||||
struct rvalue_insert_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.insert_or_cvisit(
|
||||
std::move(r), [&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_insert_or_cvisit;
|
||||
|
||||
struct rvalue_insert_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.insert_or_visit(
|
||||
std::move(r), [&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_insert_or_visit;
|
||||
|
||||
struct iterator_range_insert_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
|
||||
x.insert_or_cvisit(
|
||||
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
} iterator_range_insert_or_cvisit;
|
||||
|
||||
struct iterator_range_insert_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
|
||||
x.insert_or_visit(
|
||||
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
} iterator_range_insert_or_visit;
|
||||
|
||||
template <class X, class G, class F>
|
||||
void insert(X*, G gen, F inserter, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
|
||||
inserter(values, x);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
if (rg == test::sequential) {
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
template <class X> void insert_initializer_list(X*)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::initializer_list<value_type> values{
|
||||
value_type{raii{0}, raii{0}},
|
||||
value_type{raii{1}, raii{1}},
|
||||
value_type{raii{2}, raii{2}},
|
||||
value_type{raii{3}, raii{3}},
|
||||
value_type{raii{4}, raii{4}},
|
||||
value_type{raii{5}, raii{5}},
|
||||
value_type{raii{6}, raii{6}},
|
||||
value_type{raii{6}, raii{6}},
|
||||
value_type{raii{7}, raii{7}},
|
||||
value_type{raii{8}, raii{8}},
|
||||
value_type{raii{9}, raii{9}},
|
||||
value_type{raii{10}, raii{10}},
|
||||
value_type{raii{9}, raii{9}},
|
||||
value_type{raii{8}, raii{8}},
|
||||
value_type{raii{7}, raii{7}},
|
||||
value_type{raii{6}, raii{6}},
|
||||
value_type{raii{5}, raii{5}},
|
||||
value_type{raii{4}, raii{4}},
|
||||
value_type{raii{3}, raii{3}},
|
||||
value_type{raii{2}, raii{2}},
|
||||
value_type{raii{1}, raii{1}},
|
||||
value_type{raii{0}, raii{0}},
|
||||
};
|
||||
|
||||
std::vector<raii> dummy;
|
||||
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
{
|
||||
X x;
|
||||
|
||||
thread_runner(
|
||||
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
|
||||
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
|
||||
X x;
|
||||
|
||||
thread_runner(dummy, [&x, &values, &num_invokes](boost::span<raii>) {
|
||||
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
x.insert_or_cvisit(
|
||||
values, [&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) +
|
||||
(num_threads - 1) * values.size() +
|
||||
num_threads * values.size());
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* trans_map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
insert_initializer_list,
|
||||
((map)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
|
||||
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
|
||||
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
|
||||
(rvalue_insert_or_cvisit)(rvalue_insert_or_visit)
|
||||
(iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map))
|
||||
((init_type_generator))
|
||||
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
|
||||
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((trans_map))
|
||||
((init_type_generator))
|
||||
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,396 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(511933564);
|
||||
|
||||
struct lvalue_try_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto const& r : s) {
|
||||
bool b = x.try_emplace(r.first, r.second.x_);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_try_emplacer;
|
||||
|
||||
struct norehash_lvalue_try_emplacer_type : public lvalue_try_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
lvalue_try_emplacer_type::operator()(values, x);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
} norehash_lvalue_try_emplacer;
|
||||
|
||||
struct rvalue_try_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace(std::move(r.first), r.second.x_);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} rvalue_try_emplacer;
|
||||
|
||||
struct norehash_rvalue_try_emplacer_type : public rvalue_try_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
|
||||
rvalue_try_emplacer_type::operator()(values, x);
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
}
|
||||
}
|
||||
} norehash_rvalue_try_emplacer;
|
||||
|
||||
struct transp_try_emplace_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
using is_transparent =
|
||||
typename boost::make_void<typename X::hasher::is_transparent,
|
||||
typename X::key_equal::is_transparent>::type;
|
||||
|
||||
boost::ignore_unused<is_transparent>();
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace(r.first.x_, r.second.x_);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} transp_try_emplace;
|
||||
|
||||
struct norehash_transp_try_emplace_type : public transp_try_emplace_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
x.reserve(values.size());
|
||||
transp_try_emplace_type::operator()(values, x);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
} norehash_transp_try_emplace;
|
||||
|
||||
struct lvalue_try_emplace_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_cvisit(
|
||||
r.first, r.second.x_,
|
||||
[&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
} lvalue_try_emplace_or_cvisit;
|
||||
|
||||
struct lvalue_try_emplace_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_visit(
|
||||
r.first, r.second.x_,
|
||||
[&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
} lvalue_try_emplace_or_visit;
|
||||
|
||||
struct rvalue_try_emplace_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_cvisit(
|
||||
std::move(r.first), r.second.x_,
|
||||
[&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_try_emplace_or_cvisit;
|
||||
|
||||
struct rvalue_try_emplace_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_visit(
|
||||
std::move(r.first), r.second.x_,
|
||||
[&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
}
|
||||
} rvalue_try_emplace_or_visit;
|
||||
|
||||
struct transp_try_emplace_or_cvisit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_cvisit(
|
||||
r.first.x_, r.second.x_,
|
||||
[&num_invokes](typename X::value_type const& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
}
|
||||
} transp_try_emplace_or_cvisit;
|
||||
|
||||
struct transp_try_emplace_or_visit_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
std::atomic<std::uint64_t> num_invokes{0};
|
||||
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.try_emplace_or_visit(
|
||||
r.first.x_, r.second.x_,
|
||||
[&num_invokes](typename X::value_type& v) {
|
||||
(void)v;
|
||||
++num_invokes;
|
||||
});
|
||||
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
}
|
||||
} transp_try_emplace_or_visit;
|
||||
|
||||
template <class X, class G, class F>
|
||||
void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
|
||||
try_emplacer(values, x);
|
||||
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
using value_type = typename X::value_type;
|
||||
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
if (rg == test::sequential) {
|
||||
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
try_emplace,
|
||||
((map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_try_emplacer)(norehash_lvalue_try_emplacer)
|
||||
(rvalue_try_emplacer)(norehash_rvalue_try_emplacer)
|
||||
(lvalue_try_emplace_or_cvisit)(lvalue_try_emplace_or_visit)
|
||||
(rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
try_emplace,
|
||||
((transp_map))
|
||||
((init_type_generator))
|
||||
((transp_try_emplace)(norehash_transp_try_emplace)
|
||||
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,461 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(335740237);
|
||||
|
||||
struct lvalue_visitor_type
|
||||
{
|
||||
template <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
std::atomic<std::uint64_t> total_count{0};
|
||||
|
||||
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
|
||||
BOOST_TEST(reference_map.contains(v.first));
|
||||
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
|
||||
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
|
||||
BOOST_TEST(reference_map.contains(v.first));
|
||||
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto count = x.visit(val.first, mut_visitor);
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = x.visit(val.second, mut_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto const& y = x;
|
||||
auto count = y.visit(val.first, const_visitor);
|
||||
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = y.visit(val.second, const_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto count = x.cvisit(val.first, const_visitor);
|
||||
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = x.cvisit(val.second, const_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
}
|
||||
} lvalue_visitor;
|
||||
|
||||
struct transp_visitor_type
|
||||
{
|
||||
template <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
std::atomic<std::uint64_t> total_count{0};
|
||||
|
||||
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
|
||||
BOOST_TEST(reference_map.contains(v.first));
|
||||
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
|
||||
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
|
||||
BOOST_TEST(reference_map.contains(v.first));
|
||||
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto count = x.visit(val.first.x_, mut_visitor);
|
||||
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = x.visit(val.second.x_, mut_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto const& y = x;
|
||||
auto count = y.visit(val.first.x_, const_visitor);
|
||||
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = y.visit(val.second.x_, const_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
|
||||
for (auto const& val : s) {
|
||||
auto r = val.first.x_;
|
||||
BOOST_TEST(r >= 0);
|
||||
|
||||
auto count = x.cvisit(val.first.x_, const_visitor);
|
||||
|
||||
BOOST_TEST_EQ(count, 1u);
|
||||
total_count += count;
|
||||
|
||||
count = x.cvisit(val.second.x_, const_visitor);
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(num_visits, values.size());
|
||||
BOOST_TEST_EQ(total_count, values.size());
|
||||
|
||||
num_visits = 0;
|
||||
total_count = 0;
|
||||
}
|
||||
}
|
||||
} transp_visitor;
|
||||
|
||||
struct visit_all_type
|
||||
{
|
||||
template <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
std::atomic<std::uint64_t> total_count{0};
|
||||
|
||||
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
|
||||
return [&reference_map, &num_visits](value_type& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
};
|
||||
|
||||
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
|
||||
return [&reference_map, &num_visits](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
};
|
||||
|
||||
{
|
||||
thread_runner(values, [&x, &total_count, &mut_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
total_count += x.visit_all(mut_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(total_count, num_threads * x.size());
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &total_count, &const_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
auto const& y = x;
|
||||
total_count += y.visit_all(const_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(total_count, num_threads * x.size());
|
||||
total_count = 0;
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(
|
||||
values, [&x, &total_count, &const_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
total_count += x.cvisit_all(const_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(total_count, num_threads * x.size());
|
||||
total_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} visit_all;
|
||||
|
||||
struct exec_policy_visit_all_type
|
||||
{
|
||||
template <class T, class X, class M>
|
||||
void operator()(std::vector<T>& values, X& x, M const& reference_map)
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
using value_type = typename X::value_type;
|
||||
|
||||
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
|
||||
return [&reference_map, &num_visits](value_type& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
};
|
||||
|
||||
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
|
||||
return [&reference_map, &num_visits](value_type const& kv) {
|
||||
BOOST_TEST(reference_map.contains(kv.first));
|
||||
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
|
||||
++num_visits;
|
||||
};
|
||||
};
|
||||
|
||||
{
|
||||
thread_runner(values, [&x, &mut_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
|
||||
x.visit_all(std::execution::par_unseq, mut_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(values, [&x, &const_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
auto const& y = x;
|
||||
|
||||
y.visit_all(std::execution::par_unseq, const_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
thread_runner(values, [&x, &const_visitor](boost::span<T>) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
x.cvisit_all(std::execution::par_unseq, const_visitor(num_visits));
|
||||
BOOST_TEST_EQ(x.size(), num_visits);
|
||||
});
|
||||
}
|
||||
#else
|
||||
(void)values;
|
||||
(void)x;
|
||||
(void)reference_map;
|
||||
#endif
|
||||
}
|
||||
} exec_policy_visit_all;
|
||||
|
||||
template <class X, class G, class F>
|
||||
void visit(X*, G gen, F visitor, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
for (auto& val : values) {
|
||||
if (val.second.x_ == 0) {
|
||||
val.second.x_ = 1;
|
||||
}
|
||||
val.second.x_ *= -1;
|
||||
}
|
||||
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
for (auto const& v : values) {
|
||||
x.insert(v);
|
||||
}
|
||||
BOOST_TEST_EQ(x.size(), reference_map.size());
|
||||
|
||||
std::uint64_t old_default_constructor = raii::default_constructor;
|
||||
std::uint64_t old_copy_constructor = raii::copy_constructor;
|
||||
std::uint64_t old_move_constructor = raii::move_constructor;
|
||||
std::uint64_t old_copy_assignment = raii::copy_assignment;
|
||||
std::uint64_t old_move_assignment = raii::move_assignment;
|
||||
|
||||
visitor(values, x, reference_map);
|
||||
|
||||
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
|
||||
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
|
||||
BOOST_TEST_EQ(old_move_constructor, raii::move_constructor);
|
||||
BOOST_TEST_EQ(old_copy_assignment, raii::copy_assignment);
|
||||
BOOST_TEST_EQ(old_move_assignment, raii::move_assignment);
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::destructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
|
||||
raii::move_constructor,
|
||||
raii::destructor);
|
||||
}
|
||||
|
||||
template <class X, class G>
|
||||
void empty_visit(X*, G gen, test::random_generator rg)
|
||||
{
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
using values_type = decltype(values);
|
||||
using span_value_type = typename values_type::value_type;
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
X x;
|
||||
|
||||
std::uint64_t old_default_constructor = raii::default_constructor;
|
||||
std::uint64_t old_copy_constructor = raii::copy_constructor;
|
||||
std::uint64_t old_move_constructor = raii::move_constructor;
|
||||
std::uint64_t old_copy_assignment = raii::copy_assignment;
|
||||
std::uint64_t old_move_assignment = raii::move_assignment;
|
||||
|
||||
{
|
||||
thread_runner(values, [&x](boost::span<span_value_type> s) {
|
||||
std::atomic<std::uint64_t> num_visits{0};
|
||||
|
||||
x.visit_all(
|
||||
[&num_visits](typename X::value_type const&) { ++num_visits; });
|
||||
BOOST_TEST_EQ(num_visits, 0u);
|
||||
|
||||
for (auto const& val : s) {
|
||||
auto count = x.visit(val.first,
|
||||
[&num_visits](typename X::value_type const&) { ++num_visits; });
|
||||
BOOST_TEST_EQ(count, 0u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
|
||||
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
|
||||
BOOST_TEST_EQ(old_move_constructor, raii::move_constructor);
|
||||
BOOST_TEST_EQ(old_copy_assignment, raii::copy_assignment);
|
||||
BOOST_TEST_EQ(old_move_assignment, raii::move_assignment);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::sequential;
|
||||
|
||||
// clang-format off
|
||||
|
||||
UNORDERED_TEST(
|
||||
visit,
|
||||
((map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_visitor)(visit_all)(exec_policy_visit_all))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
visit,
|
||||
((transp_map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((transp_visitor))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
empty_visit,
|
||||
((map)(transp_map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((default_generator)(sequential)(limited_range))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -25,8 +25,24 @@ typedef boost::unordered_flat_set<
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_pair_set;
|
||||
|
||||
#define CONTAINER_SEQ (test_set)(test_map)
|
||||
#define CONTAINER_PAIR_SEQ (test_pair_set)(test_map)
|
||||
typedef boost::unordered_node_set<test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_node_set;
|
||||
|
||||
typedef boost::unordered_node_map<test::exception::object,
|
||||
test::exception::object, test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >
|
||||
test_node_map;
|
||||
|
||||
typedef boost::unordered_node_set<
|
||||
std::pair<test::exception::object, test::exception::object>,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_pair_node_set;
|
||||
|
||||
#define CONTAINER_SEQ (test_set)(test_map)(test_node_set)(test_node_map)
|
||||
#define CONTAINER_PAIR_SEQ (test_pair_set)(test_map)(test_pair_node_set)(test_node_map)
|
||||
#else
|
||||
typedef boost::unordered_set<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#include "./containers.hpp"
|
||||
@@ -229,10 +229,12 @@ using test::generate_collisions;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test_set* test_set_;
|
||||
test_map* test_map_;
|
||||
test_node_set* test_node_set_;
|
||||
test_node_map* test_node_map_;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_set_)(test_map_))
|
||||
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
|
||||
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
|
||||
(insert_lvalue_pos)(insert_single_item_range)
|
||||
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
|
||||
@@ -242,7 +244,7 @@ UNORDERED_TEST(insert_exception_test,
|
||||
)
|
||||
|
||||
UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((test_set_)(test_map_))
|
||||
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
|
||||
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
|
||||
(insert_lvalue_pos)(insert_single_item_range)
|
||||
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
|
||||
@@ -314,15 +316,16 @@ struct pair_emplace2_type : inserter_base
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test_pair_set* test_pair_set_;
|
||||
test_pair_node_set* test_pair_node_set_;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_pair_set_)(test_map_))
|
||||
((test_pair_set_)(test_map_)(test_pair_node_set_)(test_node_map_))
|
||||
((pair_emplace)(pair_emplace2))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((test_pair_set_)(test_map_))
|
||||
((test_pair_set_)(test_map_)(test_pair_node_set_)(test_node_map_))
|
||||
((pair_emplace)(pair_emplace2))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
@@ -399,6 +402,20 @@ struct map_insert_or_assign_type : map_inserter_base
|
||||
}
|
||||
} map_insert_or_assign;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_map_)(test_node_map_))
|
||||
((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((test_map_)(test_node_map_))
|
||||
((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
#else
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_map_))
|
||||
@@ -411,6 +428,7 @@ UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
// Range insert tests
|
||||
|
||||
@@ -459,12 +477,12 @@ void insert_range_rehash_exception_test(T*, test::random_generator gen)
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_range_exception_test,
|
||||
((test_set_)(test_map_))
|
||||
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
|
||||
UNORDERED_TEST(insert_range_rehash_exception_test,
|
||||
((test_set_)(test_map_))
|
||||
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
// Copyright 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/helpers.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/strong.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
UNORDERED_AUTO_TEST (less_osx_regression) {
|
||||
DISABLE_EXCEPTIONS;
|
||||
typedef test_pair_set::value_type value_type;
|
||||
typedef test::exception::object object;
|
||||
|
||||
std::vector<value_type> v;
|
||||
v.push_back(value_type(object(12, 98), object(88, 13)));
|
||||
v.push_back(value_type(object(24, 71), object(62, 84)));
|
||||
v.push_back(value_type(object(30, 0), object(5, 73)));
|
||||
v.push_back(value_type(object(34, 64), object(79, 58)));
|
||||
v.push_back(value_type(object(36, 95), object(64, 23)));
|
||||
v.push_back(value_type(object(42, 89), object(68, 44)));
|
||||
v.push_back(value_type(object(42, 26), object(93, 64)));
|
||||
v.push_back(value_type(object(86, 86), object(16, 62)));
|
||||
v.push_back(value_type(object(86, 86), object(75, 23)));
|
||||
v.push_back(value_type(object(92, 37), object(41, 90)));
|
||||
|
||||
BOOST_TEST_EQ(v.size(), 10u);
|
||||
|
||||
std::set<value_type, test::exception::less> s;
|
||||
s.insert(v.begin(), v.end());
|
||||
BOOST_TEST_EQ(s.size(), v.size());
|
||||
|
||||
test::ordered<test_pair_set> tracker;
|
||||
test_pair_set x;
|
||||
for (std::vector<value_type>::iterator it = v.begin(); it != v.end();
|
||||
++it) {
|
||||
x.insert(*it);
|
||||
}
|
||||
|
||||
tracker.insert(v.begin(), v.end());
|
||||
tracker.compare(x);
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2017-2018 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -64,6 +64,12 @@ boost::unordered_flat_set<test::exception::object, test::exception::hash,
|
||||
boost::unordered_flat_map<test::exception::object, test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >* test_map_;
|
||||
boost::unordered_node_set<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >* test_node_set_;
|
||||
boost::unordered_node_map<test::exception::object, test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >* test_node_map_;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_MULTI_TEST(set_merge, merge_exception_test,
|
||||
@@ -99,6 +105,40 @@ UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test,
|
||||
((generate_collisions))
|
||||
((generate_collisions))
|
||||
)
|
||||
UNORDERED_MULTI_TEST(node_set_merge, merge_exception_test,
|
||||
((test_node_set_))
|
||||
((test_node_set_))
|
||||
(/* (0x0000)(0x6400) */(0x0064)/* (0x0a64)(0x3232) */)
|
||||
((0x0000)(0x0001)(0x0102))
|
||||
((default_generator)(limited_range))
|
||||
((default_generator)(limited_range))
|
||||
)
|
||||
UNORDERED_MULTI_TEST(node_map_merge, merge_exception_test,
|
||||
((test_node_map_))
|
||||
((test_node_map_))
|
||||
((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232))
|
||||
((0x0101)(0x0200)(0x0201))
|
||||
((default_generator)(limited_range))
|
||||
((default_generator)(limited_range))
|
||||
)
|
||||
// Run fewer generate_collisions tests, as they're slow.
|
||||
UNORDERED_MULTI_TEST(node_set_merge_collisions, merge_exception_test,
|
||||
((test_node_set_))
|
||||
((test_node_set_))
|
||||
((0x0a0a))
|
||||
((0x0202)(0x0100)(0x0201))
|
||||
((generate_collisions))
|
||||
((generate_collisions))
|
||||
)
|
||||
UNORDERED_MULTI_TEST(node_map_merge_collisions, merge_exception_test,
|
||||
((test_node_map_))
|
||||
((test_node_map_))
|
||||
((0x0a0a))
|
||||
((0x0000)(0x0002)(0x0102))
|
||||
((generate_collisions))
|
||||
((generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
#else
|
||||
boost::unordered_set<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
|
||||
@@ -247,8 +247,14 @@ using unordered_flat_set = boost::unordered_flat_set<int, boost::hash<int>,
|
||||
std::equal_to<int>, test::allocator1<int> >;
|
||||
using unordered_flat_map = boost::unordered_flat_map<int, int, boost::hash<int>,
|
||||
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
|
||||
using unordered_node_set = boost::unordered_node_set<int, boost::hash<int>,
|
||||
std::equal_to<int>, test::allocator1<int> >;
|
||||
using unordered_node_map = boost::unordered_node_map<int, int, boost::hash<int>,
|
||||
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
|
||||
|
||||
#define SWAP_CONTAINER_SEQ (unordered_flat_set)(unordered_flat_map)
|
||||
#define SWAP_CONTAINER_SEQ \
|
||||
(unordered_flat_set)(unordered_flat_map) \
|
||||
(unordered_node_set)(unordered_node_map)
|
||||
|
||||
#else
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_TEST_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_TEST_HEADER
|
||||
|
||||
#include <boost/unordered/detail/fwd.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
@@ -42,9 +42,8 @@ namespace test {
|
||||
value_list values2(x2.begin(), x2.end());
|
||||
values1.sort();
|
||||
values2.sort();
|
||||
BOOST_TEST(values1.size() == values2.size() &&
|
||||
test::equal(values1.begin(), values1.end(), values2.begin(),
|
||||
test::equivalent));
|
||||
BOOST_TEST_ALL_WITH(values1.begin(), values1.end(), values2.begin(),
|
||||
values2.end(), test::equivalent);
|
||||
}
|
||||
|
||||
template <class X1, class X2, class T>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#else
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace test {
|
||||
if (less_impl(x1.first, x2.first)) {
|
||||
return true;
|
||||
}
|
||||
if (!less_impl(x1.first, x2.first)) {
|
||||
if (less_impl(x2.first, x1.first)) {
|
||||
return false;
|
||||
}
|
||||
return less_impl(x1.second, x2.second);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -209,40 +209,75 @@ namespace assign_tests {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, std::allocator<test::object> >* test_map_std_alloc;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, std::allocator<test::object> >* test_node_map_std_alloc;
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_set;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_node_set;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator2<test::object> >* test_map;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator2<test::object> >* test_node_map;
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_set_prop_assign;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_node_set_prop_assign;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_map_prop_assign;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::propagate_assign> >*
|
||||
test_node_map_prop_assign;
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_set_no_prop_assign;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_node_set_no_prop_assign;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_map_no_prop_assign;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
|
||||
test_node_map_no_prop_assign;
|
||||
|
||||
UNORDERED_AUTO_TEST (check_traits) {
|
||||
BOOST_TEST(!is_propagate(test_set));
|
||||
BOOST_TEST(is_propagate(test_set_prop_assign));
|
||||
BOOST_TEST(!is_propagate(test_set_no_prop_assign));
|
||||
|
||||
BOOST_TEST(!is_propagate(test_node_set));
|
||||
BOOST_TEST(is_propagate(test_node_set_prop_assign));
|
||||
BOOST_TEST(!is_propagate(test_node_set_no_prop_assign));
|
||||
}
|
||||
|
||||
UNORDERED_TEST(assign_tests1,
|
||||
((test_map_std_alloc)(test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))(
|
||||
((test_map_std_alloc)(test_node_map_std_alloc)
|
||||
(test_set)(test_node_set)
|
||||
(test_map)(test_node_map)
|
||||
(test_set_prop_assign)(test_node_set_prop_assign)
|
||||
(test_map_prop_assign)(test_node_map_prop_assign)
|
||||
(test_set_no_prop_assign)(test_node_set_no_prop_assign)
|
||||
(test_map_no_prop_assign)(test_node_map_no_prop_assign))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(assign_tests2,
|
||||
((test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))(
|
||||
((test_set)(test_node_set)
|
||||
(test_map)(test_node_map)
|
||||
(test_set_prop_assign)(test_node_set_prop_assign)
|
||||
(test_map_prop_assign)(test_node_map_prop_assign)
|
||||
(test_set_no_prop_assign)(test_node_set_no_prop_assign)
|
||||
(test_map_no_prop_assign)(test_node_map_no_prop_assign))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
#else
|
||||
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
|
||||
@@ -315,6 +350,12 @@ namespace assign_tests {
|
||||
std::initializer_list<std::pair<int const, int> > init;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<int, int> x1;
|
||||
boost::unordered_node_map<int, int> x2;
|
||||
x2[25] = 3;
|
||||
x2[16] = 10;
|
||||
BOOST_TEST(!x2.empty());
|
||||
x2 = init;
|
||||
BOOST_TEST(x2.empty());
|
||||
#else
|
||||
boost::unordered_map<int, int> x1;
|
||||
#endif
|
||||
@@ -333,6 +374,12 @@ namespace assign_tests {
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> x;
|
||||
boost::unordered_node_set<int> y;
|
||||
y.insert(10);
|
||||
y.insert(20);
|
||||
y = {1, 2, -10};
|
||||
BOOST_TEST(y.find(10) == y.end());
|
||||
BOOST_TEST(y.find(-10) != y.end());
|
||||
#else
|
||||
boost::unordered_set<int> x;
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2007-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -11,16 +11,12 @@
|
||||
|
||||
namespace at_tests {
|
||||
|
||||
UNORDERED_AUTO_TEST (at_tests) {
|
||||
template <class X> static void at_tests(X*)
|
||||
{
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Create Map" << std::endl;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<std::string, int> x;
|
||||
boost::unordered_flat_map<std::string, int> const& x_const(x);
|
||||
#else
|
||||
boost::unordered_map<std::string, int> x;
|
||||
boost::unordered_map<std::string, int> const& x_const(x);
|
||||
#endif
|
||||
X x;
|
||||
X const& x_const(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check empty container" << std::endl;
|
||||
|
||||
@@ -64,6 +60,21 @@ namespace at_tests {
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Finished" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_flat_map<std::string, int>* test_map;
|
||||
static boost::unordered_node_map<std::string, int>* test_node_map;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(at_tests, ((test_map)(test_node_map)))
|
||||
// clang-format on
|
||||
#else
|
||||
static boost::unordered_map<std::string, int>* test_map;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(at_tests, ((test_map)))
|
||||
// clang-format on
|
||||
#endif
|
||||
} // namespace at_tests
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -35,6 +35,22 @@ template class instantiate_flat_map<test::minimal::assignable const,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
template <typename K, typename T, typename H, typename P, typename A>
|
||||
class instantiate_node_map
|
||||
{
|
||||
typedef boost::unordered_node_map<K, T, H, P, A> container;
|
||||
container x;
|
||||
};
|
||||
|
||||
template class instantiate_node_map<int, int, boost::hash<int>,
|
||||
std::equal_to<int>, test::minimal::allocator<int> >;
|
||||
|
||||
template class instantiate_node_map<test::minimal::assignable const,
|
||||
test::minimal::default_assignable const,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
#else
|
||||
#define INSTANTIATE(type) \
|
||||
template class boost::unordered::detail::instantiate_##type
|
||||
@@ -55,149 +71,101 @@ INSTANTIATE(multimap)<test::minimal::assignable, test::minimal::assignable,
|
||||
test::minimal::allocator<int> >;
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (test0) {
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void test0_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
typedef std::pair<test::minimal::assignable const, test::minimal::assignable>
|
||||
value_type;
|
||||
value_type value(x, x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
|
||||
Map<int, int> int_map;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<int, int> int_map;
|
||||
|
||||
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_map2;
|
||||
|
||||
boost::unordered_flat_map<test::minimal::assignable,
|
||||
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
map;
|
||||
#else
|
||||
boost::unordered_map<int, int> int_map;
|
||||
|
||||
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_map2;
|
||||
|
||||
boost::unordered_map<test::minimal::assignable, test::minimal::assignable,
|
||||
Map<test::minimal::assignable, test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
map;
|
||||
#endif
|
||||
|
||||
container_test(int_map, std::pair<int const, int>(0, 0));
|
||||
container_test(int_map2, std::pair<int const, int>(0, 0));
|
||||
container_test(map, value);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
|
||||
|
||||
boost::unordered_multimap<int, int> int_multimap;
|
||||
|
||||
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_multimap2;
|
||||
|
||||
boost::unordered_multimap<test::minimal::assignable,
|
||||
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
multimap;
|
||||
|
||||
container_test(int_multimap, std::pair<int const, int>(0, 0));
|
||||
container_test(int_multimap2, std::pair<int const, int>(0, 0));
|
||||
container_test(multimap, value);
|
||||
UNORDERED_AUTO_TEST (test0) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test0_impl<boost::unordered_flat_map>();
|
||||
test0_impl<boost::unordered_node_map>();
|
||||
#else
|
||||
test0_impl<boost::unordered_map>();
|
||||
test0_impl<boost::unordered_multimap>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (equality_tests) {
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void equality_tests_impl()
|
||||
{
|
||||
typedef std::pair<test::minimal::copy_constructible_equality_comparable const,
|
||||
test::minimal::copy_constructible_equality_comparable>
|
||||
value_type;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<int, int> int_map;
|
||||
Map<int, int> int_map;
|
||||
|
||||
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_map2;
|
||||
|
||||
boost::unordered_flat_map<
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
Map<test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
map;
|
||||
#else
|
||||
boost::unordered_map<int, int> int_map;
|
||||
|
||||
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_map2;
|
||||
|
||||
boost::unordered_map<test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
map;
|
||||
#endif
|
||||
|
||||
equality_test(int_map);
|
||||
equality_test(int_map2);
|
||||
equality_test(map);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_multimap<int, int> int_multimap;
|
||||
|
||||
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
int_multimap2;
|
||||
|
||||
boost::unordered_multimap<
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
multimap;
|
||||
|
||||
equality_test(int_multimap);
|
||||
equality_test(int_multimap2);
|
||||
equality_test(multimap);
|
||||
UNORDERED_AUTO_TEST (equality_tests) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
equality_tests_impl<boost::unordered_flat_map>();
|
||||
equality_tests_impl<boost::unordered_node_map>();
|
||||
#else
|
||||
equality_tests_impl<boost::unordered_map>();
|
||||
equality_tests_impl<boost::unordered_multimap>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (test1) {
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void test1_unique_impl()
|
||||
{
|
||||
|
||||
boost::hash<int> hash;
|
||||
std::equal_to<int> equal_to;
|
||||
int value = 0;
|
||||
std::pair<int const, int> map_value(0, 0);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
|
||||
Map<int, int> map;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<int, int> map;
|
||||
|
||||
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
map2;
|
||||
#else
|
||||
boost::unordered_map<int, int> map;
|
||||
|
||||
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
map2;
|
||||
#endif
|
||||
|
||||
unordered_unique_test(map, map_value);
|
||||
unordered_map_test(map, value, value);
|
||||
@@ -208,27 +176,53 @@ UNORDERED_AUTO_TEST (test1) {
|
||||
unordered_map_test(map2, value, value);
|
||||
unordered_copyable_test(map2, value, map_value, hash, equal_to);
|
||||
unordered_map_functions(map2, value, value);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void test1_equivalent_impl()
|
||||
{
|
||||
|
||||
boost::unordered_multimap<int, int> multimap;
|
||||
boost::hash<int> hash;
|
||||
std::equal_to<int> equal_to;
|
||||
int value = 0;
|
||||
std::pair<int const, int> map_value(0, 0);
|
||||
|
||||
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
Map<int, int> map;
|
||||
|
||||
Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<std::pair<int const, int> > >
|
||||
multimap2;
|
||||
map2;
|
||||
|
||||
unordered_equivalent_test(multimap, map_value);
|
||||
unordered_map_test(multimap, value, value);
|
||||
unordered_copyable_test(multimap, value, map_value, hash, equal_to);
|
||||
unordered_equivalent_test(map, map_value);
|
||||
unordered_map_test(map, value, value);
|
||||
unordered_copyable_test(map, value, map_value, hash, equal_to);
|
||||
|
||||
unordered_equivalent_test(multimap2, map_value);
|
||||
unordered_map_test(multimap2, value, value);
|
||||
unordered_copyable_test(multimap2, value, map_value, hash, equal_to);
|
||||
unordered_equivalent_test(map2, map_value);
|
||||
unordered_map_test(map2, value, value);
|
||||
unordered_copyable_test(map2, value, map_value, hash, equal_to);
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (test1) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test1_unique_impl<boost::unordered_flat_map>();
|
||||
test1_unique_impl<boost::unordered_node_map>();
|
||||
#else
|
||||
test1_unique_impl<boost::unordered_map>();
|
||||
test1_equivalent_impl<boost::unordered_multimap>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (test2) {
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void test2_unique_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::assignable assignable(x);
|
||||
@@ -240,60 +234,66 @@ UNORDERED_AUTO_TEST (test2) {
|
||||
map_value_type;
|
||||
map_value_type map_value(assignable, assignable);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<test::minimal::assignable,
|
||||
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<map_value_type> >
|
||||
map;
|
||||
#else
|
||||
boost::unordered_map<test::minimal::assignable, test::minimal::assignable,
|
||||
Map<test::minimal::assignable, test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<map_value_type> >
|
||||
map;
|
||||
#endif
|
||||
|
||||
unordered_unique_test(map, map_value);
|
||||
unordered_map_test(map, assignable, assignable);
|
||||
unordered_copyable_test(map, assignable, map_value, hash, equal_to);
|
||||
unordered_map_member_test(map, map_value);
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<test::minimal::assignable,
|
||||
test::minimal::default_assignable,
|
||||
Map<test::minimal::assignable, test::minimal::default_assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<map_value_type> >
|
||||
map2;
|
||||
#else
|
||||
boost::unordered_map<test::minimal::assignable,
|
||||
test::minimal::default_assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<map_value_type> >
|
||||
map2;
|
||||
#endif
|
||||
|
||||
test::minimal::default_assignable default_assignable;
|
||||
|
||||
unordered_map_functions(map2, assignable, default_assignable);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
class P = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class Map>
|
||||
static void test2_equivalent_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
boost::unordered_multimap<test::minimal::assignable,
|
||||
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::assignable assignable(x);
|
||||
test::minimal::copy_constructible copy_constructible(x);
|
||||
test::minimal::hash<test::minimal::assignable> hash(x);
|
||||
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
|
||||
|
||||
typedef std::pair<test::minimal::assignable const, test::minimal::assignable>
|
||||
map_value_type;
|
||||
map_value_type map_value(assignable, assignable);
|
||||
|
||||
Map<test::minimal::assignable, test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<map_value_type> >
|
||||
multimap;
|
||||
map;
|
||||
|
||||
unordered_equivalent_test(multimap, map_value);
|
||||
unordered_map_test(multimap, assignable, assignable);
|
||||
unordered_copyable_test(multimap, assignable, map_value, hash, equal_to);
|
||||
unordered_map_member_test(multimap, map_value);
|
||||
unordered_equivalent_test(map, map_value);
|
||||
unordered_map_test(map, assignable, assignable);
|
||||
unordered_copyable_test(map, assignable, map_value, hash, equal_to);
|
||||
unordered_map_member_test(map, map_value);
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (test2) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test2_unique_impl<boost::unordered_flat_map>();
|
||||
test2_unique_impl<boost::unordered_node_map>();
|
||||
#else
|
||||
test2_unique_impl<boost::unordered_map>();
|
||||
test2_equivalent_impl<boost::unordered_multimap>();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -316,9 +316,17 @@ bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; }
|
||||
|
||||
UNORDERED_AUTO_TEST (lwg2059) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_map<lwg2059_key, int> x;
|
||||
x.emplace(lwg2059_key(10), 5);
|
||||
x.erase(x.begin());
|
||||
{
|
||||
boost::unordered_flat_map<lwg2059_key, int> x;
|
||||
x.emplace(lwg2059_key(10), 5);
|
||||
x.erase(x.begin());
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_node_map<lwg2059_key, int> x;
|
||||
x.emplace(lwg2059_key(10), 5);
|
||||
x.erase(x.begin());
|
||||
}
|
||||
#else
|
||||
{
|
||||
boost::unordered_map<lwg2059_key, int> x;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -35,6 +35,21 @@ template class instantiate_flat_set<test::minimal::assignable const,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_node_set
|
||||
{
|
||||
typedef boost::unordered_node_set<T, H, P, A> container;
|
||||
container x;
|
||||
};
|
||||
|
||||
template class instantiate_node_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
template class instantiate_node_set<test::minimal::assignable const,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
#else
|
||||
|
||||
#define INSTANTIATE(type) \
|
||||
@@ -56,153 +71,106 @@ INSTANTIATE(multiset)<test::minimal::assignable,
|
||||
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (type_traits) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef boost::unordered_flat_set<int> set_type;
|
||||
#else
|
||||
typedef boost::unordered_set<int> set_type;
|
||||
#endif
|
||||
|
||||
typedef set_type::iterator iterator;
|
||||
|
||||
template <class X> static void type_traits_impl()
|
||||
{
|
||||
BOOST_STATIC_ASSERT(boost::is_same<int const&,
|
||||
std::iterator_traits<iterator>::reference>::value);
|
||||
typename std::iterator_traits<typename X::iterator>::reference>::value);
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (test0) {
|
||||
UNORDERED_AUTO_TEST (type_traits) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
type_traits_impl<boost::unordered_flat_set<int> >();
|
||||
type_traits_impl<boost::unordered_node_set<int> >();
|
||||
#else
|
||||
type_traits_impl<boost::unordered_set<int> >();
|
||||
type_traits_impl<boost::unordered_multiset<int> >();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void test0_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::assignable assignable(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
|
||||
Set<int> int_set;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> int_set;
|
||||
|
||||
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_set2;
|
||||
|
||||
boost::unordered_flat_set<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<int> int_set;
|
||||
|
||||
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_set2;
|
||||
|
||||
boost::unordered_set<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
container_test(int_set, 0);
|
||||
container_test(int_set2, 0);
|
||||
container_test(set, assignable);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
|
||||
|
||||
boost::unordered_multiset<int> int_multiset;
|
||||
|
||||
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_multiset2;
|
||||
|
||||
boost::unordered_multiset<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
multiset;
|
||||
|
||||
container_test(int_multiset, 0);
|
||||
container_test(int_multiset2, 0);
|
||||
container_test(multiset, assignable);
|
||||
UNORDERED_AUTO_TEST (test0) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test0_impl<boost::unordered_flat_set>();
|
||||
test0_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
test0_impl<boost::unordered_set>();
|
||||
test0_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (equality_tests) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void equality_tests_impl()
|
||||
{
|
||||
typedef test::minimal::copy_constructible_equality_comparable value_type;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> int_set;
|
||||
Set<int> int_set;
|
||||
|
||||
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_set2;
|
||||
|
||||
boost::unordered_flat_set<
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
Set<test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<int> int_set;
|
||||
|
||||
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_set2;
|
||||
|
||||
boost::unordered_set<test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
equality_test(int_set);
|
||||
equality_test(int_set2);
|
||||
equality_test(set);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_multiset<int> int_multiset;
|
||||
|
||||
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
int_multiset2;
|
||||
|
||||
boost::unordered_multiset<
|
||||
test::minimal::copy_constructible_equality_comparable,
|
||||
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::equal_to<
|
||||
test::minimal::copy_constructible_equality_comparable>,
|
||||
test::minimal::allocator<value_type> >
|
||||
multiset;
|
||||
|
||||
equality_test(int_multiset);
|
||||
equality_test(int_multiset2);
|
||||
equality_test(multiset);
|
||||
UNORDERED_AUTO_TEST (equality_tests) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
equality_tests_impl<boost::unordered_flat_set>();
|
||||
equality_tests_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
equality_tests_impl<boost::unordered_set>();
|
||||
equality_tests_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (test1) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void test1_unique_impl()
|
||||
{
|
||||
|
||||
boost::hash<int> hash;
|
||||
std::equal_to<int> equal_to;
|
||||
int value = 0;
|
||||
Set<int> set;
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set." << std::endl;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> set;
|
||||
|
||||
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
set2;
|
||||
#else
|
||||
boost::unordered_set<int> set;
|
||||
|
||||
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
set2;
|
||||
#endif
|
||||
|
||||
unordered_unique_test(set, value);
|
||||
unordered_set_test(set, value);
|
||||
@@ -211,27 +179,49 @@ UNORDERED_AUTO_TEST (test1) {
|
||||
unordered_unique_test(set2, value);
|
||||
unordered_set_test(set2, value);
|
||||
unordered_copyable_test(set2, value, value, hash, equal_to);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset." << std::endl;
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void test1_equivalent_impl()
|
||||
{
|
||||
|
||||
boost::unordered_multiset<int> multiset;
|
||||
boost::hash<int> hash;
|
||||
std::equal_to<int> equal_to;
|
||||
int value = 0;
|
||||
Set<int> set;
|
||||
|
||||
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
|
||||
Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::cxx11_allocator<int> >
|
||||
multiset2;
|
||||
set2;
|
||||
|
||||
unordered_equivalent_test(multiset, value);
|
||||
unordered_set_test(multiset, value);
|
||||
unordered_copyable_test(multiset, value, value, hash, equal_to);
|
||||
unordered_equivalent_test(set, value);
|
||||
unordered_set_test(set, value);
|
||||
unordered_copyable_test(set, value, value, hash, equal_to);
|
||||
|
||||
unordered_equivalent_test(multiset2, value);
|
||||
unordered_set_test(multiset2, value);
|
||||
unordered_copyable_test(multiset2, value, value, hash, equal_to);
|
||||
unordered_equivalent_test(set2, value);
|
||||
unordered_set_test(set2, value);
|
||||
unordered_copyable_test(set2, value, value, hash, equal_to);
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (test1) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test1_unique_impl<boost::unordered_flat_set>();
|
||||
test1_unique_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
test1_unique_impl<boost::unordered_set>();
|
||||
test1_equivalent_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (test2) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void test2_unique_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::assignable assignable(x);
|
||||
@@ -239,155 +229,144 @@ UNORDERED_AUTO_TEST (test2) {
|
||||
test::minimal::hash<test::minimal::assignable> hash(x);
|
||||
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
unordered_unique_test(set, assignable);
|
||||
unordered_set_test(set, assignable);
|
||||
unordered_copyable_test(set, assignable, assignable, hash, equal_to);
|
||||
unordered_set_member_test(set, assignable);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void test2_equivalent_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
boost::unordered_multiset<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::assignable assignable(x);
|
||||
test::minimal::copy_constructible copy_constructible(x);
|
||||
test::minimal::hash<test::minimal::assignable> hash(x);
|
||||
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
|
||||
|
||||
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<test::minimal::assignable> >
|
||||
multiset;
|
||||
set;
|
||||
|
||||
unordered_equivalent_test(multiset, assignable);
|
||||
unordered_set_test(multiset, assignable);
|
||||
unordered_copyable_test(multiset, assignable, assignable, hash, equal_to);
|
||||
unordered_set_member_test(multiset, assignable);
|
||||
unordered_equivalent_test(set, assignable);
|
||||
unordered_set_test(set, assignable);
|
||||
unordered_copyable_test(set, assignable, assignable, hash, equal_to);
|
||||
unordered_set_member_test(set, assignable);
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (test2) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test2_unique_impl<boost::unordered_flat_set>();
|
||||
test2_unique_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
test2_unique_impl<boost::unordered_set>();
|
||||
test2_equivalent_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (movable1_tests) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void movable1_tests_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::movable1 movable1(x);
|
||||
test::minimal::hash<test::minimal::movable1> hash(x);
|
||||
test::minimal::equal_to<test::minimal::movable1> equal_to(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<test::minimal::movable1,
|
||||
test::minimal::hash<test::minimal::movable1>,
|
||||
Set<test::minimal::movable1, test::minimal::hash<test::minimal::movable1>,
|
||||
test::minimal::equal_to<test::minimal::movable1>,
|
||||
test::minimal::allocator<test::minimal::movable1> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<test::minimal::movable1,
|
||||
test::minimal::hash<test::minimal::movable1>,
|
||||
test::minimal::equal_to<test::minimal::movable1>,
|
||||
test::minimal::allocator<test::minimal::movable1> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
// TODO: find out why Daniel had this commented out and if we need it and the
|
||||
// corresponding equivalent impl
|
||||
//
|
||||
// unordered_unique_test(set, movable1);
|
||||
unordered_set_test(set, movable1);
|
||||
unordered_movable_test(set, movable1, movable1, hash, equal_to);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
|
||||
|
||||
boost::unordered_multiset<test::minimal::movable1,
|
||||
test::minimal::hash<test::minimal::movable1>,
|
||||
test::minimal::equal_to<test::minimal::movable1>,
|
||||
test::minimal::allocator<test::minimal::movable1> >
|
||||
multiset;
|
||||
|
||||
// unordered_equivalent_test(multiset, movable1);
|
||||
unordered_set_test(multiset, movable1);
|
||||
unordered_movable_test(multiset, movable1, movable1, hash, equal_to);
|
||||
UNORDERED_AUTO_TEST (movable1_tests) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
movable1_tests_impl<boost::unordered_flat_set>();
|
||||
movable1_tests_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
movable1_tests_impl<boost::unordered_set>();
|
||||
movable1_tests_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (movable2_tests) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void movable2_tests_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::movable2 movable2(x);
|
||||
test::minimal::hash<test::minimal::movable2> hash(x);
|
||||
test::minimal::equal_to<test::minimal::movable2> equal_to(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<test::minimal::movable2,
|
||||
test::minimal::hash<test::minimal::movable2>,
|
||||
Set<test::minimal::movable2, test::minimal::hash<test::minimal::movable2>,
|
||||
test::minimal::equal_to<test::minimal::movable2>,
|
||||
test::minimal::allocator<test::minimal::movable2> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<test::minimal::movable2,
|
||||
test::minimal::hash<test::minimal::movable2>,
|
||||
test::minimal::equal_to<test::minimal::movable2>,
|
||||
test::minimal::allocator<test::minimal::movable2> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
// unordered_unique_test(set, movable2);
|
||||
unordered_set_test(set, movable2);
|
||||
unordered_movable_test(set, movable2, movable2, hash, equal_to);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
|
||||
UNORDERED_AUTO_TEST (movable2_tests) {
|
||||
|
||||
boost::unordered_multiset<test::minimal::movable2,
|
||||
test::minimal::hash<test::minimal::movable2>,
|
||||
test::minimal::equal_to<test::minimal::movable2>,
|
||||
test::minimal::allocator<test::minimal::movable2> >
|
||||
multiset;
|
||||
|
||||
// unordered_equivalent_test(multiset, movable2);
|
||||
unordered_set_test(multiset, movable2);
|
||||
unordered_movable_test(multiset, movable2, movable2, hash, equal_to);
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
movable2_tests_impl<boost::unordered_flat_set>();
|
||||
movable2_tests_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
movable2_tests_impl<boost::unordered_set>();
|
||||
movable2_tests_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (destructible_tests) {
|
||||
template <template <class T, class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>, class A = std::allocator<T> >
|
||||
class Set>
|
||||
static void destructible_tests_impl()
|
||||
{
|
||||
test::minimal::constructor_param x;
|
||||
|
||||
test::minimal::destructible destructible(x);
|
||||
test::minimal::hash<test::minimal::destructible> hash(x);
|
||||
test::minimal::equal_to<test::minimal::destructible> equal_to(x);
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<test::minimal::destructible,
|
||||
Set<test::minimal::destructible,
|
||||
test::minimal::hash<test::minimal::destructible>,
|
||||
test::minimal::equal_to<test::minimal::destructible> >
|
||||
set;
|
||||
#else
|
||||
boost::unordered_set<test::minimal::destructible,
|
||||
test::minimal::hash<test::minimal::destructible>,
|
||||
test::minimal::equal_to<test::minimal::destructible> >
|
||||
set;
|
||||
#endif
|
||||
|
||||
unordered_destructible_test(set);
|
||||
}
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
|
||||
|
||||
boost::unordered_multiset<test::minimal::destructible,
|
||||
test::minimal::hash<test::minimal::destructible>,
|
||||
test::minimal::equal_to<test::minimal::destructible> >
|
||||
multiset;
|
||||
|
||||
unordered_destructible_test(multiset);
|
||||
UNORDERED_AUTO_TEST (destructible_tests) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
destructible_tests_impl<boost::unordered_flat_set>();
|
||||
destructible_tests_impl<boost::unordered_node_set>();
|
||||
#else
|
||||
destructible_tests_impl<boost::unordered_set>();
|
||||
destructible_tests_impl<boost::unordered_multiset>();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -410,9 +389,17 @@ bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; }
|
||||
|
||||
UNORDERED_AUTO_TEST (lwg2059) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<lwg2059_key> x;
|
||||
x.emplace(lwg2059_key(10));
|
||||
x.erase(x.begin());
|
||||
{
|
||||
boost::unordered_flat_set<lwg2059_key> x;
|
||||
x.emplace(lwg2059_key(10));
|
||||
x.erase(x.begin());
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_node_set<lwg2059_key> x;
|
||||
x.emplace(lwg2059_key(10));
|
||||
x.erase(x.begin());
|
||||
}
|
||||
#else
|
||||
{
|
||||
boost::unordered_set<lwg2059_key> x;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2010 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// 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)
|
||||
|
||||
@@ -655,23 +655,27 @@ namespace constructor_tests {
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_set;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_node_set;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator2<test::object> >* test_map;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator2<test::object> >* test_node_map;
|
||||
|
||||
UNORDERED_TEST(constructor_tests1,
|
||||
((test_map_std_alloc)(test_set)(test_map))(
|
||||
((test_map_std_alloc)(test_set)(test_node_set)(test_map)(test_node_map))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(constructor_tests2,
|
||||
((test_set)(test_map))(
|
||||
((test_set)(test_node_set)(test_map)(test_node_map))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(map_constructor_test,
|
||||
((test_map_std_alloc)(test_map))(
|
||||
((test_map_std_alloc)(test_map)(test_node_map))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(no_alloc_default_construct_test,
|
||||
((test_set)(test_map))(
|
||||
((test_set)(test_node_set)(test_map)(test_node_map))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
#else
|
||||
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
|
||||
@@ -709,6 +713,8 @@ namespace constructor_tests {
|
||||
std::initializer_list<int> init;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> x1 = init;
|
||||
boost::unordered_node_set<int> x2 = init;
|
||||
BOOST_TEST(x2.empty());
|
||||
#else
|
||||
boost::unordered_set<int> x1 = init;
|
||||
#endif
|
||||
@@ -722,6 +728,9 @@ namespace constructor_tests {
|
||||
UNORDERED_AUTO_TEST (test_initializer_list) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> x1 = {2, 10, 45, -5};
|
||||
boost::unordered_node_set<int> x2 = {2, 10, 45, -5};
|
||||
BOOST_TEST(x2.find(10) != x2.end());
|
||||
BOOST_TEST(x2.find(46) == x2.end());
|
||||
#else
|
||||
boost::unordered_set<int> x1 = {2, 10, 45, -5};
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 Christian Mazakas.
|
||||
// Copyright 2021-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -137,6 +137,24 @@ void test_map()
|
||||
|
||||
typedef boost::unordered_flat_map<key, int, hasher, key_equal>
|
||||
non_transparent_map3;
|
||||
|
||||
typedef boost::unordered_node_map<key, int, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
transparent_node_map;
|
||||
|
||||
typedef boost::unordered_node_map<key, int, transparent_hasher, key_equal>
|
||||
non_transparent_node_map1;
|
||||
|
||||
typedef boost::unordered_node_map<key, int, hasher, transparent_key_equal>
|
||||
non_transparent_node_map2;
|
||||
|
||||
typedef boost::unordered_node_map<key, int, hasher, key_equal>
|
||||
non_transparent_node_map3;
|
||||
|
||||
test_map_transparent_contains<transparent_node_map>();
|
||||
test_map_non_transparent_contains<non_transparent_node_map1>();
|
||||
test_map_non_transparent_contains<non_transparent_node_map2>();
|
||||
test_map_non_transparent_contains<non_transparent_node_map3>();
|
||||
#else
|
||||
typedef boost::unordered_map<key, int, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
@@ -254,6 +272,22 @@ void test_set()
|
||||
non_transparent_set2;
|
||||
typedef boost::unordered_flat_set<key, hasher, key_equal>
|
||||
non_transparent_set3;
|
||||
|
||||
typedef boost::unordered_node_set<key, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
transparent_node_set;
|
||||
|
||||
typedef boost::unordered_node_set<key, transparent_hasher, key_equal>
|
||||
non_transparent_node_set1;
|
||||
typedef boost::unordered_node_set<key, hasher, transparent_key_equal>
|
||||
non_transparent_node_set2;
|
||||
typedef boost::unordered_node_set<key, hasher, key_equal>
|
||||
non_transparent_node_set3;
|
||||
|
||||
test_set_transparent_contains<transparent_node_set>();
|
||||
test_set_non_transparent_contains<non_transparent_node_set1>();
|
||||
test_set_non_transparent_contains<non_transparent_node_set2>();
|
||||
test_set_non_transparent_contains<non_transparent_node_set3>();
|
||||
#else
|
||||
typedef boost::unordered_set<key, transparent_hasher, transparent_key_equal>
|
||||
transparent_set;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
|
||||
test::seed_t initialize_seed(9063);
|
||||
|
||||
@@ -482,31 +482,82 @@ namespace copy_tests {
|
||||
allocator<std::pair<int const, int> > >*
|
||||
test_map_trivially_copyable_no_construct;
|
||||
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_node_set;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator1<test::object> >* test_node_map;
|
||||
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::select_copy> >*
|
||||
test_node_set_select_copy;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::cxx11_allocator<test::object, test::select_copy> >*
|
||||
test_node_map_select_copy;
|
||||
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::cxx11_allocator<test::object, test::no_select_copy> >*
|
||||
test_node_set_no_select_copy;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
|
||||
test_node_map_no_select_copy;
|
||||
|
||||
boost::unordered_node_set<int, test::hash, test::equal_to,
|
||||
test::allocator1<int> >* test_node_set_trivially_copyable;
|
||||
boost::unordered_node_map<int, int, test::hash, test::equal_to,
|
||||
test::allocator1<std::pair<int const, int> > >*
|
||||
test_node_map_trivially_copyable;
|
||||
|
||||
boost::unordered_node_set<int, test::hash, test::equal_to,
|
||||
std::allocator<int> >* test_node_set_trivially_copyable_std_allocator;
|
||||
boost::unordered_node_map<int, int, test::hash, test::equal_to,
|
||||
std::allocator<std::pair<int const, int> > >*
|
||||
test_node_map_trivially_copyable_std_allocator;
|
||||
|
||||
boost::unordered_node_set<int, test::hash, test::equal_to, allocator<int> >*
|
||||
test_node_set_trivially_copyable_no_construct;
|
||||
boost::unordered_node_map<int, int, test::hash, test::equal_to,
|
||||
allocator<std::pair<int const, int> > >*
|
||||
test_node_map_trivially_copyable_no_construct;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(copy_construct_tests1,
|
||||
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
|
||||
(test_set_no_select_copy)(test_map_no_select_copy)
|
||||
(test_set_trivially_copyable)(test_map_trivially_copyable))
|
||||
(test_set_trivially_copyable)(test_map_trivially_copyable)
|
||||
(test_node_set)(test_node_map)(test_node_set_select_copy)(test_node_map_select_copy)
|
||||
(test_node_set_no_select_copy)(test_node_map_no_select_copy)
|
||||
(test_node_set_trivially_copyable)(test_node_map_trivially_copyable))
|
||||
((default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(copy_construct_tests2,
|
||||
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
|
||||
(test_set_no_select_copy)(test_map_no_select_copy)
|
||||
(test_set_trivially_copyable)(test_map_trivially_copyable))
|
||||
(test_set_trivially_copyable)(test_map_trivially_copyable)
|
||||
(test_node_set)(test_node_map)(test_node_set_select_copy)(test_node_map_select_copy)
|
||||
(test_node_set_no_select_copy)(test_node_map_no_select_copy)
|
||||
(test_node_set_trivially_copyable)(test_node_map_trivially_copyable))
|
||||
((default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(copy_construct_tests_std_allocator1,
|
||||
((test_set_trivially_copyable_std_allocator)
|
||||
(test_map_trivially_copyable_std_allocator)
|
||||
(test_set_trivially_copyable_no_construct)
|
||||
(test_map_trivially_copyable_no_construct))
|
||||
(test_map_trivially_copyable_no_construct)
|
||||
(test_node_set_trivially_copyable_std_allocator)
|
||||
(test_node_map_trivially_copyable_std_allocator)
|
||||
(test_node_set_trivially_copyable_no_construct)
|
||||
(test_node_map_trivially_copyable_no_construct))
|
||||
((default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(copy_construct_tests_std_allocator2,
|
||||
((test_set_trivially_copyable_std_allocator)
|
||||
(test_map_trivially_copyable_std_allocator)
|
||||
(test_set_trivially_copyable_no_construct)
|
||||
(test_map_trivially_copyable_no_construct))
|
||||
(test_map_trivially_copyable_no_construct)
|
||||
(test_node_set_trivially_copyable_std_allocator)
|
||||
(test_node_map_trivially_copyable_std_allocator)
|
||||
(test_node_set_trivially_copyable_no_construct)
|
||||
(test_node_map_trivially_copyable_no_construct))
|
||||
((default_generator)(generate_collisions)(limited_range)))
|
||||
// clang-format on
|
||||
#else
|
||||
@@ -545,13 +596,23 @@ namespace copy_tests {
|
||||
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
|
||||
test_multimap_no_select_copy;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(copy_construct_tests1,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))(
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_select_copy)(test_multiset_select_copy)
|
||||
(test_map_select_copy)(test_multimap_select_copy)
|
||||
(test_set_no_select_copy)(test_multiset_no_select_copy)
|
||||
(test_map_no_select_copy)(test_multimap_no_select_copy))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(copy_construct_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))(
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)
|
||||
(test_set_select_copy)(test_multiset_select_copy)
|
||||
(test_map_select_copy)(test_multimap_select_copy)
|
||||
(test_set_no_select_copy)(test_multiset_no_select_copy)
|
||||
(test_map_no_select_copy)(test_multimap_no_select_copy))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
// clang-format on
|
||||
#endif
|
||||
} // namespace copy_tests
|
||||
|
||||
|
||||
@@ -179,18 +179,13 @@ namespace emplace_tests {
|
||||
|
||||
};
|
||||
|
||||
UNORDERED_AUTO_TEST (emplace_set) {
|
||||
template <class X> static void emplace_set(X*)
|
||||
{
|
||||
test::check_instances check_;
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef boost::unordered_flat_set<emplace_value,
|
||||
boost::hash<emplace_value> >
|
||||
container;
|
||||
#else
|
||||
typedef boost::unordered_set<emplace_value, boost::hash<emplace_value> >
|
||||
container;
|
||||
#endif
|
||||
typedef container::iterator iterator;
|
||||
typedef X container;
|
||||
|
||||
typedef typename container::iterator iterator;
|
||||
typedef std::pair<iterator, bool> return_type;
|
||||
container x(10);
|
||||
iterator i1;
|
||||
@@ -225,7 +220,7 @@ namespace emplace_tests {
|
||||
BOOST_TEST(r1.first == x.find(v2));
|
||||
BOOST_TEST_EQ(check_.instances(), 4);
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_TEST_EQ(check_.constructions(), 6);
|
||||
BOOST_TEST_EQ(check_.constructions(), 6);
|
||||
#else
|
||||
BOOST_TEST_EQ(check_.constructions(), 4);
|
||||
#endif
|
||||
@@ -294,6 +289,21 @@ namespace emplace_tests {
|
||||
BOOST_TEST(x.count(v4) == 1);
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_flat_set<emplace_value, boost::hash<emplace_value> >*
|
||||
test_set;
|
||||
|
||||
static boost::unordered_node_set<emplace_value, boost::hash<emplace_value> >*
|
||||
test_node_set;
|
||||
|
||||
UNORDERED_TEST(emplace_set, ((test_set)(test_node_set)))
|
||||
#else
|
||||
static boost::unordered_set<emplace_value, boost::hash<emplace_value> >*
|
||||
test_set;
|
||||
|
||||
UNORDERED_TEST(emplace_set, ((test_set)))
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
UNORDERED_AUTO_TEST (emplace_multiset) {
|
||||
test::check_instances check_;
|
||||
@@ -374,18 +384,12 @@ namespace emplace_tests {
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (emplace_map) {
|
||||
template <class X> static void emplace_map(X*)
|
||||
{
|
||||
test::check_instances check_;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef boost::unordered_flat_map<emplace_value, emplace_value,
|
||||
boost::hash<emplace_value> >
|
||||
container;
|
||||
#else
|
||||
typedef boost::unordered_map<emplace_value, emplace_value,
|
||||
boost::hash<emplace_value> >
|
||||
container;
|
||||
#endif
|
||||
typedef container::iterator iterator;
|
||||
|
||||
typedef X container;
|
||||
typedef typename container::iterator iterator;
|
||||
typedef std::pair<iterator, bool> return_type;
|
||||
container x(10);
|
||||
return_type r1, r2;
|
||||
@@ -393,7 +397,9 @@ namespace emplace_tests {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
// 5/8 args + duplicate
|
||||
emplace_value k1(5, "", 'b', 4, 5);
|
||||
BOOST_TEST_EQ(check_.constructions(), 1);
|
||||
emplace_value m1(8, "xxx", 'z', 4, 5, 6, 7, 8);
|
||||
BOOST_TEST_EQ(check_.constructions(), 2);
|
||||
r1 = x.emplace(std::piecewise_construct, std::make_tuple(5, "", 'b', 4, 5),
|
||||
std::make_tuple(8, "xxx", 'z', 4, 5, 6, 7, 8));
|
||||
BOOST_TEST_EQ(x.size(), 1u);
|
||||
@@ -485,18 +491,18 @@ namespace emplace_tests {
|
||||
BOOST_TEST_EQ(check_.instances(), 8);
|
||||
BOOST_TEST_EQ(check_.constructions(), 10);
|
||||
|
||||
BOOST_TEST(r1.first ==
|
||||
x.emplace_hint(r1.first, boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(15, "jkjk")));
|
||||
BOOST_TEST(r1.first ==
|
||||
x.emplace_hint(r2.first, boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(275, "xxx", 'm', 6)));
|
||||
BOOST_TEST(r1.first ==
|
||||
x.emplace_hint(x.end(), boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(-10, "blah blah", '\0')));
|
||||
BOOST_TEST(r1.first == x.emplace_hint(r1.first,
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(15, "jkjk")));
|
||||
BOOST_TEST(r1.first == x.emplace_hint(r2.first,
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(275, "xxx", 'm', 6)));
|
||||
BOOST_TEST(
|
||||
r1.first == x.emplace_hint(x.end(), boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
|
||||
boost::make_tuple(-10, "blah blah", '\0')));
|
||||
BOOST_TEST_EQ(x.size(), 2u);
|
||||
BOOST_TEST(x.find(k2)->second == m2);
|
||||
BOOST_TEST_EQ(check_.instances(), 8);
|
||||
@@ -504,6 +510,21 @@ namespace emplace_tests {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_flat_map<emplace_value, emplace_value,
|
||||
boost::hash<emplace_value> >* test_map;
|
||||
|
||||
static boost::unordered_node_map<emplace_value, emplace_value,
|
||||
boost::hash<emplace_value> >* test_node_map;
|
||||
|
||||
UNORDERED_TEST(emplace_map, ((test_map)(test_node_map)))
|
||||
#else
|
||||
static boost::unordered_map<emplace_value, emplace_value,
|
||||
boost::hash<emplace_value> >* test_map;
|
||||
|
||||
UNORDERED_TEST(emplace_map, ((test_map)))
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
UNORDERED_AUTO_TEST (emplace_multimap) {
|
||||
test::check_instances check_;
|
||||
@@ -571,14 +592,13 @@ namespace emplace_tests {
|
||||
}
|
||||
#endif
|
||||
|
||||
UNORDERED_AUTO_TEST (try_emplace) {
|
||||
template <class X> static void try_emplace(X*)
|
||||
{
|
||||
test::check_instances check_;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef boost::unordered_flat_map<int, emplace_value> container;
|
||||
#else
|
||||
typedef boost::unordered_map<int, emplace_value> container;
|
||||
#endif
|
||||
typedef container::iterator iterator;
|
||||
|
||||
typedef X container;
|
||||
|
||||
typedef typename container::iterator iterator;
|
||||
typedef std::pair<iterator, bool> return_type;
|
||||
container x(10);
|
||||
return_type r1, r2, r3;
|
||||
@@ -615,12 +635,22 @@ namespace emplace_tests {
|
||||
BOOST_TEST_EQ(check_.constructions(), 4);
|
||||
|
||||
BOOST_TEST(r2.first == x.try_emplace(r2.first, k2, 808709, "what"));
|
||||
BOOST_TEST(
|
||||
r2.first ==
|
||||
x.try_emplace(r2.first, k2, 10, "xxx", 'a', 4, 5, 6, 7, 8, 9, 10));
|
||||
BOOST_TEST(r2.first == x.try_emplace(r2.first, k2, 10, "xxx", 'a', 4, 5, 6,
|
||||
7, 8, 9, 10));
|
||||
BOOST_TEST(r2.first->second == m2);
|
||||
BOOST_TEST_EQ(x.size(), 2u);
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_flat_map<int, emplace_value>* test_int_map;
|
||||
static boost::unordered_node_map<int, emplace_value>* test_int_node_map;
|
||||
|
||||
UNORDERED_TEST(try_emplace, ((test_int_map)(test_int_node_map)))
|
||||
#else
|
||||
static boost::unordered_map<int, emplace_value>* test_int_map;
|
||||
|
||||
UNORDERED_TEST(try_emplace, ((test_int_map)))
|
||||
#endif
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
|
||||
// Copyright 2008-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/preprocessor/seq.hpp>
|
||||
#include <list>
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
// TODO: this test needs to be someday cleaned up to not be so heavily
|
||||
// macro-generated
|
||||
//
|
||||
|
||||
namespace equality_tests {
|
||||
struct mod_compare
|
||||
@@ -33,6 +37,12 @@ namespace equality_tests {
|
||||
using boost_unordered_map =
|
||||
boost::unordered_flat_map<int, int, mod_compare, mod_compare>;
|
||||
|
||||
using boost_unordered_node_set =
|
||||
boost::unordered_node_set<int, mod_compare, mod_compare>;
|
||||
|
||||
using boost_unordered_node_map =
|
||||
boost::unordered_node_map<int, int, mod_compare, mod_compare>;
|
||||
|
||||
#define UNORDERED_EQUALITY_MULTISET_TEST(seq1, op, seq2) \
|
||||
{ \
|
||||
}
|
||||
@@ -64,14 +74,46 @@ namespace equality_tests {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \
|
||||
{ \
|
||||
boost_unordered_set set1, set2; \
|
||||
boost_unordered_set set1, set2; \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
|
||||
BOOST_TEST(set1 op set2); \
|
||||
} \
|
||||
{ \
|
||||
boost_unordered_node_set set1, set2; \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
|
||||
BOOST_TEST(set1 op set2); \
|
||||
}
|
||||
#else
|
||||
#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \
|
||||
{ \
|
||||
boost_unordered_set set1, set2; \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
|
||||
BOOST_TEST(set1 op set2); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \
|
||||
{ \
|
||||
boost_unordered_map map1, map2; \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
|
||||
BOOST_TEST(map1 op map2); \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
boost_unordered_node_map map1, map2; \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
|
||||
BOOST_TEST(map1 op map2); \
|
||||
}
|
||||
#else
|
||||
#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \
|
||||
{ \
|
||||
boost_unordered_map map1, map2; \
|
||||
@@ -79,6 +121,7 @@ namespace equality_tests {
|
||||
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
|
||||
BOOST_TEST(map1 op map2); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define UNORDERED_SET_INSERT(r, set, item) set.insert(item);
|
||||
#define UNORDERED_MAP_INSERT(r, map, item) \
|
||||
@@ -86,10 +129,51 @@ namespace equality_tests {
|
||||
|
||||
UNORDERED_AUTO_TEST (equality_size_tests) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
boost::unordered_flat_set<int> x1, x2;
|
||||
{
|
||||
boost::unordered_flat_set<int> x1, x2;
|
||||
BOOST_TEST(x1 == x2);
|
||||
BOOST_TEST(!(x1 != x2));
|
||||
|
||||
x1.insert(1);
|
||||
BOOST_TEST(x1 != x2);
|
||||
BOOST_TEST(!(x1 == x2));
|
||||
BOOST_TEST(x2 != x1);
|
||||
BOOST_TEST(!(x2 == x1));
|
||||
|
||||
x2.insert(1);
|
||||
BOOST_TEST(x1 == x2);
|
||||
BOOST_TEST(!(x1 != x2));
|
||||
|
||||
x2.insert(2);
|
||||
BOOST_TEST(x1 != x2);
|
||||
BOOST_TEST(!(x1 == x2));
|
||||
BOOST_TEST(x2 != x1);
|
||||
BOOST_TEST(!(x2 == x1));
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_node_set<int> x1, x2;
|
||||
BOOST_TEST(x1 == x2);
|
||||
BOOST_TEST(!(x1 != x2));
|
||||
|
||||
x1.insert(1);
|
||||
BOOST_TEST(x1 != x2);
|
||||
BOOST_TEST(!(x1 == x2));
|
||||
BOOST_TEST(x2 != x1);
|
||||
BOOST_TEST(!(x2 == x1));
|
||||
|
||||
x2.insert(1);
|
||||
BOOST_TEST(x1 == x2);
|
||||
BOOST_TEST(!(x1 != x2));
|
||||
|
||||
x2.insert(2);
|
||||
BOOST_TEST(x1 != x2);
|
||||
BOOST_TEST(!(x1 == x2));
|
||||
BOOST_TEST(x2 != x1);
|
||||
BOOST_TEST(!(x2 == x1));
|
||||
}
|
||||
#else
|
||||
boost::unordered_set<int> x1, x2;
|
||||
#endif
|
||||
BOOST_TEST(x1 == x2);
|
||||
BOOST_TEST(!(x1 != x2));
|
||||
|
||||
@@ -108,6 +192,7 @@ namespace equality_tests {
|
||||
BOOST_TEST(!(x1 == x2));
|
||||
BOOST_TEST(x2 != x1);
|
||||
BOOST_TEST(!(x2 == x1));
|
||||
#endif
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (equality_key_value_tests) {
|
||||
@@ -156,6 +241,45 @@ namespace equality_tests {
|
||||
// different hash functions but the same equality predicate.
|
||||
|
||||
UNORDERED_AUTO_TEST (equality_different_hash_test) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
{
|
||||
typedef boost_unordered_set set;
|
||||
set set1(0, mod_compare(false), mod_compare(false));
|
||||
set set2(0, mod_compare(true), mod_compare(true));
|
||||
BOOST_TEST(set1 == set2);
|
||||
set1.insert(1);
|
||||
set2.insert(2);
|
||||
BOOST_TEST(set1 != set2);
|
||||
set1.insert(2);
|
||||
set2.insert(1);
|
||||
BOOST_TEST(set1 == set2);
|
||||
set1.insert(10);
|
||||
set2.insert(20);
|
||||
BOOST_TEST(set1 != set2);
|
||||
set1.insert(20);
|
||||
set2.insert(10);
|
||||
BOOST_TEST(set1 == set2);
|
||||
}
|
||||
|
||||
{
|
||||
typedef boost_unordered_node_set set;
|
||||
set set1(0, mod_compare(false), mod_compare(false));
|
||||
set set2(0, mod_compare(true), mod_compare(true));
|
||||
BOOST_TEST(set1 == set2);
|
||||
set1.insert(1);
|
||||
set2.insert(2);
|
||||
BOOST_TEST(set1 != set2);
|
||||
set1.insert(2);
|
||||
set2.insert(1);
|
||||
BOOST_TEST(set1 == set2);
|
||||
set1.insert(10);
|
||||
set2.insert(20);
|
||||
BOOST_TEST(set1 != set2);
|
||||
set1.insert(20);
|
||||
set2.insert(10);
|
||||
BOOST_TEST(set1 == set2);
|
||||
}
|
||||
#else
|
||||
typedef boost_unordered_set set;
|
||||
set set1(0, mod_compare(false), mod_compare(false));
|
||||
set set2(0, mod_compare(true), mod_compare(true));
|
||||
@@ -172,7 +296,8 @@ namespace equality_tests {
|
||||
set1.insert(20);
|
||||
set2.insert(10);
|
||||
BOOST_TEST(set1 == set2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace equality_tests
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
@@ -36,9 +36,21 @@ UNORDERED_AUTO_TEST (set_tests) {
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef boost::unordered_flat_set<int> set;
|
||||
|
||||
test_equal_insertion<set>(values[0], values[0] + 1);
|
||||
test_equal_insertion<set>(values[1], values[1] + 2);
|
||||
test_equal_insertion<set>(values[2], values[2] + 2);
|
||||
test_equal_insertion<set>(values[3], values[3] + 2);
|
||||
test_equal_insertion<set>(values[4], values[4] + 3);
|
||||
|
||||
typedef boost::unordered_node_set<int> node_set;
|
||||
test_equal_insertion<node_set>(values[0], values[0] + 1);
|
||||
test_equal_insertion<node_set>(values[1], values[1] + 2);
|
||||
test_equal_insertion<node_set>(values[2], values[2] + 2);
|
||||
test_equal_insertion<node_set>(values[3], values[3] + 2);
|
||||
test_equal_insertion<node_set>(values[4], values[4] + 3);
|
||||
#else
|
||||
typedef boost::unordered_set<int> set;
|
||||
#endif
|
||||
|
||||
test_equal_insertion<set>(values[0], values[0] + 1);
|
||||
test_equal_insertion<set>(values[1], values[1] + 2);
|
||||
@@ -52,6 +64,8 @@ UNORDERED_AUTO_TEST (set_tests) {
|
||||
test_equal_insertion<multiset>(values[2], values[2] + 2);
|
||||
test_equal_insertion<multiset>(values[3], values[3] + 2);
|
||||
test_equal_insertion<multiset>(values[4], values[4] + 3);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (map_tests) {
|
||||
@@ -65,11 +79,16 @@ UNORDERED_AUTO_TEST (map_tests) {
|
||||
v[2].push_back(std::pair<int const, int>(9, 13));
|
||||
v[2].push_back(std::pair<int const, int>(432, 24));
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
for (int i = 0; i < 5; ++i)
|
||||
test_equal_insertion<boost::unordered_flat_map<int, int> >(
|
||||
v[i].begin(), v[i].end());
|
||||
|
||||
for (int i2 = 0; i2 < 5; ++i2)
|
||||
test_equal_insertion<boost::unordered_node_map<int, int> >(
|
||||
v[i2].begin(), v[i2].end());
|
||||
#else
|
||||
for (int i = 0; i < 5; ++i)
|
||||
test_equal_insertion<boost::unordered_map<int, int> >(
|
||||
v[i].begin(), v[i].end());
|
||||
|
||||
|
||||
@@ -110,6 +110,8 @@ UNORDERED_AUTO_TEST (unordered_erase_if) {
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
test_map_erase_if<boost::unordered_flat_map<std::string, int> >();
|
||||
test_set_erase_if<boost::unordered_flat_set<int> >();
|
||||
test_map_erase_if<boost::unordered_node_map<std::string, int> >();
|
||||
test_set_erase_if<boost::unordered_node_set<int> >();
|
||||
#else
|
||||
test_map_erase_if<boost::unordered_map<std::string, int> >();
|
||||
test_map_erase_if<boost::unordered_multimap<std::string, int> >();
|
||||
|
||||
@@ -270,10 +270,16 @@ namespace erase_tests {
|
||||
test::allocator1<test::object> >* test_set;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator1<test::object> >* test_map;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_node_set;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator1<test::object> >* test_node_map;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase_tests1, ((test_set)(test_map))(
|
||||
erase_tests1, ((test_set)(test_map)(test_node_set)(test_node_map))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
// clang-format on
|
||||
#else
|
||||
boost::unordered_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_set;
|
||||
@@ -284,10 +290,11 @@ namespace erase_tests {
|
||||
boost::unordered_multimap<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator2<test::object> >* test_multimap;
|
||||
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase_tests1, ((test_set)(test_multiset)(test_map)(test_multimap))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
// clang-format on
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||