mirror of
https://github.com/boostorg/unordered.git
synced 2026-08-06 13:44:12 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 34868c50ca |
@@ -1,57 +0,0 @@
|
||||
# Copyright 2017 Daniel James
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
version: 1.0.{build}-{branch}
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- /feature\/.*/
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
|
||||
ADDRMD: 32
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-12.0,msvc-14.0
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: msvc-14.1
|
||||
CXXSTD: 14,17
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: 14
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: 17
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: latest
|
||||
ADDRMD: 32,64
|
||||
|
||||
install:
|
||||
- set BOOST_BRANCH=develop
|
||||
- if "%APPVEYOR_REPO_BRANCH%" == "master" set BOOST_BRANCH=master
|
||||
- cd ..
|
||||
- git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
- cd boost-root
|
||||
- git submodule update --init tools/boostdep
|
||||
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\unordered\
|
||||
- python tools/boostdep/depinst/depinst.py unordered
|
||||
- cmd /c bootstrap
|
||||
- b2 -d0 headers
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
- if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
- b2 -j3 libs/unordered/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
@@ -1,7 +0,0 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
|
||||
[*.?pp]
|
||||
indent_size = 2
|
||||
@@ -1,212 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "03,11"
|
||||
os: ubuntu-18.04
|
||||
install: g++-4.8
|
||||
- toolset: gcc-5
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-5
|
||||
- toolset: gcc-6
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-6
|
||||
- toolset: gcc-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-8
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
install: g++-8
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-11
|
||||
sanitizers: true
|
||||
- toolset: clang
|
||||
compiler: clang++-3.9
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-3.9
|
||||
- toolset: clang
|
||||
compiler: clang++-4.0
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-4.0
|
||||
- toolset: clang
|
||||
compiler: clang++-5.0
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: clang-5.0
|
||||
- toolset: clang
|
||||
compiler: clang++-6.0
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-6.0
|
||||
- toolset: clang
|
||||
compiler: clang++-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-7
|
||||
- toolset: clang
|
||||
compiler: clang++-8
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-8
|
||||
- toolset: clang
|
||||
compiler: clang++-9
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-9
|
||||
- toolset: clang
|
||||
compiler: clang++-10
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
- toolset: clang
|
||||
compiler: clang++-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: clang
|
||||
compiler: clang++-12
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
sanitizers: true
|
||||
- toolset: clang
|
||||
cxxstd: "03,11,14,17"
|
||||
os: macos-10.15
|
||||
sanitizers: true
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
- name: Create user-config.jam
|
||||
if: matrix.compiler
|
||||
run: |
|
||||
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root
|
||||
./b2 -j3 libs/$LIBRARY/test \
|
||||
toolset=${{matrix.toolset}} \
|
||||
cxxstd=${{matrix.cxxstd}} \
|
||||
variant=debug,release \
|
||||
${{(matrix.sanitizers && 'address-sanitizer=norecover undefined-sanitizer=norecover') || ''}}
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: msvc-14.0
|
||||
cxxstd: 14,latest
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.1
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2016
|
||||
- toolset: msvc-14.2
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.3
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: clang-win
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: gcc
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
addrmd: 64
|
||||
os: windows-2019
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Boost
|
||||
shell: cmd
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
|
||||
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
|
||||
echo LIBRARY: %LIBRARY%
|
||||
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
|
||||
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
|
||||
echo GITHUB_REF: %GITHUB_REF%
|
||||
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
|
||||
set BOOST_BRANCH=develop
|
||||
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
|
||||
echo BOOST_BRANCH: %BOOST_BRANCH%
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
|
||||
- name: Run tests
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker
|
||||
@@ -1 +0,0 @@
|
||||
/doc/html/
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
# Copyright (C) 2016 Daniel James.
|
||||
# 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)
|
||||
|
||||
# Use Trusty to get a reasonably recent version of Boost.
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
language: c++
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxml2-utils
|
||||
- g++-multilib
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: |
|
||||
label="gcc C++03/11";
|
||||
user_config="using gcc : : g++-4.8 --coverage -fsanitize=address ;"
|
||||
enable_coverage=1
|
||||
CXXSTD=03,11
|
||||
- compiler: gcc
|
||||
env: |
|
||||
label="gcc 32 bit C++11";
|
||||
user_config="using gcc : : g++-4.8 -m32 -fsanitize=address ;"
|
||||
CXXSTD=11
|
||||
- compiler: clang
|
||||
env: |
|
||||
label="clang C++11/17";
|
||||
user_config="using clang : : clang++ -fsanitize=address ;"
|
||||
CXXSTD=11,17
|
||||
# sanitized=address not available for 32-bit clang on travis.
|
||||
- compiler: clang
|
||||
env: |
|
||||
label="clang 32 bit";
|
||||
user_config="using clang : : clang++ -m32 ;"
|
||||
CXXSTD=03
|
||||
|
||||
before_install:
|
||||
- if [ -n $enable_coverage ]; then pip install --user cpp-coveralls; fi
|
||||
|
||||
before_script:
|
||||
- export BOOST_VERSION=1.67.0
|
||||
- export BOOST_FILENAME=boost_1_67_0
|
||||
- export BOOST_ROOT=${HOME}/boost
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- touch Jamroot.jam
|
||||
- cd $HOME
|
||||
- echo $user_config > ~/user-config.jam
|
||||
- cat ~/user-config.jam
|
||||
- |
|
||||
# Pick snapshot to use
|
||||
if [ "$TRAVIS_EVENT_TYPE" == "cron" ]
|
||||
then
|
||||
if [ "$TRAVIS_BRANCH" == "master" ]
|
||||
then
|
||||
snapshot=master
|
||||
else
|
||||
snapshot=develop
|
||||
fi
|
||||
else
|
||||
#snapshot=stable
|
||||
snapshot=master
|
||||
fi
|
||||
|
||||
# Download and extract snapshot
|
||||
echo "Downloading ${download_url}"
|
||||
mkdir $HOME/download
|
||||
cd $HOME/download
|
||||
python ${TRAVIS_BUILD_DIR}/ci/download-boost-snapshot.py $snapshot
|
||||
mv * ${BOOST_ROOT}
|
||||
- rm -r ${BOOST_ROOT}/boost/unordered
|
||||
- cd ${BOOST_ROOT}/tools/build
|
||||
- mkdir ${HOME}/opt
|
||||
- bash bootstrap.sh
|
||||
- ./b2 install --prefix=$HOME/opt
|
||||
|
||||
after_success:
|
||||
if [ -n $enable_coverage ]; then coveralls -r ${TRAVIS_BUILD_DIR} -b ${TRAVIS_BUILD_DIR}/test --gcov-options '\-lp' --include include/boost/unordered/ ; fi
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}/test
|
||||
- ${HOME}/opt/bin/b2 -j 3 cxxstd=$CXXSTD -q include=${BOOST_ROOT} include=${TRAVIS_BUILD_DIR}/include
|
||||
- xmllint --noout ${TRAVIS_BUILD_DIR}/doc/ref.xml
|
||||
@@ -1,37 +0,0 @@
|
||||
# Generated by `boostdep --cmake unordered`
|
||||
# Copyright 2020 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
|
||||
project(boost_unordered VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
add_library(boost_unordered INTERFACE)
|
||||
add_library(Boost::unordered ALIAS boost_unordered)
|
||||
|
||||
target_include_directories(boost_unordered INTERFACE include)
|
||||
|
||||
target_link_libraries(boost_unordered
|
||||
INTERFACE
|
||||
Boost::assert
|
||||
Boost::config
|
||||
Boost::container
|
||||
Boost::container_hash
|
||||
Boost::core
|
||||
Boost::detail
|
||||
Boost::move
|
||||
Boost::predef
|
||||
Boost::preprocessor
|
||||
Boost::smart_ptr
|
||||
Boost::throw_exception
|
||||
Boost::tuple
|
||||
Boost::type_traits
|
||||
)
|
||||
|
||||
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
# Copyright 2017 Daniel James.
|
||||
# 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)
|
||||
|
||||
# Using clang format 4.0
|
||||
# http://llvm.org/releases/4.0.0/tools/clang/docs/ClangFormatStyleOptions.html
|
||||
|
||||
# Becuase you have to start somewhere.
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
# Basic settings
|
||||
ColumnLimit: 80
|
||||
NamespaceIndentation: All
|
||||
ContinuationIndentWidth: 2
|
||||
IndentWidth: 2
|
||||
UseTab: Never
|
||||
Language: Cpp
|
||||
Standard: Cpp03
|
||||
|
||||
# Code layout
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignTrailingComments: true
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterNamespace: false
|
||||
AfterClass: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterControlStatement: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
PointerAlignment: Left
|
||||
|
||||
# Boost specific stuff
|
||||
ForEachMacros: [ BOOST_FOREACH, UNORDERED_AUTO_TEST ]
|
||||
@@ -1,411 +0,0 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector<std::string> indices1, indices2;
|
||||
|
||||
static std::string make_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static std::string make_random_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.reserve( N*2+1 );
|
||||
indices1.push_back( make_index( 0 ) );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( make_index( i ) );
|
||||
}
|
||||
|
||||
indices2.reserve( N*2+1 );
|
||||
indices2.push_back( make_index( 0 ) );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::string, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
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>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
|
||||
template<> struct fnv1a_hash_impl<32>
|
||||
{
|
||||
std::size_t operator()( std::string const& s ) const
|
||||
{
|
||||
std::size_t h = 0x811C9DC5u;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x01000193ul;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct fnv1a_hash_impl<64>
|
||||
{
|
||||
std::size_t operator()( std::string const& s ) const
|
||||
{
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x00000100000001B3ull;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
template<class K, class V> using std_unordered_map_fnv1a =
|
||||
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 multi_index_map_fnv1a = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first>, fnv1a_hash >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map_fnv1a =
|
||||
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
#if 0
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<multi_index_map_fnv1a>( "multi_index_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
|
||||
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -1,412 +0,0 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector<std::string> indices1, indices2;
|
||||
|
||||
static std::string make_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static std::string make_random_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.reserve( N*2+1 );
|
||||
indices1.push_back( make_index( 0 ) );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( make_index( i ) );
|
||||
}
|
||||
|
||||
indices2.reserve( N*2+1 );
|
||||
indices2.push_back( make_index( 0 ) );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::string_view, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
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>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
|
||||
template<> struct fnv1a_hash_impl<32>
|
||||
{
|
||||
std::size_t operator()( std::string_view const& s ) const
|
||||
{
|
||||
std::size_t h = 0x811C9DC5u;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x01000193ul;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct fnv1a_hash_impl<64>
|
||||
{
|
||||
std::size_t operator()( std::string_view const& s ) const
|
||||
{
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x00000100000001B3ull;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
template<class K, class V> using std_unordered_map_fnv1a =
|
||||
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 multi_index_map_fnv1a = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first>, fnv1a_hash >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map_fnv1a =
|
||||
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
#if 0
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<multi_index_map_fnv1a>( "multi_index_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
|
||||
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -1,342 +0,0 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector< std::uint32_t > indices1, indices2, indices3;
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( i );
|
||||
}
|
||||
|
||||
indices2.push_back( 0 );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( static_cast<std::uint32_t>( rng() ) );
|
||||
}
|
||||
}
|
||||
|
||||
indices3.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint32_t)i << 11 );
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices3[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::uint32_t, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
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>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 25 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -1,342 +0,0 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector< std::uint64_t > indices1, indices2, indices3;
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( i );
|
||||
}
|
||||
|
||||
indices2.push_back( 0 );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( rng() );
|
||||
}
|
||||
}
|
||||
|
||||
indices3.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint64_t)i << 40 );
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint64_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices3[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::uint64_t, std::uint64_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
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>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 25 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import urllib, os, os.path, sys, json, tarfile, zipfile, tempfile
|
||||
|
||||
def download(snapshot):
|
||||
if snapshot == 'stable':
|
||||
# TODO: Default version/filename if not available?
|
||||
downloads = [
|
||||
"https://sourceforge.net/projects/boost/files/boost/%s/%s.tar.bz2/download" %
|
||||
(os.environ['BOOST_VERSION'], os.environ['BOOST_FILENAME'])]
|
||||
else:
|
||||
json_response = urllib.urlopen('https://api.bintray.com/packages/boostorg/%s/snapshot/files' % (snapshot))
|
||||
x = json.load(json_response)
|
||||
|
||||
extension_priorities = { '.bz2': 2, '.gz': 1, '.zip': 0 }
|
||||
file_list = []
|
||||
version_dates = {}
|
||||
for file in x:
|
||||
file_extension = os.path.splitext(file['path'])[1]
|
||||
if (file_extension in extension_priorities):
|
||||
file['priority'] = extension_priorities[file_extension]
|
||||
file_list.append(file)
|
||||
if not file['version'] in version_dates or file['created'] < version_dates[file['version']]:
|
||||
version_dates[file['version']] = file['created']
|
||||
file_list.sort(key=lambda x: (version_dates[x['version']], x['priority']), reverse=True)
|
||||
downloads = ['http://dl.bintray.com/boostorg/%s/%s' % (snapshot, file['path']) for file in file_list]
|
||||
|
||||
filename = ''
|
||||
for download_url in downloads:
|
||||
try:
|
||||
print "Downloading: " + download_url
|
||||
(filename, headers) = urllib.urlretrieve(download_url)
|
||||
|
||||
print "Extracting: " + filename
|
||||
dir = tempfile.mkdtemp()
|
||||
extract(filename, dir)
|
||||
os.remove(filename)
|
||||
files = os.listdir(dir)
|
||||
assert(len(files) == 1)
|
||||
os.rename(os.path.join(dir, files[0]), 'boost')
|
||||
return
|
||||
except IOError:
|
||||
print "Error opening URL: " + download_url
|
||||
|
||||
def extract(filename, path = '.'):
|
||||
if (filename.endswith(".gz")):
|
||||
tar = tarfile.open(filename, "r:gz")
|
||||
tar.extractall(path)
|
||||
tar.close
|
||||
elif (filename.endswith(".bz2")):
|
||||
tar = tarfile.open(filename, "r:bz2")
|
||||
tar.extractall(path)
|
||||
tar.close
|
||||
elif (filename.endswith(".zip")):
|
||||
zip = zipfile.ZipFile(filename, "r")
|
||||
zip.extractall(path)
|
||||
zip.close
|
||||
else:
|
||||
assert False
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
download('stable')
|
||||
elif len(sys.argv) == 2:
|
||||
download(sys.argv[1])
|
||||
else:
|
||||
print "Usage: %s [stable|branch-name]" % (sys.argv[0])
|
||||
+50
-12
@@ -1,21 +1,59 @@
|
||||
|
||||
# Copyright 2005 Daniel James.
|
||||
# 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)
|
||||
|
||||
import asciidoctor ;
|
||||
using boostbook ;
|
||||
using quickbook ;
|
||||
|
||||
html unordered.html : unordered.adoc ;
|
||||
path-constant images_location : ../ ;
|
||||
path-constant admonishment_location : ../../../../doc/src/images ;
|
||||
|
||||
install html_ : unordered.html : <location>html ;
|
||||
xml unordered : unordered.qbk ;
|
||||
boostbook standalone : unordered :
|
||||
<xsl:param>chunk.first.sections=1
|
||||
<xsl:param>chunk.section.depth=2
|
||||
<xsl:param>generate.section.toc.level=2
|
||||
<xsl:param>toc.section.depth=1
|
||||
<xsl:param>toc.max.depth=1
|
||||
|
||||
pdf unordered.pdf : unordered.adoc ;
|
||||
explicit unordered.pdf ;
|
||||
<xsl:param>boost.compact.typedef=0
|
||||
<xsl:param>boost.compact.function=0
|
||||
<xsl:param>boost.compact.enum=0
|
||||
|
||||
install pdf_ : unordered.pdf : <location>pdf ;
|
||||
explicit pdf_ ;
|
||||
# HTML Options:
|
||||
|
||||
<format>html:<xsl:param>boost.root=../../../..
|
||||
<format>html:<xsl:param>img.src.path=../../../../doc/html/
|
||||
<format>xhtml:<xsl:param>boost.root=../../../..
|
||||
<format>xhtml:<xsl:param>img.src.path=../../../../doc/html/
|
||||
|
||||
# PDF Options:
|
||||
|
||||
# TOC Generation: this is needed for FOP-0.9 and later:
|
||||
<xsl:param>fop1.extensions=0
|
||||
<format>pdf:<xsl:param>xep.extensions=1
|
||||
# TOC generation: this is needed for FOP 0.2, but must not be set to zero for FOP-0.9!
|
||||
<format>pdf:<xsl:param>fop.extensions=0
|
||||
# No indent on body text:
|
||||
<format>pdf:<xsl:param>body.start.indent=0pt
|
||||
# Margin size:
|
||||
<format>pdf:<xsl:param>page.margin.inner=0.5in
|
||||
# Margin size:
|
||||
<format>pdf:<xsl:param>page.margin.outer=0.5in
|
||||
# Paper type = A4
|
||||
<format>pdf:<xsl:param>paper.type=A4
|
||||
# Yes, we want graphics for admonishments:
|
||||
<xsl:param>admon.graphics=1
|
||||
# Set this one for PDF generation *only*:
|
||||
# default png graphics are awful in PDF form,
|
||||
# better use SVG's instead:
|
||||
<format>pdf:<xsl:param>admon.graphics.extension=".svg"
|
||||
<format>pdf:<xsl:param>use.role.for.mediaobject=1
|
||||
<format>pdf:<xsl:param>preferred.mediaobject.role=print
|
||||
<format>pdf:<xsl:param>img.src.path=$(images_location)/
|
||||
#<format>pdf:<xsl:param>admon.graphics.path=$(admonishment_location)
|
||||
<format>pdf:<xsl:param>draft.mode="no"
|
||||
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/libs/unordered/doc/html
|
||||
;
|
||||
|
||||
###############################################################################
|
||||
alias boostdoc ;
|
||||
explicit boostdoc ;
|
||||
alias boostrelease : html_ ;
|
||||
explicit boostrelease ;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
Copyright Daniel James 2008-2009
|
||||
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)
|
||||
-->
|
||||
<section id="unordered.bibliography">
|
||||
<title>Bibliography</title>
|
||||
<bibliography>
|
||||
<biblioentry>
|
||||
<biblioset relation="journal">
|
||||
<title>C/C++ Users Journal</title>
|
||||
<date>February, 2006</date>
|
||||
</biblioset>
|
||||
<biblioset relation="article">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Pete</firstname>
|
||||
<surname>Becker</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title><ulink url="http://www.ddj.com/cpp/184402066">STL and TR1: Part III - Unordered containers</ulink></title>
|
||||
</biblioset>
|
||||
<para>An introducation to the standard unordered containers.</para>
|
||||
</biblioentry>
|
||||
</bibliography>
|
||||
</section>
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
[/ Copyright 2006-2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[section:buckets The Data Structure]
|
||||
|
||||
The containers are made up of a number of 'buckets', each of which can contain
|
||||
any number of elements. For example, the following diagram shows an [classref
|
||||
boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`,
|
||||
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
|
||||
have more buckets).
|
||||
|
||||
[diagram buckets]
|
||||
|
||||
In order to decide which bucket to place an element in, the container applies
|
||||
the hash function, `Hash`, to the element's key (for `unordered_set` and
|
||||
`unordered_multiset` the key is the whole element, but is referred to as the key
|
||||
so that the same terminology can be used for sets and maps). This returns a
|
||||
value of type `std::size_t`. `std::size_t` has a much greater range of values
|
||||
then the number of buckets, so that container applies another transformation to
|
||||
that value to choose a bucket to place the element in.
|
||||
|
||||
Retrieving the elements for a given key is simple. The same process is applied
|
||||
to the key to find the correct bucket. Then the key is compared with the
|
||||
elements in the bucket to find any elements that match (using the equality
|
||||
predicate `Pred`). If the hash function has worked well the elements will be
|
||||
evenly distributed amongst the buckets so only a small number of elements will
|
||||
need to be examined.
|
||||
|
||||
There is [link unordered.hash_equality more information on hash functions and
|
||||
equality predicates in the next section].
|
||||
|
||||
You can see in the diagram that `A` & `D` have been placed in the same bucket.
|
||||
When looking for elements in this bucket up to 2 comparisons are made, making
|
||||
the search slower. This is known as a collision. To keep things fast we try to
|
||||
keep collisions to a minimum.
|
||||
|
||||
'''
|
||||
<table frame="all"><title>Methods for Accessing Buckets</title>
|
||||
<tgroup cols="2">
|
||||
<thead><row>
|
||||
<entry><para>Method</para></entry>
|
||||
<entry><para>Description</para></entry>
|
||||
</row></thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>'''`size_type bucket_count() const`'''</entry>
|
||||
<entry>'''The number of buckets.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type max_bucket_count() const`'''</entry>
|
||||
<entry>'''An upper bound on the number of buckets.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type bucket_size(size_type n) const`'''</entry>
|
||||
<entry>'''The number of elements in bucket `n`.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type bucket(key_type const& k) const`'''</entry>
|
||||
<entry>'''Returns the index of the bucket which would contain k'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`local_iterator begin(size_type n);`'''</entry>
|
||||
<entry morerows='5'>'''Return begin and end iterators for bucket `n`.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`local_iterator end(size_type n);`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator begin(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator end(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator cbegin(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator cend(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
'''
|
||||
|
||||
[h2 Controlling the number of buckets]
|
||||
|
||||
As more elements are added to an unordered associative container, the number
|
||||
of elements in the buckets will increase causing performance to degrade.
|
||||
To combat this the containers increase the bucket count as elements are inserted.
|
||||
You can also tell the container to change the bucket count (if required) by
|
||||
calling `rehash`.
|
||||
|
||||
The standard leaves a lot of freedom to the implementer to decide how the
|
||||
number of buckets are chosen, but it does make some requirements based on the
|
||||
container's 'load factor', the average number of elements per bucket.
|
||||
Containers also have a 'maximum load factor' which they should try to keep the
|
||||
load factor below.
|
||||
|
||||
You can't control the bucket count directly but there are two ways to
|
||||
influence it:
|
||||
|
||||
* Specify the minimum number of buckets when constructing a container or
|
||||
when calling `rehash`.
|
||||
* Suggest a maximum load factor by calling `max_load_factor`.
|
||||
|
||||
`max_load_factor` doesn't let you set the maximum load factor yourself, it just
|
||||
lets you give a /hint/. And even then, the draft standard doesn't actually
|
||||
require the container to pay much attention to this value. The only time the
|
||||
load factor is /required/ to be less than the maximum is following a call to
|
||||
`rehash`. But most implementations will try to keep the number of elements
|
||||
below the max load factor, and set the maximum load factor to be the same as
|
||||
or close to the hint - unless your hint is unreasonably small or large.
|
||||
|
||||
[table:bucket_size Methods for Controlling Bucket Size
|
||||
[[Method] [Description]]
|
||||
|
||||
[
|
||||
[`X(size_type n)`]
|
||||
[Construct an empty container with at least `n` buckets (`X` is the container type).]
|
||||
]
|
||||
[
|
||||
[`X(InputIterator i, InputIterator j, size_type n)`]
|
||||
[Construct an empty container with at least `n` buckets and insert elements
|
||||
from the range \[`i`, `j`) (`X` is the container type).]
|
||||
]
|
||||
[
|
||||
[`float load_factor() const`]
|
||||
[The average number of elements per bucket.]
|
||||
]
|
||||
[
|
||||
[`float max_load_factor() const`]
|
||||
[Returns the current maximum load factor.]
|
||||
]
|
||||
[
|
||||
[`float max_load_factor(float z)`]
|
||||
[Changes the container's maximum load factor, using `z` as a hint.]
|
||||
]
|
||||
[
|
||||
[`void rehash(size_type n)`]
|
||||
[Changes the number of buckets so that there at least n buckets, and
|
||||
so that the load factor is less than the maximum load factor.]
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
[h2 Iterator Invalidation]
|
||||
|
||||
It is not specified how member functions other than `rehash` affect
|
||||
the bucket count, although `insert` is only allowed to invalidate iterators
|
||||
when the insertion causes the load factor to be greater than or equal to the
|
||||
maximum load factor. For most implementations this means that insert will only
|
||||
change the number of buckets when this happens. While iterators can be
|
||||
invalidated by calls to `insert` and `rehash`, pointers and references to the
|
||||
container's elements are never invalidated.
|
||||
|
||||
In a similar manner to using `reserve` for `vector`s, it can be a good idea
|
||||
to call `rehash` before inserting a large number of elements. This will get
|
||||
the expensive rehashing out of the way and let you store iterators, safe in
|
||||
the knowledge that they won't be invalidated. If you are inserting `n`
|
||||
elements into container `x`, you could first call:
|
||||
|
||||
x.rehash((x.size() + n) / x.max_load_factor() + 1);
|
||||
|
||||
[blurb Note: `rehash`'s argument is the minimum number of buckets, not the
|
||||
number of elements, which is why the new size is divided by the maximum load factor. The
|
||||
`+ 1` guarantees there is no invalidation; without it, reallocation could occur
|
||||
if the number of bucket exactly divides the target size, since the container is
|
||||
allowed to rehash when the load factor is equal to the maximum load factor.]
|
||||
|
||||
[endsect]
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
|
||||
[/ Copyright 2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[template ticket[number]'''<ulink
|
||||
url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
|
||||
|
||||
[section:changes Change Log]
|
||||
|
||||
[h2 Review Version]
|
||||
|
||||
Initial review version, for the review conducted from 7th December 2007 to
|
||||
16th December 2007.
|
||||
|
||||
[h2 1.35.0 Add-on - 31st March 2008]
|
||||
|
||||
Unofficial release uploaded to vault, to be used with Boost 1.35.0. Incorporated
|
||||
many of the suggestions from the review.
|
||||
|
||||
* Improved portability thanks to Boost regression testing.
|
||||
* Fix lots of typos, and clearer text in the documentation.
|
||||
* Fix floating point to `std::size_t` conversion when calculating sizes from
|
||||
the max load factor, and use `double` in the calculation for greater accuracy.
|
||||
* Fix some errors in the examples.
|
||||
|
||||
[h2 Boost 1.36.0]
|
||||
|
||||
First official release.
|
||||
|
||||
* Rearrange the internals.
|
||||
* Move semantics - full support when rvalue references are available, emulated
|
||||
using a cut down version of the Adobe move library when they are not.
|
||||
* Emplace support when rvalue references and variadic template are available.
|
||||
* More efficient node allocation when rvalue references and variadic template
|
||||
are available.
|
||||
* Added equality operators.
|
||||
|
||||
[h2 Boost 1.37.0]
|
||||
|
||||
* Rename overload of `emplace` with hint, to `emplace_hint` as specified in
|
||||
[@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf n2691].
|
||||
* Provide forwarding headers at `<boost/unordered/unordered_map_fwd.hpp>` and
|
||||
`<boost/unordered/unordered_set_fwd.hpp>`.
|
||||
* Move all the implementation inside `boost/unordered`, to assist
|
||||
modularization and hopefully make it easier to track changes in subversion.
|
||||
|
||||
[h2 Boost 1.38.0]
|
||||
|
||||
* Use [@boost:/libs/utility/swap.html `boost::swap`].
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2237 Ticket 2237]:
|
||||
Document that the equality and inequality operators are undefined for two
|
||||
objects if their equality predicates aren't equivalent. Thanks to Daniel
|
||||
Krügler.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/1710 Ticket 1710]:
|
||||
Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé
|
||||
Brönnimann.
|
||||
* Use
|
||||
[@boost:/libs/type_traits/doc/html/boost_typetraits/category/alignment.html
|
||||
aligned storage] to store the types. This changes the way the allocator is
|
||||
used to construct nodes. It used to construct the node with two calls to
|
||||
the allocator's `construct` method - once for the pointers and once for the
|
||||
value. It now constructs the node with a single call to construct and
|
||||
then constructs the value using in place construction.
|
||||
* Add support for C++0x initializer lists where they're available (currently
|
||||
only g++ 4.4 in C++0x mode).
|
||||
|
||||
[h2 Boost 1.39.0]
|
||||
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2756 Ticket 2756]: Avoid a warning
|
||||
on Visual C++ 2009.
|
||||
* Some other minor internal changes to the implementation, tests and
|
||||
documentation.
|
||||
* Avoid an unnecessary copy in `operator[]`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]: Fix length of
|
||||
prime number list.
|
||||
|
||||
[h2 Boost 1.40.0]
|
||||
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]:
|
||||
Store the prime list as a preprocessor sequence - so that it will always get
|
||||
the length right if it changes again in the future.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/1978 Ticket 1978]:
|
||||
Implement `emplace` for all compilers.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2908 Ticket 2908],
|
||||
[@https://svn.boost.org/trac/boost/ticket/3096 Ticket 3096]:
|
||||
Some workarounds for old versions of borland, including adding explicit
|
||||
destructors to all containers.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/3082 Ticket 3082]:
|
||||
Disable incorrect Visual C++ warnings.
|
||||
* Better configuration for C++0x features when the headers aren't available.
|
||||
* Create less buckets by default.
|
||||
|
||||
[h2 Boost 1.41.0 - Major update]
|
||||
|
||||
* The original version made heavy use of macros to sidestep some of the older
|
||||
compilers' poor template support. But since I no longer support those
|
||||
compilers and the macro use was starting to become a maintenance burden it
|
||||
has been rewritten to use templates instead of macros for the implementation
|
||||
classes.
|
||||
|
||||
* The container objcet is now smaller thanks to using `boost::compressed_pair`
|
||||
for EBO and a slightly different function buffer - now using a bool instead
|
||||
of a member pointer.
|
||||
|
||||
* Buckets are allocated lazily which means that constructing an empty container
|
||||
will not allocate any memory.
|
||||
|
||||
[h2 Boost 1.42.0]
|
||||
|
||||
* Support instantiating the containers with incomplete value types.
|
||||
* Reduced the number of warnings (mostly in tests).
|
||||
* Improved codegear compatibility.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3693 Ticket 3693]:
|
||||
Add `erase_return_void` as a temporary workaround for the current
|
||||
`erase` which can be inefficient because it has to find the next
|
||||
element to return an iterator.
|
||||
* Add templated find overload for compatible keys.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3773 Ticket 3773]:
|
||||
Add missing `std` qualifier to `ptrdiff_t`.
|
||||
* Some code formatting changes to fit almost all lines into 80 characters.
|
||||
|
||||
[h2 Boost 1.43.0]
|
||||
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3966 Ticket 3966]:
|
||||
`erase_return_void` is now `quick_erase`, which is the
|
||||
[@http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579
|
||||
current forerunner for resolving the slow erase by iterator], although
|
||||
there's a strong possibility that this may change in the future. The old
|
||||
method name remains for backwards compatibility but is considered deprecated
|
||||
and will be removed in a future release.
|
||||
* Use Boost.Exception.
|
||||
* Stop using deprecated `BOOST_HAS_*` macros.
|
||||
|
||||
[h2 Boost 1.45.0]
|
||||
|
||||
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
|
||||
iterators which returns `value_type` by copy.
|
||||
|
||||
[h2 Boost 1.48.0 - Major update]
|
||||
|
||||
This is major change which has been converted to use Boost.Move's move
|
||||
emulation, and be more compliant with the C++11 standard. See the
|
||||
[link unordered.compliance compliance section] for details.
|
||||
|
||||
The container now meets C++11's complexity requirements, but to do so
|
||||
uses a little more memory. This means that `quick_erase` and
|
||||
`erase_return_void` are no longer required, they'll be removed in a
|
||||
future version.
|
||||
|
||||
C++11 support has resulted in some breaking changes:
|
||||
|
||||
* Equality comparison has been changed to the C++11 specification.
|
||||
In a container with equivalent keys, elements in a group with equal
|
||||
keys used to have to be in the same order to be considered equal,
|
||||
now they can be a permutation of each other. To use the old
|
||||
behavior define the macro `BOOST_UNORDERED_DEPRECATED_EQUALITY`.
|
||||
|
||||
* The behaviour of swap is different when the two containers to be
|
||||
swapped has unequal allocators. It used to allocate new nodes using
|
||||
the appropriate allocators, it now swaps the allocators if
|
||||
the allocator has a member structure `propagate_on_container_swap`,
|
||||
such that `propagate_on_container_swap::value` is true.
|
||||
|
||||
* Allocator's `construct` and `destroy` functions are called with raw
|
||||
pointers, rather than the allocator's `pointer` type.
|
||||
|
||||
* `emplace` used to emulate the variadic pair constructors that
|
||||
appeared in early C++0x drafts. Since they were removed it no
|
||||
longer does so. It does emulate the new `piecewise_construct`
|
||||
pair constructors - only you need to use
|
||||
`boost::piecewise_construct`. To use the old emulation of
|
||||
the variadic consturctors define
|
||||
`BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT`.
|
||||
|
||||
[h2 Boost 1.49.0]
|
||||
|
||||
* Fix warning due to accidental odd assignment.
|
||||
* Slightly better error messages.
|
||||
|
||||
[h2 Boost 1.50.0]
|
||||
|
||||
* Fix equality for `unordered_multiset` and `unordered_multimap`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6857 Ticket 6857]:
|
||||
Implement `reserve`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6771 Ticket 6771]:
|
||||
Avoid gcc's `-Wfloat-equal` warning.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6784 Ticket 6784]:
|
||||
Fix some Sun specific code.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6190 Ticket 6190]:
|
||||
Avoid gcc's `-Wshadow` warning.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6905 Ticket 6905]:
|
||||
Make namespaces in macros compatible with `bcp` custom namespaces.
|
||||
Fixed by Luke Elliott.
|
||||
* Remove some of the smaller prime number of buckets, as they may make
|
||||
collisions quite probable (e.g. multiples of 5 are very common because
|
||||
we used base 10).
|
||||
* On old versions of Visual C++, use the container library's implementation
|
||||
of `allocator_traits`, as it's more likely to work.
|
||||
* On machines with 64 bit std::size_t, use power of 2 buckets, with Thomas
|
||||
Wang's hash function to pick which one to use. As modulus is very slow
|
||||
for 64 bit values.
|
||||
* Some internal changes.
|
||||
|
||||
[h2 Boost 1.51.0]
|
||||
|
||||
* Fix construction/destruction issue when using a C++11 compiler with a
|
||||
C++03 allocator ([ticket 7100]).
|
||||
* Remove a `try..catch` to support compiling without exceptions.
|
||||
* Adjust SFINAE use to try to supprt g++ 3.4 ([ticket 7175]).
|
||||
* Updated to use the new config macros.
|
||||
|
||||
[h2 Boost 1.52.0]
|
||||
|
||||
* Faster assign, which assigns to existing nodes where possible, rather than
|
||||
creating entirely new nodes and copy constructing.
|
||||
* Fixed bug in `erase_range` ([ticket 7471]).
|
||||
* Reverted some of the internal changes to how nodes are created, especially
|
||||
for C++11 compilers. 'construct' and 'destroy' should work a little better
|
||||
for C++11 allocators.
|
||||
* Simplified the implementation a bit. Hopefully more robust.
|
||||
|
||||
[endsect]
|
||||
@@ -0,0 +1,161 @@
|
||||
[/ Copyright 2006-2011 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[section:comparison Comparison with Associative Containers]
|
||||
|
||||
[table:interface_differences Interface differences.
|
||||
[[Associative Containers] [Unordered Associative Containers]]
|
||||
|
||||
[
|
||||
[Parameterized by an ordering relation `Compare`]
|
||||
[Parameterized by a function object `Hash` and an equivalence relation
|
||||
`Pred`]
|
||||
]
|
||||
[
|
||||
[Keys can be compared using `key_compare` which is accessed by member function `key_comp()`,
|
||||
values can be compared using `value_compare` which is accessed by member function `value_comp()`.]
|
||||
[Keys can be hashed using `hasher` which is accessed by member function `hash_function()`,
|
||||
and checked for equality using `key_equal` which is accessed by member function `key_eq()`.
|
||||
There is no function object for compared or hashing values.]
|
||||
]
|
||||
[
|
||||
[Constructors have optional extra parameters for the comparison object.]
|
||||
[Constructors have optional extra parameters for the initial minimum
|
||||
number of buckets, a hash function and an equality object.]
|
||||
]
|
||||
|
||||
[
|
||||
[Keys `k1`, `k2` are considered equivalent if
|
||||
`!Compare(k1, k2) && !Compare(k2, k1)`]
|
||||
[Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`]
|
||||
]
|
||||
[
|
||||
[Member function `lower_bound(k)` and `upper_bound(k)`]
|
||||
[No equivalent. Since the elements aren't ordered `lower_bound` and
|
||||
`upper_bound` would be meaningless.]
|
||||
]
|
||||
[
|
||||
[`equal_range(k)` returns an empty range at the position that k
|
||||
would be inserted if k isn't present in the container.]
|
||||
[`equal_range(k)` returns a range at the end of the container if
|
||||
k isn't present in the container. It can't return a positioned
|
||||
range as k could be inserted into multiple place. To find out the
|
||||
bucket that k would be inserted into use `bucket(k)`. But remember
|
||||
that an insert can cause the container to rehash - meaning that the
|
||||
element can be inserted into a different bucket.]
|
||||
]
|
||||
[
|
||||
[`iterator`, `const_iterator` are of the bidirectional category.]
|
||||
[`iterator`, `const_iterator` are of at least the forward category.]
|
||||
]
|
||||
[
|
||||
[Iterators, pointers and references to the container's elements are
|
||||
never invalidated.]
|
||||
[[link unordered.buckets.iterator_invalidation Iterators can
|
||||
be invalidated by calls to insert or rehash]. Pointers and
|
||||
references to the container's elements are never invalidated.]
|
||||
]
|
||||
[
|
||||
[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.]
|
||||
]
|
||||
[
|
||||
[No equivalent]
|
||||
[Local iterators can be used to iterate through individual buckets.
|
||||
(The order of local iterators and iterators aren't
|
||||
required to have any correspondence.)]
|
||||
]
|
||||
[
|
||||
[Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators.]
|
||||
[Can be compared using the `==` and `!=` operators.]
|
||||
]
|
||||
[
|
||||
[]
|
||||
[When inserting with a hint, implementations are permitted to ignore
|
||||
the hint.]
|
||||
]
|
||||
[
|
||||
[`erase` never throws an exception]
|
||||
[The containers' hash or predicate function can throw exceptions
|
||||
from `erase`]
|
||||
]
|
||||
]
|
||||
|
||||
[table:complexity_guarantees Complexity Guarantees
|
||||
[[Operation] [Associative Containers] [Unordered Associative Containers]]
|
||||
[
|
||||
[Construction of empty container]
|
||||
[constant]
|
||||
[O(/n/) where /n/ is the minimum number of buckets.]
|
||||
]
|
||||
[
|
||||
[Construction of container from a range of /N/ elements]
|
||||
[O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`]
|
||||
[Average case O(/N/), worst case
|
||||
O(/N/'''<superscript>2</superscript>''')]
|
||||
]
|
||||
[
|
||||
[Insert a single element]
|
||||
[logarithmic]
|
||||
[Average case constant, worst case linear]
|
||||
]
|
||||
[
|
||||
[Insert a single element with a hint]
|
||||
[Amortized constant if t elements inserted right after hint,
|
||||
logarithmic otherwise]
|
||||
[Average case constant, worst case linear (ie. the same as
|
||||
a normal insert).]
|
||||
]
|
||||
[
|
||||
[Inserting a range of /N/ elements]
|
||||
[ /N/ log(`size()`+/N/) ]
|
||||
[Average case O(/N/), worst case O(/N/ * `size()`)]
|
||||
]
|
||||
[
|
||||
[Erase by key, `k`]
|
||||
[O(log(`size()`) + `count(k)`)]
|
||||
[Average case: O(`count(k)`), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Erase a single element by iterator]
|
||||
[Amortized constant]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Erase a range of /N/ elements]
|
||||
[O(log(`size()`) + /N/)]
|
||||
[Average case: O(/N/), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Clearing the container]
|
||||
[O(`size()`)]
|
||||
[O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Find]
|
||||
[logarithmic]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[/ TODO: Average case is probably wrong. ]
|
||||
[
|
||||
[Count]
|
||||
[O(log(`size()`) + `count(k)`)]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[`equal_range(k)`]
|
||||
[logarithmic]
|
||||
[Average case: O(`count(k)`), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[`lower_bound`,`upper_bound`]
|
||||
[logarithmic]
|
||||
[n/a]
|
||||
]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
@@ -1,31 +1,25 @@
|
||||
[#compliance]
|
||||
= Standard Compliance
|
||||
[/ Copyright 2011 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
:idprefix: compliance_
|
||||
[section:compliance C++11 Compliance]
|
||||
|
||||
:cpp: C++
|
||||
|
||||
The intent of Boost.Unordered is to implement a close (but imperfect)
|
||||
implementation of the {cpp}17 standard, that will work with {cpp}98 upwards.
|
||||
The wide compatibility does mean some comprimises have to be made.
|
||||
With a compiler and library that fully support {cpp}11, the differences should
|
||||
be minor.
|
||||
|
||||
== Move emulation
|
||||
[section:move Move emulation]
|
||||
|
||||
Support for move semantics is implemented using Boost.Move. If rvalue
|
||||
references are available it will use them, but if not it uses a close,
|
||||
but imperfect emulation. On such compilers:
|
||||
but imperfect emulation. On such compilers you'll need to use Boost.Move
|
||||
to take advantage of using movable container elements, also note that:
|
||||
|
||||
* Non-copyable objects can be stored in the containers.
|
||||
They can be constructed in place using `emplace`, or if they support
|
||||
Boost.Move, moved into place.
|
||||
* The containers themselves are not movable.
|
||||
* Non-copyable objects can be stored in the containers, but without support
|
||||
for rvalue references the container will not be movable.
|
||||
* Argument forwarding is not perfect.
|
||||
|
||||
== Use of allocators
|
||||
[endsect]
|
||||
|
||||
{cpp}11 introduced a new allocator system. It's backwards compatible due to
|
||||
[section:allocator_compliance Use of allocators]
|
||||
|
||||
C++11 introduced a new allocator system. It's backwards compatible due to
|
||||
the lax requirements for allocators in the old standard, but might need
|
||||
some changes for allocators which worked with the old versions of the
|
||||
unordered containers.
|
||||
@@ -56,27 +50,23 @@ Due to imperfect move emulation, some assignments might check
|
||||
`propagate_on_container_copy_assignment` on some compilers and
|
||||
`propagate_on_container_move_assignment` on others.
|
||||
|
||||
== Construction/Destruction using allocators
|
||||
The use of the allocator's construct and destruct methods might be a bit
|
||||
surprising.
|
||||
Nodes are constructed and destructed using the allocator, but the elements
|
||||
are stored in aligned space within the node and constructed and destructed
|
||||
by calling the constructor and destructor directly.
|
||||
|
||||
The following support is required for full use of {cpp}11 style
|
||||
construction/destruction:
|
||||
In C++11 the allocator's construct function has the signature:
|
||||
|
||||
* Variadic templates.
|
||||
* Piecewise construction of `std::pair`.
|
||||
* Either `std::allocator_traits` or expression SFINAE.
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, Args&&... args);
|
||||
|
||||
This is detected using Boost.Config. The macro
|
||||
`BOOST_UNORDERED_CXX11_CONSTRUCTION` will be set to 1 if it is found, or 0
|
||||
otherwise.
|
||||
|
||||
When this is the case `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` will always be used, apart from when piecewise
|
||||
constructing a `std::pair` using `boost::tuple` (see <<compliance_pairs,below>>), but that should be easily avoided.
|
||||
|
||||
When support is not available `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` are never called.
|
||||
|
||||
== Pointer Traits
|
||||
which supports calling `construct` for the contained object, but
|
||||
most existing allocators don't support this. If member function detection
|
||||
was good enough then with old allocators it would fall back to calling
|
||||
the element's constructor directly but in general, detection isn't good
|
||||
enough to do this which is why Boost.Unordered just calls the constructor
|
||||
directly every time. In most cases this will work okay.
|
||||
|
||||
`pointer_traits` aren't used. Instead, pointer types are obtained from
|
||||
rebound allocators, this can cause problems if the allocator can't be
|
||||
@@ -84,33 +74,38 @@ used with incomplete types. If `const_pointer` is not defined in the
|
||||
allocator, `boost::pointer_to_other<pointer, const value_type>::type`
|
||||
is used to obtain a const pointer.
|
||||
|
||||
== Pairs
|
||||
[endsect]
|
||||
|
||||
[section:pairs Pairs]
|
||||
|
||||
Since the containers use `std::pair` they're limited to the version
|
||||
from the current standard library. But since {cpp}11 ``std::pair``'s
|
||||
from the current standard library. But since C++11 `std::pair`'s
|
||||
`piecewise_construct` based constructor is very useful, `emplace`
|
||||
emulates it with a `piecewise_construct` in the `boost::unordered`
|
||||
namespace. So for example, the following will work:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
boost::unordered_multimap<std::string, std::complex> x;
|
||||
boost::unordered_multimap<std::string, std::complex> x;
|
||||
|
||||
x.emplace(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple("key"), boost::make_tuple(1, 2));
|
||||
----
|
||||
x.emplace(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple("key"), boost::make_tuple(1, 2));
|
||||
|
||||
Older drafts of the standard also supported variadic constructors
|
||||
for `std::pair`, where the first argument would be used for the
|
||||
first part of the pair, and the remaining for the second part.
|
||||
|
||||
== Miscellaneous
|
||||
[endsect]
|
||||
|
||||
[section:misc Miscellaneous]
|
||||
|
||||
When swapping, `Pred` and `Hash` are not currently swapped by calling
|
||||
`swap`, their copy constructors are used. As a consequence when swapping
|
||||
an exception may be thrown from their copy constructor.
|
||||
an exception may be throw from their copy constructor.
|
||||
|
||||
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.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
@@ -0,0 +1,313 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="507.85925"
|
||||
height="400.45422"
|
||||
viewBox="1.33 0.95 6.01 4.09"
|
||||
id="svg2">
|
||||
<defs
|
||||
id="defs95" />
|
||||
<rect
|
||||
width="6.0023117"
|
||||
height="4.0815721"
|
||||
x="1.3310578"
|
||||
y="0.95080346"
|
||||
id="rect4"
|
||||
style="fill:#e5e5e5;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="6.0023117"
|
||||
height="4.0815721"
|
||||
x="1.3310578"
|
||||
y="0.95080346"
|
||||
id="rect6"
|
||||
style="opacity:1;fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="1.5711501"
|
||||
y="1.1908962"
|
||||
id="rect8"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="1.5711501"
|
||||
y="1.1908962"
|
||||
id="rect10"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="1.7289008"
|
||||
y="1.4950322"
|
||||
id="text12"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 1</text>
|
||||
<line
|
||||
x1="1.5711501"
|
||||
y1="1.6710808"
|
||||
x2="2.7716124"
|
||||
y2="1.6710808"
|
||||
id="line14"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.0117054"
|
||||
y="1.1908962"
|
||||
id="rect16"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.0117054"
|
||||
y="1.1908962"
|
||||
id="rect18"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="3.1603069"
|
||||
y="1.4950322"
|
||||
id="text20"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 2</text>
|
||||
<line
|
||||
x1="3.0117054"
|
||||
y1="1.6710808"
|
||||
x2="4.2121677"
|
||||
y2="1.6710808"
|
||||
id="line22"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="4.4522605"
|
||||
y="1.1908962"
|
||||
id="rect24"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="4.4522605"
|
||||
y="1.1908962"
|
||||
id="rect26"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="4.5917125"
|
||||
y="1.4950322"
|
||||
id="text28"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 3</text>
|
||||
<line
|
||||
x1="4.4522605"
|
||||
y1="1.6710808"
|
||||
x2="5.6527228"
|
||||
y2="1.6710808"
|
||||
id="line30"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.8928151"
|
||||
y="1.1908962"
|
||||
id="rect32"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.8928151"
|
||||
y="1.1908962"
|
||||
id="rect34"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="6.0688629"
|
||||
y="1.4858831"
|
||||
id="text36"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 4</text>
|
||||
<line
|
||||
x1="5.8928151"
|
||||
y1="1.6710808"
|
||||
x2="7.093277"
|
||||
y2="1.6710808"
|
||||
id="line38"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="2.2941716"
|
||||
y="3.1054616"
|
||||
id="rect40"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="2.2941716"
|
||||
y="3.1054616"
|
||||
id="rect42"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="2.4427731"
|
||||
y="3.4187472"
|
||||
id="text44"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 5</text>
|
||||
<line
|
||||
x1="2.2941716"
|
||||
y1="3.5856469"
|
||||
x2="3.4946339"
|
||||
y2="3.5856469"
|
||||
id="line46"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.7347264"
|
||||
y="3.1054616"
|
||||
id="rect48"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.7347264"
|
||||
y="3.1054616"
|
||||
id="rect50"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="3.8833277"
|
||||
y="3.4187472"
|
||||
id="text52"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 6</text>
|
||||
<line
|
||||
x1="3.7347264"
|
||||
y1="3.5856469"
|
||||
x2="4.9351892"
|
||||
y2="3.5856469"
|
||||
id="line54"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.175281"
|
||||
y="3.1054616"
|
||||
id="rect56"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.175281"
|
||||
y="3.1054616"
|
||||
id="rect58"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="5.3330317"
|
||||
y="3.4187472"
|
||||
id="text60"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 7</text>
|
||||
<line
|
||||
x1="5.175281"
|
||||
y1="3.5856469"
|
||||
x2="6.3757439"
|
||||
y2="3.5856469"
|
||||
id="line62"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<ellipse
|
||||
cx="7.1999998"
|
||||
cy="4.0110002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse64"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="7.1999998"
|
||||
cy="4.0110002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse66"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="6.1443377"
|
||||
y="2.1364057"
|
||||
id="text68"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">A</text>
|
||||
<ellipse
|
||||
cx="3.007"
|
||||
cy="4.0300002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse70"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="3.007"
|
||||
cy="4.0300002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse72"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="3.2742035"
|
||||
y="2.1540098"
|
||||
id="text74"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">B</text>
|
||||
<ellipse
|
||||
cx="4.0599999"
|
||||
cy="6.7820001"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse76"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="4.0599999"
|
||||
cy="6.7820001"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse78"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="3.976877"
|
||||
y="4.0473108"
|
||||
id="text80"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">C</text>
|
||||
<ellipse
|
||||
cx="7.8449998"
|
||||
cy="4.6550002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse82"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="7.8449998"
|
||||
cy="4.6550002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse84"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="6.5808516"
|
||||
y="2.5937216"
|
||||
id="text86"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">D</text>
|
||||
<ellipse
|
||||
cx="0.87"
|
||||
cy="4.0079999"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse88"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="0.87"
|
||||
cy="4.0079999"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse90"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="1.7991183"
|
||||
y="2.1403852"
|
||||
id="text92"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">E</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
@@ -0,0 +1,86 @@
|
||||
[/ Copyright 2006-2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[section:hash_equality Equality Predicates and Hash Functions]
|
||||
|
||||
While the associative containers use an ordering relation to specify how the
|
||||
elements are stored, the unordered associative containers use an equality
|
||||
predicate and a hash function. For example, [classref boost::unordered_map]
|
||||
is declared as:
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class ``[classref boost::unordered_map unordered_map]``;
|
||||
|
||||
The hash function comes first as you might want to change the hash function
|
||||
but not the equality predicate. For example, if you wanted to use the
|
||||
[@http://www.isthe.com/chongo/tech/comp/fnv/ FNV-1 hash] you could write:
|
||||
|
||||
[import src_code/dictionary.cpp]
|
||||
[case_sensitive_dictionary_fnv]
|
||||
|
||||
There is an [@boost:/libs/unordered/examples/fnv1.hpp implementation
|
||||
of FNV-1] in the examples directory.
|
||||
|
||||
If you wish to use a different equality function,
|
||||
you will also need to use a matching hash function. For
|
||||
example, to implement a case insensitive dictionary you need to define a
|
||||
case insensitive equality predicate and hash function:
|
||||
|
||||
[case_insensitive_functions]
|
||||
|
||||
Which you can then use in a case insensitive dictionary:
|
||||
|
||||
[case_insensitive_dictionary]
|
||||
|
||||
This is a simplified version of the example at
|
||||
[@boost:/libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp]
|
||||
which supports other locales and string types.
|
||||
|
||||
[caution
|
||||
Be careful when using the equality (`==`) operator with custom equality
|
||||
predicates, especially if you're using a function pointer. If you compare two
|
||||
containers with different equality predicates then the result is undefined.
|
||||
For most stateless function objects this is impossible - since you can only
|
||||
compare objects with the same equality predicate you know the equality
|
||||
predicates must be equal. But if you're using function pointers or a stateful
|
||||
equality predicate (e.g. boost::function) then you can get into trouble.
|
||||
]
|
||||
|
||||
[h2 Custom Types]
|
||||
|
||||
Similarly, a custom hash function can be used for custom types:
|
||||
|
||||
[import src_code/point1.cpp]
|
||||
[point_example1]
|
||||
|
||||
Since the default hash function is [link hash Boost.Hash],
|
||||
we can [link hash.custom extend it to support the type]
|
||||
so that the hash function doesn't need to be explicitly given:
|
||||
|
||||
[import src_code/point2.cpp]
|
||||
[point_example2]
|
||||
|
||||
See the [link hash.custom Boost.Hash documentation] for more detail on how to
|
||||
do this. Remember that it relies on extensions to the draft standard - so it
|
||||
won't work for other implementations of the unordered associative containers,
|
||||
you'll need to explicitly use Boost.Hash.
|
||||
|
||||
[table:access_methods Methods for accessing the hash and equality functions.
|
||||
[[Method] [Description]]
|
||||
|
||||
[
|
||||
[`hasher hash_function() const`]
|
||||
[Returns the container's hash function.]
|
||||
]
|
||||
[
|
||||
[`key_equal key_eq() const`]
|
||||
[Returns the container's key equality function.]
|
||||
]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
[/ Copyright 2006-2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table
|
||||
hash table]]
|
||||
[def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function
|
||||
hash function]]
|
||||
|
||||
[section:intro Introduction]
|
||||
|
||||
For accessing data based on key lookup, the C++ standard library offers `std::set`,
|
||||
`std::map`, `std::multiset` and `std::multimap`. These are generally
|
||||
implemented using balanced binary trees so that lookup time has
|
||||
logarithmic complexity. That is generally okay, but in many cases a
|
||||
__hash-table__ can perform better, as accessing data has constant complexity,
|
||||
on average. The worst case complexity is linear, but that occurs rarely and
|
||||
with some care, can be avoided.
|
||||
|
||||
Also, the existing containers require a 'less than' comparison object
|
||||
to order their elements. For some data types this is impossible to implement
|
||||
or isn't practical. In contrast, a hash table only needs an equality function
|
||||
and a hash function for the key.
|
||||
|
||||
With this in mind, unordered associative containers were added to the C++
|
||||
standard. This is an implementation of the containers described in C++11,
|
||||
with some [link unordered.compliance deviations from the standard] in
|
||||
order to work with non-C++11 compilers and libraries.
|
||||
|
||||
`unordered_set` and `unordered_multiset` are defined in the header
|
||||
<[headerref boost/unordered_set.hpp]>
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_set unordered_set]``;
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_multiset unordered_multiset]``;
|
||||
}
|
||||
|
||||
`unordered_map` and `unordered_multimap` are defined in the header
|
||||
<[headerref boost/unordered_map.hpp]>
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_map unordered_map]``;
|
||||
|
||||
template<
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_multimap unordered_multimap]``;
|
||||
}
|
||||
|
||||
When using Boost.TR1, these classes are included from `<unordered_set>` and
|
||||
`<unordered_map>`, with the classes added to the `std::tr1` namespace.
|
||||
|
||||
The containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
[import src_code/intro.cpp]
|
||||
[intro_example1_2]
|
||||
|
||||
But since the elements aren't ordered, the output of:
|
||||
|
||||
[intro_example1_3]
|
||||
|
||||
can be in any order. For example, it might be:
|
||||
|
||||
two,2
|
||||
one,1
|
||||
three,3
|
||||
|
||||
To store an object in an unordered associative container requires both an
|
||||
key equality function and a hash function. The default function objects in
|
||||
the standard containers support a few basic types including integer types,
|
||||
floating point types, pointer types, and the standard strings. Since
|
||||
Boost.Unordered uses [classref boost::hash] it also supports some other types,
|
||||
including standard containers. To use any types not supported by these methods
|
||||
you have to [link hash.custom extend Boost.Hash to support the type] or use
|
||||
your own custom equality predicates and hash functions. See the
|
||||
[link unordered.hash_equality Equality Predicates and Hash Functions] section
|
||||
for more details.
|
||||
|
||||
There are other differences, which are listed in the
|
||||
[link unordered.comparison Comparison with Associative Containers] section.
|
||||
|
||||
[endsect]
|
||||
@@ -0,0 +1,106 @@
|
||||
[/ Copyright 2006-2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[def __wang__
|
||||
[@http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
Thomas Wang's article on integer hash functions]]
|
||||
|
||||
[section:rationale Implementation Rationale]
|
||||
|
||||
The intent of this library is to implement the unordered
|
||||
containers in the draft standard, so the interface was fixed. But there are
|
||||
still some implementation decisions to make. The priorities are
|
||||
conformance to the standard and portability.
|
||||
|
||||
The [@http://en.wikipedia.org/wiki/Hash_table wikipedia article on hash tables]
|
||||
has a good summary of the implementation issues for hash tables in general.
|
||||
|
||||
[h2 Data Structure]
|
||||
|
||||
By specifying an interface for accessing the buckets of the container the
|
||||
standard pretty much requires that the hash table uses chained addressing.
|
||||
|
||||
It would be conceivable to write a hash table that uses another method. For
|
||||
example, it could use open addressing, and use the lookup chain to act as a
|
||||
bucket but there are a some serious problems with this:
|
||||
|
||||
* The draft standard requires that pointers to elements aren't invalidated, so
|
||||
the elements can't be stored in one array, but will need a layer of
|
||||
indirection instead - losing the efficiency and most of the memory gain,
|
||||
the main advantages of open addressing.
|
||||
|
||||
* Local iterators would be very inefficient and may not be able to
|
||||
meet the complexity requirements.
|
||||
|
||||
* There are also the restrictions on when iterators can be invalidated. Since
|
||||
open addressing degrades badly when there are a high number of collisions the
|
||||
restrictions could prevent a rehash when it's really needed. The maximum load
|
||||
factor could be set to a fairly low value to work around this - but the
|
||||
standard requires that it is initially set to 1.0.
|
||||
|
||||
* And since the standard is written with a eye towards chained
|
||||
addressing, users will be surprised if the performance doesn't reflect that.
|
||||
|
||||
So chained addressing is used.
|
||||
|
||||
[/ (Removing for now as this is out of date)
|
||||
|
||||
For containers with unique keys I store the buckets in a single-linked list.
|
||||
There are other possible data structures (such as a double-linked list)
|
||||
that allow for some operations to be faster (such as erasing and iteration)
|
||||
but the possible gain seems small compared to the extra memory needed.
|
||||
The most commonly used operations (insertion and lookup) would not be improved
|
||||
at all.
|
||||
|
||||
But for containers with equivalent keys a single-linked list can degrade badly
|
||||
when a large number of elements with equivalent keys are inserted. I think it's
|
||||
reasonable to assume that users who choose to use `unordered_multiset` or
|
||||
`unordered_multimap` do so because they are likely to insert elements with
|
||||
equivalent keys. So I have used an alternative data structure that doesn't
|
||||
degrade, at the expense of an extra pointer per node.
|
||||
|
||||
This works by adding storing a circular linked list for each group of equivalent
|
||||
nodes in reverse order. This allows quick navigation to the end of a group (since
|
||||
the first element points to the last) and can be quickly updated when elements
|
||||
are inserted or erased. The main disadvantage of this approach is some hairy code
|
||||
for erasing elements.
|
||||
]
|
||||
|
||||
[/ (Starting to write up new structure, might not be ready in time)
|
||||
The node used to be stored in a linked list for each bucket but that
|
||||
didn't meet the complexity requirements for C++11, so now the nodes
|
||||
are stored in one long single linked list. But there needs a way to get
|
||||
the bucket from the node, to do that a copy of the key's hash value is
|
||||
stored in the node. Another possibility would be to store a pointer to
|
||||
the bucket, or the bucket's index, but storing the hash value allows
|
||||
some operations to be faster.
|
||||
]
|
||||
|
||||
[h2 Number of Buckets]
|
||||
|
||||
There are two popular methods for choosing the number of buckets in a hash
|
||||
table. One is to have a prime number of buckets, another is to use a power
|
||||
of 2.
|
||||
|
||||
Using a prime number of buckets, and choosing a bucket by using the modulus
|
||||
of the hash function's result will usually give a good result. The downside
|
||||
is that the required modulus operation is fairly expensive.
|
||||
|
||||
Using a power of 2 allows for much quicker selection of the bucket
|
||||
to use, but at the expense of loosing the upper bits of the hash value.
|
||||
For some specially designed hash functions it is possible to do this and
|
||||
still get a good result but as the containers can take arbitrary hash
|
||||
functions this can't be relied on.
|
||||
|
||||
To avoid this a transformation could be applied to the hash function, for an
|
||||
example see __wang__. Unfortunately, a transformation like Wang's requires
|
||||
knowledge of the number of bits in the hash value, so it isn't portable enough.
|
||||
This leaves more expensive methods, such as Knuth's Multiplicative Method
|
||||
(mentioned in Wang's article). These don't tend to work as well as taking the
|
||||
modulus of a prime, and the extra computation required might negate
|
||||
efficiency advantage of power of 2 hash tables.
|
||||
|
||||
So, this implementation uses a prime number for the hash table size.
|
||||
|
||||
[endsect]
|
||||
+1231
File diff suppressed because it is too large
Load Diff
+4259
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
|
||||
// Copyright 2006-2007 Daniel James.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include "../../examples/fnv1.hpp"
|
||||
|
||||
//[case_insensitive_functions
|
||||
struct iequal_to
|
||||
: std::binary_function<std::string, std::string, bool>
|
||||
{
|
||||
bool operator()(std::string const& x,
|
||||
std::string const& y) const
|
||||
{
|
||||
return boost::algorithm::iequals(x, y, std::locale());
|
||||
}
|
||||
};
|
||||
|
||||
struct ihash
|
||||
: std::unary_function<std::string, std::size_t>
|
||||
{
|
||||
std::size_t operator()(std::string const& x) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(std::string::const_iterator it = x.begin();
|
||||
it != x.end(); ++it)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(*it, locale));
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
int main() {
|
||||
//[case_sensitive_dictionary_fnv
|
||||
boost::unordered_map<std::string, int, hash::fnv_1>
|
||||
dictionary;
|
||||
//]
|
||||
|
||||
BOOST_TEST(dictionary.empty());
|
||||
|
||||
dictionary["one"] = 1;
|
||||
BOOST_TEST(dictionary.size() == 1);
|
||||
BOOST_TEST(dictionary.find("ONE") == dictionary.end());
|
||||
|
||||
dictionary.insert(std::make_pair("ONE", 2));
|
||||
BOOST_TEST(dictionary.size() == 2);
|
||||
BOOST_TEST(dictionary.find("ONE") != dictionary.end() &&
|
||||
dictionary.find("ONE")->first == "ONE" &&
|
||||
dictionary.find("ONE")->second == 2);
|
||||
|
||||
dictionary["One"] = 3;
|
||||
BOOST_TEST(dictionary.size() == 3);
|
||||
BOOST_TEST(dictionary.find("One") != dictionary.end() &&
|
||||
dictionary.find("One")->first == "One" &&
|
||||
dictionary.find("One")->second == 3);
|
||||
|
||||
dictionary["two"] = 4;
|
||||
BOOST_TEST(dictionary.size() == 4);
|
||||
BOOST_TEST(dictionary.find("Two") == dictionary.end() &&
|
||||
dictionary.find("two") != dictionary.end() &&
|
||||
dictionary.find("two")->second == 4);
|
||||
|
||||
|
||||
//[case_insensitive_dictionary
|
||||
boost::unordered_map<std::string, int, ihash, iequal_to>
|
||||
idictionary;
|
||||
//]
|
||||
|
||||
BOOST_TEST(idictionary.empty());
|
||||
|
||||
idictionary["one"] = 1;
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE") == idictionary.find("one"));
|
||||
|
||||
idictionary.insert(std::make_pair("ONE", 2));
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE")->first == "one" &&
|
||||
idictionary.find("ONE")->second == 1);
|
||||
|
||||
idictionary["One"] = 3;
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE")->first == "one" &&
|
||||
idictionary.find("ONE")->second == 3);
|
||||
|
||||
idictionary["two"] = 4;
|
||||
BOOST_TEST(idictionary.size() == 2);
|
||||
BOOST_TEST(idictionary.find("two") != idictionary.end() &&
|
||||
idictionary.find("TWO")->first == "two" &&
|
||||
idictionary.find("Two")->second == 4);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// 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)
|
||||
|
||||
//[intro_example1_1
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
//]
|
||||
|
||||
int main() {
|
||||
//[intro_example1_2
|
||||
typedef boost::unordered_map<std::string, int> map;
|
||||
map x;
|
||||
x["one"] = 1;
|
||||
x["two"] = 2;
|
||||
x["three"] = 3;
|
||||
|
||||
assert(x.at("one") == 1);
|
||||
assert(x.find("missing") == x.end());
|
||||
//]
|
||||
|
||||
//[intro_example1_3
|
||||
BOOST_FOREACH(map::value_type i, x) {
|
||||
std::cout<<i.first<<","<<i.second<<"\n";
|
||||
}
|
||||
//]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
|
||||
//[point_example1
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
struct point_hash
|
||||
: std::unary_function<point, std::size_t>
|
||||
{
|
||||
std::size_t operator()(point const& p) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
boost::unordered_multiset<point, point_hash> points;
|
||||
//]
|
||||
|
||||
int main() {
|
||||
point x[] = {{1,2}, {3,4}, {1,5}, {1,2}};
|
||||
for(int i = 0; i < sizeof(x) / sizeof(point); ++i)
|
||||
points.insert(x[i]);
|
||||
BOOST_TEST(points.count(x[0]) == 2);
|
||||
BOOST_TEST(points.count(x[1]) == 1);
|
||||
point y = {10, 2};
|
||||
BOOST_TEST(points.count(y) == 0);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
|
||||
//[point_example2
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
std::size_t hash_value(point const& p) {
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Now the default function objects work.
|
||||
boost::unordered_multiset<point> points;
|
||||
//]
|
||||
|
||||
int main() {
|
||||
point x[] = {{1,2}, {3,4}, {1,5}, {1,2}};
|
||||
for(int i = 0; i < sizeof(x) / sizeof(point); ++i)
|
||||
points.insert(x[i]);
|
||||
BOOST_TEST(points.count(x[0]) == 2);
|
||||
BOOST_TEST(points.count(x[1]) == 1);
|
||||
point y = {10, 2};
|
||||
BOOST_TEST(points.count(y) == 0);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
= Boost.Unordered
|
||||
:toc: left
|
||||
:toclevels: 3
|
||||
:idprefix:
|
||||
:docinfo: private-footer
|
||||
:source-highlighter: rouge
|
||||
:source-language: c++
|
||||
:nofooter:
|
||||
:sectlinks:
|
||||
|
||||
:leveloffset: +1
|
||||
|
||||
include::unordered/intro.adoc[]
|
||||
include::unordered/buckets.adoc[]
|
||||
include::unordered/hash_equality.adoc[]
|
||||
include::unordered/comparison.adoc[]
|
||||
include::unordered/compliance.adoc[]
|
||||
include::unordered/rationale.adoc[]
|
||||
include::unordered/ref.adoc[]
|
||||
include::unordered/changes.adoc[]
|
||||
include::unordered/bibliography.adoc[]
|
||||
include::unordered/copyright.adoc[]
|
||||
@@ -0,0 +1,38 @@
|
||||
[/ Copyright 2006-2008 Daniel James.
|
||||
/ 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) ]
|
||||
|
||||
[library Boost.Unordered
|
||||
[quickbook 1.5]
|
||||
[authors [James, Daniel]]
|
||||
[copyright 2003 2004 Jeremy B. Maitin-Shepard]
|
||||
[copyright 2005 2006 2007 2008 Daniel James]
|
||||
[purpose std::tr1 compliant hash containers]
|
||||
[id unordered]
|
||||
[dirname unordered]
|
||||
[license
|
||||
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])
|
||||
]
|
||||
]
|
||||
|
||||
[template diagram[name] '''<inlinemediaobject>
|
||||
<imageobject role="html">
|
||||
<imagedata align = "center" fileref="../../libs/unordered/doc/diagrams/'''[name]'''.png"></imagedata>
|
||||
</imageobject>
|
||||
<imageobject role="print">
|
||||
<imagedata align = "center" fileref="../../libs/unordered/doc/diagrams/'''[name]'''.svg"></imagedata>
|
||||
</imageobject>
|
||||
</inlinemediaobject>''']
|
||||
|
||||
|
||||
[include:unordered intro.qbk]
|
||||
[include:unordered buckets.qbk]
|
||||
[include:unordered hash_equality.qbk]
|
||||
[include:unordered comparison.qbk]
|
||||
[include:unordered compliance.qbk]
|
||||
[include:unordered rationale.qbk]
|
||||
[include:unordered changes.qbk]
|
||||
[xinclude ref.xml]
|
||||
[xinclude bibliography.xml]
|
||||
@@ -1,10 +0,0 @@
|
||||
[#bibliography]
|
||||
|
||||
:idprefix: bibliography_
|
||||
|
||||
= Bibliography
|
||||
|
||||
* _C/C++ Users Journal_. February, 2006. Pete Becker. http://www.ddj.com/cpp/184402066[STL and TR1: Part III - Unordered containers^]. +
|
||||
An introducation to the standard unordered containers.
|
||||
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
[#buckets]
|
||||
:idprefix: buckets_
|
||||
|
||||
= The Data Structure
|
||||
|
||||
The containers are made up of a number of 'buckets', each of which can contain
|
||||
any number of elements. For example, the following diagram shows an <<unordered_set,unordered_set>> with 7 buckets containing 5 elements, `A`,
|
||||
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
|
||||
have more buckets).
|
||||
|
||||
image::../diagrams/buckets.png[]
|
||||
|
||||
In order to decide which bucket to place an element in, the container applies
|
||||
the hash function, `Hash`, to the element's key (for `unordered_set` and
|
||||
`unordered_multiset` the key is the whole element, but is referred to as the key
|
||||
so that the same terminology can be used for sets and maps). This returns a
|
||||
value of type `std::size_t`. `std::size_t` has a much greater range of values
|
||||
then the number of buckets, so the container applies another transformation to
|
||||
that value to choose a bucket to place the element in.
|
||||
|
||||
Retrieving the elements for a given key is simple. The same process is applied
|
||||
to the key to find the correct bucket. Then the key is compared with the
|
||||
elements in the bucket to find any elements that match (using the equality
|
||||
predicate `Pred`). If the hash function has worked well the elements will be
|
||||
evenly distributed amongst the buckets so only a small number of elements will
|
||||
need to be examined.
|
||||
|
||||
There is <<hash_equality, more information on hash functions and
|
||||
equality predicates in the next section>>.
|
||||
|
||||
You can see in the diagram that `A` & `D` have been placed in the same bucket.
|
||||
When looking for elements in this bucket up to 2 comparisons are made, making
|
||||
the search slower. This is known as a collision. To keep things fast we try to
|
||||
keep collisions to a minimum.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Methods for Accessing Buckets']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`size_type bucket_count() const`
|
||||
|The number of buckets.
|
||||
|
||||
|`size_type max_bucket_count() const`
|
||||
|An upper bound on the number of buckets.
|
||||
|
||||
|`size_type bucket_size(size_type n) const`
|
||||
|The number of elements in bucket `n`.
|
||||
|
||||
|`size_type bucket(key_type const& k) const`
|
||||
|Returns the index of the bucket which would contain `k`.
|
||||
|
||||
|`local_iterator begin(size_type n)`
|
||||
1.6+|Return begin and end iterators for bucket `n`.
|
||||
|
||||
|`local_iterator end(size_type n)`
|
||||
|
||||
|`const_local_iterator begin(size_type n) const`
|
||||
|
||||
|`const_local_iterator end(size_type n) const`
|
||||
|
||||
|`const_local_iterator cbegin(size_type n) const`
|
||||
|
||||
|`const_local_iterator cend(size_type n) const`
|
||||
|
||||
|===
|
||||
|
||||
== Controlling the number of buckets
|
||||
|
||||
As more elements are added to an unordered associative container, the number
|
||||
of elements in the buckets will increase causing performance to degrade.
|
||||
To combat this the containers increase the bucket count as elements are inserted.
|
||||
You can also tell the container to change the bucket count (if required) by
|
||||
calling `rehash`.
|
||||
|
||||
The standard leaves a lot of freedom to the implementer to decide how the
|
||||
number of buckets is chosen, but it does make some requirements based on the
|
||||
container's 'load factor', the average number of elements per bucket.
|
||||
Containers also have a 'maximum load factor' which they should try to keep the
|
||||
load factor below.
|
||||
|
||||
You can't control the bucket count directly but there are two ways to
|
||||
influence it:
|
||||
|
||||
* Specify the minimum number of buckets when constructing a container or when calling `rehash`.
|
||||
* Suggest a maximum load factor by calling `max_load_factor`.
|
||||
|
||||
`max_load_factor` doesn't let you set the maximum load factor yourself, it just
|
||||
lets you give a _hint_. And even then, the standard doesn't actually
|
||||
require the container to pay much attention to this value. The only time the
|
||||
load factor is _required_ to be less than the maximum is following a call to
|
||||
`rehash`. But most implementations will try to keep the number of elements
|
||||
below the max load factor, and set the maximum load factor to be the same as
|
||||
or close to the hint - unless your hint is unreasonably small or large.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Methods for Controlling Bucket Size']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`X(size_type n)`
|
||||
|Construct an empty container with at least `n` buckets (`X` is the container type).
|
||||
|
||||
|`X(InputIterator i, InputIterator j, size_type n)`
|
||||
|Construct an empty container with at least `n` buckets and insert elements from the range `[i, j)` (`X` is the container type).
|
||||
|
||||
|`float load_factor() const`
|
||||
|The average number of elements per bucket.
|
||||
|
||||
|`float max_load_factor() const`
|
||||
|Returns the current maximum load factor.
|
||||
|
||||
|`float max_load_factor(float z)`
|
||||
|Changes the container's maximum load factor, using `z` as a hint.
|
||||
|
||||
|`void rehash(size_type n)`
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
||||
|===
|
||||
|
||||
== Iterator Invalidation
|
||||
|
||||
It is not specified how member functions other than `rehash` and `reserve` affect
|
||||
the bucket count, although `insert` is only allowed to invalidate iterators
|
||||
when the insertion causes the load factor to be greater than or equal to the
|
||||
maximum load factor. For most implementations this means that `insert` will only
|
||||
change the number of buckets when this happens. While iterators can be
|
||||
invalidated by calls to `insert`, `rehash` and `reserve`, pointers and references to the
|
||||
container's elements are never invalidated.
|
||||
|
||||
In a similar manner to using `reserve` for ``vector``s, it can be a good idea
|
||||
to call `reserve` before inserting a large number of elements. This will get
|
||||
the expensive rehashing out of the way and let you store iterators, safe in
|
||||
the knowledge that they won't be invalidated. If you are inserting `n`
|
||||
elements into container `x`, you could first call:
|
||||
|
||||
```
|
||||
x.reserve(n);
|
||||
```
|
||||
|
||||
Note:: `reserve(n)` reserves space for at least `n` elements, allocating enough buckets
|
||||
so as to not exceed the maximum load factor.
|
||||
+
|
||||
Because the maximum load factor is defined as the number of elements divided by the total
|
||||
number of available buckets, this function is logically equivalent to:
|
||||
+
|
||||
```
|
||||
x.rehash(std::ceil(n / x.max_load_factor()))
|
||||
```
|
||||
+
|
||||
See the <<unordered_map_rehash,reference for more details>> on the `rehash` function.
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
[#changes]
|
||||
= Change Log
|
||||
|
||||
:idprefix: changes_
|
||||
:svn-ticket-url: https://svn.boost.org/trac/boost/ticket
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.79.0
|
||||
|
||||
* Improved {cpp}20 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `count`, `equal_range` and `find`.
|
||||
** All containers now implement the member function `contains`.
|
||||
** `erase_if` has been implemented for all containers.
|
||||
* Improved {cpp}23 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `erase` and `extract`.
|
||||
* Changed behavior of `reserve` to eagerly
|
||||
allocate ({github-pr-url}/59[PR#59^]).
|
||||
* Various warning fixes in the test suite.
|
||||
* Update code to internally use `boost::allocator_traits`.
|
||||
* Switch to Fibonacci hashing.
|
||||
* Update documentation to be written in AsciiDoc instead of QuickBook.
|
||||
|
||||
== Release 1.67.0
|
||||
|
||||
* Improved {cpp}17 support:
|
||||
** Add template deduction guides from the standard.
|
||||
** Use a simple implementation of `optional` in node handles, so
|
||||
that they're closer to the standard.
|
||||
** Add missing `noexcept` specifications to `swap`, `operator=`
|
||||
and node handles, and change the implementation to match.
|
||||
Using `std::allocator_traits::is_always_equal`, or our own
|
||||
implementation when not available, and
|
||||
`boost::is_nothrow_swappable` in the implementation.
|
||||
* Improved {cpp}20 support:
|
||||
** Use `boost::to_address`, which has the proposed {cpp}20 semantics,
|
||||
rather than the old custom implementation.
|
||||
* Add `element_type` to iterators, so that `std::pointer_traits`
|
||||
will work.
|
||||
* Use `std::piecewise_construct` on recent versions of Visual {cpp},
|
||||
and other uses of the Dinkumware standard library,
|
||||
now using Boost.Predef to check compiler and library versions.
|
||||
* Use `std::iterator_traits` rather than the boost iterator traits
|
||||
in order to remove dependency on Boost.Iterator.
|
||||
* Remove iterators' inheritance from `std::iterator`, which is
|
||||
deprecated in {cpp}17, thanks to Daniela Engert
|
||||
({github-pr-url}/7[PR#7^]).
|
||||
* Stop using `BOOST_DEDUCED_TYPENAME`.
|
||||
* Update some Boost include paths.
|
||||
* Rename some internal methods, and variables.
|
||||
* Various testing improvements.
|
||||
* Miscellaneous internal changes.
|
||||
|
||||
== Release 1.66.0
|
||||
|
||||
* Simpler move construction implementation.
|
||||
* Documentation fixes ({github-pr-url}/6[GitHub #6^]).
|
||||
|
||||
== Release 1.65.0
|
||||
|
||||
* Add deprecated attributes to `quick_erase` and `erase_return_void`.
|
||||
I really will remove them in a future version this time.
|
||||
* Small standards compliance fixes:
|
||||
** `noexpect` specs for `swap` free functions.
|
||||
** Add missing `insert(P&&)` methods.
|
||||
|
||||
== Release 1.64.0
|
||||
|
||||
* Initial support for new {cpp}17 member functions:
|
||||
`insert_or_assign` and `try_emplace` in `unordered_map`,
|
||||
* Initial support for `merge` and `extract`.
|
||||
Does not include transferring nodes between
|
||||
`unordered_map` and `unordered_multimap` or between `unordered_set` and
|
||||
`unordered_multiset` yet. That will hopefully be in the next version of
|
||||
Boost.
|
||||
|
||||
== Release 1.63.0
|
||||
|
||||
* Check hint iterator in `insert`/`emplace_hint`.
|
||||
* Fix some warnings, mostly in the tests.
|
||||
* Manually write out `emplace_args` for small numbers of arguments -
|
||||
should make template error messages a little more bearable.
|
||||
* Remove superfluous use of `boost::forward` in emplace arguments,
|
||||
which fixes emplacing string literals in old versions of Visual {cpp}.
|
||||
* Fix an exception safety issue in assignment. If bucket allocation
|
||||
throws an exception, it can overwrite the hash and equality functions while
|
||||
leaving the existing elements in place. This would mean that the function
|
||||
objects wouldn't match the container elements, so elements might be in the
|
||||
wrong bucket and equivalent elements would be incorrectly handled.
|
||||
* Various reference documentation improvements.
|
||||
* Better allocator support ({svn-ticket-url}/12459[#12459^]).
|
||||
* Make the no argument constructors implicit.
|
||||
* Implement missing allocator aware constructors.
|
||||
* Fix assigning the hash/key equality functions for empty containers.
|
||||
* Remove unary/binary_function from the examples in the documentation.
|
||||
They are removed in {cpp}17.
|
||||
* Support 10 constructor arguments in emplace. It was meant to support up to 10
|
||||
arguments, but an off by one error in the preprocessor code meant it only
|
||||
supported up to 9.
|
||||
|
||||
== Release 1.62.0
|
||||
|
||||
* Remove use of deprecated `boost::iterator`.
|
||||
* Remove `BOOST_NO_STD_DISTANCE` workaround.
|
||||
* Remove `BOOST_UNORDERED_DEPRECATED_EQUALITY` warning.
|
||||
* Simpler implementation of assignment, fixes an exception safety issue
|
||||
for `unordered_multiset` and `unordered_multimap`. Might be a little slower.
|
||||
* Stop using return value SFINAE which some older compilers have issues
|
||||
with.
|
||||
|
||||
== Release 1.58.0
|
||||
|
||||
* Remove unnecessary template parameter from const iterators.
|
||||
* Rename private `iterator` typedef in some iterator classes, as it
|
||||
confuses some traits classes.
|
||||
* Fix move assignment with stateful, propagate_on_container_move_assign
|
||||
allocators ({svn-ticket-url}/10777[#10777^]).
|
||||
* Fix rare exception safety issue in move assignment.
|
||||
* Fix potential overflow when calculating number of buckets to allocate
|
||||
({github-pr-url}/4[GitHub #4^]).
|
||||
|
||||
== Release 1.57.0
|
||||
|
||||
* Fix the `pointer` typedef in iterators ({svn-ticket-url}/10672[#10672^]).
|
||||
* Fix Coverity warning
|
||||
({github-pr-url}/2[GitHub #2^]).
|
||||
|
||||
== Release 1.56.0
|
||||
|
||||
* Fix some shadowed variable warnings ({svn-ticket-url}/9377[#9377^]).
|
||||
* Fix allocator use in documentation ({svn-ticket-url}/9719[#9719^]).
|
||||
* Always use prime number of buckets for integers. Fixes performance
|
||||
regression when inserting consecutive integers, although makes other
|
||||
uses slower ({svn-ticket-url}/9282[#9282^]).
|
||||
* Only construct elements using allocators, as specified in {cpp}11 standard.
|
||||
|
||||
== Release 1.55.0
|
||||
|
||||
* Avoid some warnings ({svn-ticket-url}/8851[#8851^], {svn-ticket-url}/8874[#8874^]).
|
||||
* Avoid exposing some detail functions via. ADL on the iterators.
|
||||
* Follow the standard by only using the allocators' construct and destroy
|
||||
methods to construct and destroy stored elements. Don't use them for internal
|
||||
data like pointers.
|
||||
|
||||
== Release 1.54.0
|
||||
|
||||
* Mark methods specified in standard as `noexpect`. More to come in the next
|
||||
release.
|
||||
* If the hash function and equality predicate are known to both have nothrow
|
||||
move assignment or construction then use them.
|
||||
|
||||
== Release 1.53.0
|
||||
|
||||
* Remove support for the old pre-standard variadic pair constructors, and
|
||||
equality implementation. Both have been deprecated since Boost 1.48.
|
||||
* Remove use of deprecated config macros.
|
||||
* More internal implementation changes, including a much simpler
|
||||
implementation of `erase`.
|
||||
|
||||
== Release 1.52.0
|
||||
|
||||
* Faster assign, which assigns to existing nodes where possible, rather than
|
||||
creating entirely new nodes and copy constructing.
|
||||
* Fixed bug in `erase_range` ({svn-ticket-url}/7471[#7471^]).
|
||||
* Reverted some of the internal changes to how nodes are created, especially
|
||||
for {cpp}11 compilers. 'construct' and 'destroy' should work a little better
|
||||
for {cpp}11 allocators.
|
||||
* Simplified the implementation a bit. Hopefully more robust.
|
||||
|
||||
== Release 1.51.0
|
||||
|
||||
* Fix construction/destruction issue when using a {cpp}11 compiler with a
|
||||
{cpp}03 allocator ({svn-ticket-url}/7100[#7100^]).
|
||||
* Remove a `try..catch` to support compiling without exceptions.
|
||||
* Adjust SFINAE use to try to support g++ 3.4 ({svn-ticket-url}/7175[#7175^]).
|
||||
* Updated to use the new config macros.
|
||||
|
||||
== Release 1.50.0
|
||||
|
||||
* Fix equality for `unordered_multiset` and `unordered_multimap`.
|
||||
* {svn-ticket-url}/6857[Ticket 6857^]:
|
||||
Implement `reserve`.
|
||||
* {svn-ticket-url}/6771[Ticket 6771^]:
|
||||
Avoid gcc's `-Wfloat-equal` warning.
|
||||
* {svn-ticket-url}/6784[Ticket 6784^]:
|
||||
Fix some Sun specific code.
|
||||
* {svn-ticket-url}/6190[Ticket 6190^]:
|
||||
Avoid gcc's `-Wshadow` warning.
|
||||
* {svn-ticket-url}/6905[Ticket 6905^]:
|
||||
Make namespaces in macros compatible with `bcp` custom namespaces.
|
||||
Fixed by Luke Elliott.
|
||||
* Remove some of the smaller prime number of buckets, as they may make
|
||||
collisions quite probable (e.g. multiples of 5 are very common because
|
||||
we used base 10).
|
||||
* On old versions of Visual {cpp}, use the container library's implementation
|
||||
of `allocator_traits`, as it's more likely to work.
|
||||
* On machines with 64 bit std::size_t, use power of 2 buckets, with Thomas
|
||||
Wang's hash function to pick which one to use. As modulus is very slow
|
||||
for 64 bit values.
|
||||
* Some internal changes.
|
||||
|
||||
== Release 1.49.0
|
||||
|
||||
* Fix warning due to accidental odd assignment.
|
||||
* Slightly better error messages.
|
||||
|
||||
== Release 1.48.0 - Major update
|
||||
|
||||
This is major change which has been converted to use Boost.Move's move
|
||||
emulation, and be more compliant with the {cpp}11 standard. See the
|
||||
xref:unordered.adoc#compliance[compliance section] for details.
|
||||
|
||||
The container now meets {cpp}11's complexity requirements, but to do so
|
||||
uses a little more memory. This means that `quick_erase` and
|
||||
`erase_return_void` are no longer required, they'll be removed in a
|
||||
future version.
|
||||
|
||||
{cpp}11 support has resulted in some breaking changes:
|
||||
|
||||
* Equality comparison has been changed to the {cpp}11 specification.
|
||||
In a container with equivalent keys, elements in a group with equal
|
||||
keys used to have to be in the same order to be considered equal,
|
||||
now they can be a permutation of each other. To use the old
|
||||
behavior define the macro `BOOST_UNORDERED_DEPRECATED_EQUALITY`.
|
||||
|
||||
* The behaviour of swap is different when the two containers to be
|
||||
swapped has unequal allocators. It used to allocate new nodes using
|
||||
the appropriate allocators, it now swaps the allocators if
|
||||
the allocator has a member structure `propagate_on_container_swap`,
|
||||
such that `propagate_on_container_swap::value` is true.
|
||||
|
||||
* Allocator's `construct` and `destroy` functions are called with raw
|
||||
pointers, rather than the allocator's `pointer` type.
|
||||
|
||||
* `emplace` used to emulate the variadic pair constructors that
|
||||
appeared in early {cpp}0x drafts. Since they were removed it no
|
||||
longer does so. It does emulate the new `piecewise_construct`
|
||||
pair constructors - only you need to use
|
||||
`boost::piecewise_construct`. To use the old emulation of
|
||||
the variadic constructors define
|
||||
`BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT`.
|
||||
|
||||
== Release 1.45.0
|
||||
|
||||
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
|
||||
iterators which returns `value_type` by copy.
|
||||
|
||||
== Release 1.43.0
|
||||
|
||||
* {svn-ticket-url}/3966[Ticket 3966^]:
|
||||
`erase_return_void` is now `quick_erase`, which is the
|
||||
http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579[
|
||||
current forerunner for resolving the slow erase by iterator^], although
|
||||
there's a strong possibility that this may change in the future. The old
|
||||
method name remains for backwards compatibility but is considered deprecated
|
||||
and will be removed in a future release.
|
||||
* Use Boost.Exception.
|
||||
* Stop using deprecated `BOOST_HAS_*` macros.
|
||||
|
||||
== Release 1.42.0
|
||||
|
||||
* Support instantiating the containers with incomplete value types.
|
||||
* Reduced the number of warnings (mostly in tests).
|
||||
* Improved codegear compatibility.
|
||||
* {svn-ticket-url}/3693[Ticket 3693^]:
|
||||
Add `erase_return_void` as a temporary workaround for the current
|
||||
`erase` which can be inefficient because it has to find the next
|
||||
element to return an iterator.
|
||||
* Add templated find overload for compatible keys.
|
||||
* {svn-ticket-url}/3773[Ticket 3773^]:
|
||||
Add missing `std` qualifier to `ptrdiff_t`.
|
||||
* Some code formatting changes to fit almost all lines into 80 characters.
|
||||
|
||||
== Release 1.41.0 - Major update
|
||||
|
||||
* The original version made heavy use of macros to sidestep some of the older
|
||||
compilers' poor template support. But since I no longer support those
|
||||
compilers and the macro use was starting to become a maintenance burden it
|
||||
has been rewritten to use templates instead of macros for the implementation
|
||||
classes.
|
||||
|
||||
* The container object is now smaller thanks to using `boost::compressed_pair`
|
||||
for EBO and a slightly different function buffer - now using a bool instead
|
||||
of a member pointer.
|
||||
|
||||
* Buckets are allocated lazily which means that constructing an empty container
|
||||
will not allocate any memory.
|
||||
|
||||
== Release 1.40.0
|
||||
|
||||
* {svn-ticket-url}/2975[Ticket 2975^]:
|
||||
Store the prime list as a preprocessor sequence - so that it will always get
|
||||
the length right if it changes again in the future.
|
||||
* {svn-ticket-url}/1978[Ticket 1978^]:
|
||||
Implement `emplace` for all compilers.
|
||||
* {svn-ticket-url}/2908[Ticket 2908^],
|
||||
{svn-ticket-url}/3096[Ticket 3096^]:
|
||||
Some workarounds for old versions of borland, including adding explicit
|
||||
destructors to all containers.
|
||||
* {svn-ticket-url}/3082[Ticket 3082^]:
|
||||
Disable incorrect Visual {cpp} warnings.
|
||||
* Better configuration for {cpp}0x features when the headers aren't available.
|
||||
* Create less buckets by default.
|
||||
|
||||
== Release 1.39.0
|
||||
|
||||
* {svn-ticket-url}/2756[Ticket 2756^]: Avoid a warning
|
||||
on Visual {cpp} 2009.
|
||||
* Some other minor internal changes to the implementation, tests and
|
||||
documentation.
|
||||
* Avoid an unnecessary copy in `operator[]`.
|
||||
* {svn-ticket-url}/2975[Ticket 2975^]: Fix length of
|
||||
prime number list.
|
||||
|
||||
== Release 1.38.0
|
||||
|
||||
* Use link:../../../core/swap.html[`boost::swap`^].
|
||||
* {svn-ticket-url}/2237[Ticket 2237^]:
|
||||
Document that the equality and inequality operators are undefined for two
|
||||
objects if their equality predicates aren't equivalent. Thanks to Daniel
|
||||
Krügler.
|
||||
* {svn-ticket-url}/1710[Ticket 1710^]:
|
||||
Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé
|
||||
Brönnimann.
|
||||
* Use
|
||||
link:../../../type_traits/index.html[aligned storage^] to store the types.
|
||||
This changes the way the allocator is used to construct nodes. It used to
|
||||
construct the node with two calls to the allocator's `construct`
|
||||
method - once for the pointers and once for the value. It now constructs
|
||||
the node with a single call to construct and then constructs the value using
|
||||
in place construction.
|
||||
* Add support for {cpp}0x initializer lists where they're available (currently
|
||||
only g++ 4.4 in {cpp}0x mode).
|
||||
|
||||
== Release 1.37.0
|
||||
|
||||
* Rename overload of `emplace` with hint, to `emplace_hint` as specified in
|
||||
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf[n2691^].
|
||||
* Provide forwarding headers at `<boost/unordered/unordered_map_fwd.hpp>` and
|
||||
`<boost/unordered/unordered_set_fwd.hpp>`.
|
||||
* Move all the implementation inside `boost/unordered`, to assist
|
||||
modularization and hopefully make it easier to track Release subversion.
|
||||
|
||||
== Release 1.36.0
|
||||
|
||||
First official release.
|
||||
|
||||
* Rearrange the internals.
|
||||
* Move semantics - full support when rvalue references are available, emulated
|
||||
using a cut down version of the Adobe move library when they are not.
|
||||
* Emplace support when rvalue references and variadic template are available.
|
||||
* More efficient node allocation when rvalue references and variadic template
|
||||
are available.
|
||||
* Added equality operators.
|
||||
|
||||
== Boost 1.35.0 Add-on - 31st March 2008
|
||||
|
||||
Unofficial release uploaded to vault, to be used with Boost 1.35.0. Incorporated
|
||||
many of the suggestions from the review.
|
||||
|
||||
* Improved portability thanks to Boost regression testing.
|
||||
* Fix lots of typos, and clearer text in the documentation.
|
||||
* Fix floating point to `std::size_t` conversion when calculating sizes from
|
||||
the max load factor, and use `double` in the calculation for greater accuracy.
|
||||
* Fix some errors in the examples.
|
||||
|
||||
== Review Version
|
||||
|
||||
Initial review version, for the review conducted from 7th December 2007 to
|
||||
16th December 2007.
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
[#comparison]
|
||||
|
||||
:idprefix: comparison_
|
||||
|
||||
= Comparison with Associative Containers
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Interface differences']
|
||||
[cols="1,1", frame=all, grid=rows]
|
||||
|===
|
||||
|Associative Containers |Unordered Associative Containers
|
||||
|
||||
|Parameterized by an ordering relation `Compare`
|
||||
|Parameterized by a function object `Hash` and an equivalence relation `Pred`
|
||||
|
||||
|Keys can be compared using `key_compare` which is accessed by member function `key_comp()`, values can be compared using `value_compare` which is accessed by member function `value_comp()`.
|
||||
|Keys can be hashed using `hasher` which is accessed by member function `hash_function()`, and checked for equality using `key_equal` which is accessed by member function `key_eq()`. There is no function object for compared or hashing values.
|
||||
|
||||
|Constructors have optional extra parameters for the comparison object.
|
||||
|Constructors have optional extra parameters for the initial minimum number of buckets, a hash function and an equality object.
|
||||
|
||||
|Keys `k1`, `k2` are considered equivalent if `!Compare(k1, k2) && !Compare(k2, k1)`.
|
||||
|Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`
|
||||
|
||||
|Member function `lower_bound(k)` and `upper_bound(k)`
|
||||
|No equivalent. Since the elements aren't ordered `lower_bound` and `upper_bound` would be meaningless.
|
||||
|
||||
|`equal_range(k)` returns an empty range at the position that `k` would be inserted if `k` isn't present in the container.
|
||||
|`equal_range(k)` returns a range at the end of the container if `k` isn't present in the container. It can't return a positioned range as `k` could be inserted into multiple place. To find out the bucket that `k` would be inserted into use `bucket(k)`. But remember that an insert can cause the container to rehash - meaning that the element can be inserted into a different bucket.
|
||||
|
||||
|`iterator`, `const_iterator` are of the bidirectional category.
|
||||
|`iterator`, `const_iterator` are of at least the forward category.
|
||||
|
||||
|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>>. Pointers and references to the container's elements are never invalidated.
|
||||
|
||||
|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.
|
||||
|
||||
|No equivalent
|
||||
|Local iterators can be used to iterate through individual buckets. (The order of local iterators and iterators aren't required to have any correspondence.)
|
||||
|
||||
|Can be compared using the `==`, `!=`, `<`, `\<=`, `>`, `>=` operators.
|
||||
|Can be compared using the `==` and `!=` operators.
|
||||
|
||||
|
|
||||
|When inserting with a hint, implementations are permitted to ignore the hint.
|
||||
|
||||
|`erase` never throws an exception
|
||||
|The containers' hash or predicate function can throw exceptions from `erase`.
|
||||
|
||||
|===
|
||||
|
||||
---
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Complexity Guarantees']
|
||||
[cols="1,1,1", frame=all, grid=rows]
|
||||
|===
|
||||
|Operation |Associative Containers |Unordered Associative Containers
|
||||
|
||||
|Construction of empty container
|
||||
|constant
|
||||
|O(_n_) where _n_ is the minimum number of buckets.
|
||||
|
||||
|Construction of container from a range of _N_ elements
|
||||
|O(_N log N_), O(_N_) if the range is sorted with `value_comp()`
|
||||
|Average case O(_N_), worst case O(_N^2^_)
|
||||
|
||||
|Insert a single element
|
||||
|logarithmic
|
||||
|Average case constant, worst case linear
|
||||
|
||||
|Insert a single element with a hint
|
||||
|Amortized constant if `t` elements inserted right after hint, logarithmic otherwise
|
||||
|Average case constant, worst case linear (ie. the same as a normal insert).
|
||||
|
||||
|Inserting a range of _N_ elements
|
||||
|_N_ log(`size()` + _N_)
|
||||
|Average case O(_N_), worst case O(_N_ * `size()`)
|
||||
|
||||
|Erase by key, `k`
|
||||
|O(log(`size()`) + `count(k)`)
|
||||
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
||||
|
||||
|Erase a single element by iterator
|
||||
|Amortized constant
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|Erase a range of _N_ elements
|
||||
|O(log(`size()`) + _N_)
|
||||
|Average case: O(_N_), Worst case: O(`size()`)
|
||||
|
||||
|Clearing the container
|
||||
|O(`size()`)
|
||||
|O(`size()`)
|
||||
|
||||
|Find
|
||||
|logarithmic
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|Count
|
||||
|O(log(`size()`) + `count(k)`)
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|`equal_range(k)`
|
||||
|logarithmic
|
||||
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
||||
|
||||
|`lower_bound`,`upper_bound`
|
||||
|logarithmic
|
||||
|n/a
|
||||
|
||||
|===
|
||||
@@ -1,12 +0,0 @@
|
||||
[#copyright]
|
||||
= Copyright and License
|
||||
|
||||
:idprefix: copyright_
|
||||
|
||||
*Daniel James*
|
||||
|
||||
Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
|
||||
|
||||
Copyright (C) 2005-2008 Daniel James
|
||||
|
||||
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)
|
||||
@@ -1,149 +0,0 @@
|
||||
[#hash_equality]
|
||||
|
||||
:idprefix: hash_equality_
|
||||
|
||||
= Equality Predicates and Hash Functions
|
||||
|
||||
While the associative containers use an ordering relation to specify how the
|
||||
elements are stored, the unordered associative containers use an equality
|
||||
predicate and a hash function. For example, <<unordered_map,boost::unordered_map>>
|
||||
is declared as:
|
||||
|
||||
```
|
||||
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_map;
|
||||
```
|
||||
|
||||
The hash function comes first as you might want to change the hash function
|
||||
but not the equality predicate. For example, if you wanted to use the
|
||||
http://www.isthe.com/chongo/tech/comp/fnv/[FNV-1 hash^] you could write:
|
||||
|
||||
```
|
||||
boost::unordered_map<std::string, int, hash::fnv_1>
|
||||
dictionary;
|
||||
```
|
||||
|
||||
There is an link:../../examples/fnv1.hpp[implementation of FNV-1^] in the examples directory.
|
||||
|
||||
If you wish to use a different equality function, you will also need to use a matching hash function. For example, to implement a case insensitive dictionary you need to define a case insensitive equality predicate and hash function:
|
||||
|
||||
```
|
||||
struct iequal_to
|
||||
{
|
||||
bool operator()(std::string const& x,
|
||||
std::string const& y) const
|
||||
{
|
||||
return boost::algorithm::iequals(x, y, std::locale());
|
||||
}
|
||||
};
|
||||
|
||||
struct ihash
|
||||
{
|
||||
std::size_t operator()(std::string const& x) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(std::string::const_iterator it = x.begin();
|
||||
it != x.end(); ++it)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(*it, locale));
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Which you can then use in a case insensitive dictionary:
|
||||
```
|
||||
boost::unordered_map<std::string, int, ihash, iequal_to>
|
||||
idictionary;
|
||||
```
|
||||
|
||||
This is a simplified version of the example at
|
||||
link:../../examples/case_insensitive.hpp[/libs/unordered/examples/case_insensitive.hpp^] which supports other locales and string types.
|
||||
|
||||
CAUTION: Be careful when using the equality (`==`) operator with custom equality
|
||||
predicates, especially if you're using a function pointer. If you compare two
|
||||
containers with different equality predicates then the result is undefined.
|
||||
For most stateless function objects this is impossible - since you can only
|
||||
compare objects with the same equality predicate you know the equality
|
||||
predicates must be equal. But if you're using function pointers or a stateful
|
||||
equality predicate (e.g. `boost::function`) then you can get into trouble.
|
||||
|
||||
== Custom Types
|
||||
|
||||
Similarly, a custom hash function can be used for custom types:
|
||||
|
||||
```
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
struct point_hash
|
||||
{
|
||||
std::size_t operator()(point const& p) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
boost::unordered_multiset<point, point_hash> points;
|
||||
```
|
||||
|
||||
Since the default hash function is link:../../../container_hash/index.html[Boost.Hash^],
|
||||
we can extend it to support the type so that the hash function doesn't need to be explicitly given:
|
||||
|
||||
```
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
std::size_t hash_value(point const& p) {
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Now the default function objects work.
|
||||
boost::unordered_multiset<point> points;
|
||||
```
|
||||
|
||||
See the link:../../../container_hash/index.html[Boost.Hash documentation^] for more detail on how to
|
||||
do this. Remember that it relies on extensions to the standard - so it
|
||||
won't work for other implementations of the unordered associative containers,
|
||||
you'll need to explicitly use Boost.Hash.
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Methods for accessing the hash and equality functions']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`hasher hash_function() const`
|
||||
|Returns the container's hash function.
|
||||
|
||||
|`key_equal key_eq() const`
|
||||
|Returns the container's key equality function..
|
||||
|
||||
|===
|
||||
@@ -1,116 +0,0 @@
|
||||
[#intro]
|
||||
= Introduction
|
||||
|
||||
:idprefix: intro_
|
||||
:cpp: C++
|
||||
|
||||
For accessing data based on key lookup, the {cpp} standard library offers `std::set`,
|
||||
`std::map`, `std::multiset` and `std::multimap`. These are generally
|
||||
implemented using balanced binary trees so that lookup time has
|
||||
logarithmic complexity. That is generally okay, but in many cases a
|
||||
link:https://en.wikipedia.org/wiki/Hash_table[hash table^] can perform better, as accessing data has constant complexity,
|
||||
on average. The worst case complexity is linear, but that occurs rarely and
|
||||
with some care, can be avoided.
|
||||
|
||||
Also, the existing containers require a 'less than' comparison object
|
||||
to order their elements. For some data types this is impossible to implement
|
||||
or isn't practical. In contrast, a hash table only needs an equality function
|
||||
and a hash function for the key.
|
||||
|
||||
With this in mind, unordered associative containers were added to the {cpp}
|
||||
standard. This is an implementation of the containers described in {cpp}11,
|
||||
with some <<compliance,deviations from the standard>> in
|
||||
order to work with non-{cpp}11 compilers and libraries.
|
||||
|
||||
`unordered_set` and `unordered_multiset` are defined in the header
|
||||
`<boost/unordered_set.hpp>`
|
||||
[source,c++]
|
||||
----
|
||||
namespace boost {
|
||||
template <
|
||||
class Key,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_multiset;
|
||||
}
|
||||
----
|
||||
|
||||
`unordered_map` and `unordered_multimap` are defined in the header
|
||||
`<boost/unordered_map.hpp>`
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
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_map;
|
||||
|
||||
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_multimap;
|
||||
}
|
||||
----
|
||||
|
||||
When using Boost.TR1, these classes are included from `<unordered_set>` and
|
||||
`<unordered_map>`, with the classes added to the `std::tr1` namespace.
|
||||
|
||||
The containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
typedef boost::unordered_map<std::string, int> map;
|
||||
map x;
|
||||
x["one"] = 1;
|
||||
x["two"] = 2;
|
||||
x["three"] = 3;
|
||||
|
||||
assert(x.at("one") == 1);
|
||||
assert(x.find("missing") == x.end());
|
||||
----
|
||||
|
||||
But since the elements aren't ordered, the output of:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
BOOST_FOREACH(map::value_type i, x) {
|
||||
std::cout<<i.first<<","<<i.second<<"\n";
|
||||
}
|
||||
----
|
||||
|
||||
can be in any order. For example, it might be:
|
||||
|
||||
[source]
|
||||
----
|
||||
two,2
|
||||
one,1
|
||||
three,3
|
||||
----
|
||||
|
||||
To store an object in an unordered associative container requires both a
|
||||
key equality function and a hash function. The default function objects in
|
||||
the standard containers support a few basic types including integer types,
|
||||
floating point types, pointer types, and the standard strings. Since
|
||||
Boost.Unordered uses link:../../../container_hash/index.html[boost::hash^] it also supports some other types,
|
||||
including standard containers. To use any types not supported by these methods
|
||||
you have to extend Boost.Hash to support the type or use
|
||||
your own custom equality predicates and hash functions. See the
|
||||
<<hash_equality,Equality Predicates and Hash Functions>> section
|
||||
for more details.
|
||||
|
||||
There are other differences, which are listed in the
|
||||
<<comparison,Comparison with Associative Containers>> section.
|
||||
@@ -1,68 +0,0 @@
|
||||
[#rationale]
|
||||
|
||||
:idprefix: rationale_
|
||||
|
||||
= Implementation Rationale
|
||||
|
||||
The intent of this library is to implement the unordered
|
||||
containers in the standard, so the interface was fixed. But there are
|
||||
still some implementation decisions to make. The priorities are
|
||||
conformance to the standard and portability.
|
||||
|
||||
The http://en.wikipedia.org/wiki/Hash_table[Wikipedia article on hash tables^]
|
||||
has a good summary of the implementation issues for hash tables in general.
|
||||
|
||||
== Data Structure
|
||||
|
||||
By specifying an interface for accessing the buckets of the container the
|
||||
standard pretty much requires that the hash table uses chained addressing.
|
||||
|
||||
It would be conceivable to write a hash table that uses another method. For
|
||||
example, it could use open addressing, and use the lookup chain to act as a
|
||||
bucket but there are some serious problems with this:
|
||||
|
||||
* The standard requires that pointers to elements aren't invalidated, so
|
||||
the elements can't be stored in one array, but will need a layer of
|
||||
indirection instead - losing the efficiency and most of the memory gain,
|
||||
the main advantages of open addressing.
|
||||
* Local iterators would be very inefficient and may not be able to
|
||||
meet the complexity requirements.
|
||||
* There are also the restrictions on when iterators can be invalidated. Since
|
||||
open addressing degrades badly when there are a high number of collisions the
|
||||
restrictions could prevent a rehash when it's really needed. The maximum load
|
||||
factor could be set to a fairly low value to work around this - but the
|
||||
standard requires that it is initially set to 1.0.
|
||||
* And since the standard is written with a eye towards chained
|
||||
addressing, users will be surprised if the performance doesn't reflect that.
|
||||
|
||||
So chained addressing is used.
|
||||
|
||||
== Number of Buckets
|
||||
|
||||
There are two popular methods for choosing the number of buckets in a hash
|
||||
table. One is to have a prime number of buckets, another is to use a power
|
||||
of 2.
|
||||
|
||||
Using a prime number of buckets, and choosing a bucket by using the modulus
|
||||
of the hash function's result will usually give a good result. The downside
|
||||
is that the required modulus operation is fairly expensive. This is what the
|
||||
containers used to do in most cases.
|
||||
|
||||
Using a power of 2 allows for much quicker selection of the bucket to use,
|
||||
but at the expense of losing the upper bits of the hash value. For some
|
||||
specially designed hash functions it is possible to do this and still get a
|
||||
good result but as the containers can take arbitrary hash functions this can't
|
||||
be relied on.
|
||||
|
||||
To avoid this a transformation could be applied to the hash function, for an
|
||||
example see
|
||||
http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm[Thomas Wang's article on integer hash functions^].
|
||||
Unfortunately, a transformation like Wang's requires knowledge of the number
|
||||
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
|
||||
number of buckets. This provides a good compromise between speed and
|
||||
distribution.
|
||||
@@ -1,7 +0,0 @@
|
||||
[#reference]
|
||||
= Reference
|
||||
|
||||
include::unordered_map.adoc[]
|
||||
include::unordered_multimap.adoc[]
|
||||
include::unordered_set.adoc[]
|
||||
include::unordered_multiset.adoc[]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@
|
||||
namespace hash_examples
|
||||
{
|
||||
struct iequal_to
|
||||
: std::binary_function<std::string, std::string, bool>
|
||||
{
|
||||
iequal_to() {}
|
||||
explicit iequal_to(std::locale const& l) : locale_(l) {}
|
||||
@@ -33,6 +34,7 @@ namespace hash_examples
|
||||
};
|
||||
|
||||
struct ihash
|
||||
: std::unary_function<std::string, std::size_t>
|
||||
{
|
||||
ihash() {}
|
||||
explicit ihash(std::locale const& l) : locale_(l) {}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./case_insensitive.hpp"
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
struct word_info {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,799 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/util.hpp>
|
||||
#include <boost/unordered/detail/allocate.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
#include <boost/swap.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/limits.hpp>
|
||||
#include <boost/iterator.hpp>
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
template <typename Types> struct table;
|
||||
template <typename NodePointer> struct bucket;
|
||||
struct ptr_bucket;
|
||||
template <typename Types> struct table_impl;
|
||||
template <typename Types> struct grouped_table_impl;
|
||||
|
||||
}}}
|
||||
|
||||
namespace boost { namespace unordered { namespace iterator_detail {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Iterators
|
||||
//
|
||||
// all no throw
|
||||
|
||||
template <typename NodePointer, typename Value> struct iterator;
|
||||
template <typename ConstNodePointer, typename NodePointer,
|
||||
typename Value> struct c_iterator;
|
||||
template <typename NodePointer, typename Value, typename Policy>
|
||||
struct l_iterator;
|
||||
template <typename ConstNodePointer, typename NodePointer,
|
||||
typename Value, typename Policy> struct cl_iterator;
|
||||
|
||||
// Local Iterators
|
||||
//
|
||||
// all no throw
|
||||
|
||||
template <typename NodePointer, typename Value, typename Policy>
|
||||
struct l_iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, Value, std::ptrdiff_t,
|
||||
NodePointer, Value&>
|
||||
{
|
||||
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
|
||||
template <typename ConstNodePointer, typename NodePointer2,
|
||||
typename Value2, typename Policy2>
|
||||
friend struct boost::unordered::iterator_detail::cl_iterator;
|
||||
private:
|
||||
#endif
|
||||
typedef NodePointer node_pointer;
|
||||
typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
|
||||
iterator;
|
||||
node_pointer ptr_;
|
||||
std::size_t bucket_;
|
||||
std::size_t bucket_count_;
|
||||
|
||||
public:
|
||||
|
||||
l_iterator() : ptr_() {}
|
||||
|
||||
l_iterator(iterator x, std::size_t b, std::size_t c)
|
||||
: ptr_(x.node_), bucket_(b), bucket_count_(c) {}
|
||||
|
||||
Value& operator*() const {
|
||||
return ptr_->value();
|
||||
}
|
||||
|
||||
Value* operator->() const {
|
||||
return ptr_->value_ptr();
|
||||
}
|
||||
|
||||
l_iterator& operator++() {
|
||||
ptr_ = static_cast<node_pointer>(ptr_->next_);
|
||||
if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_)
|
||||
!= bucket_)
|
||||
ptr_ = node_pointer();
|
||||
return *this;
|
||||
}
|
||||
|
||||
l_iterator operator++(int) {
|
||||
l_iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(l_iterator x) const {
|
||||
return ptr_ == x.ptr_;
|
||||
}
|
||||
|
||||
bool operator!=(l_iterator x) const {
|
||||
return ptr_ != x.ptr_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ConstNodePointer, typename NodePointer, typename Value,
|
||||
typename Policy>
|
||||
struct cl_iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, Value, std::ptrdiff_t,
|
||||
ConstNodePointer, Value const&>
|
||||
{
|
||||
friend struct boost::unordered::iterator_detail::l_iterator
|
||||
<NodePointer, Value, Policy>;
|
||||
private:
|
||||
|
||||
typedef NodePointer node_pointer;
|
||||
typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
|
||||
iterator;
|
||||
node_pointer ptr_;
|
||||
std::size_t bucket_;
|
||||
std::size_t bucket_count_;
|
||||
|
||||
public:
|
||||
|
||||
cl_iterator() : ptr_() {}
|
||||
|
||||
cl_iterator(iterator x, std::size_t b, std::size_t c) :
|
||||
ptr_(x.node_), bucket_(b), bucket_count_(c) {}
|
||||
|
||||
cl_iterator(boost::unordered::iterator_detail::l_iterator<
|
||||
NodePointer, Value, Policy> const& x) :
|
||||
ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_)
|
||||
{}
|
||||
|
||||
Value const&
|
||||
operator*() const {
|
||||
return ptr_->value();
|
||||
}
|
||||
|
||||
Value const* operator->() const {
|
||||
return ptr_->value_ptr();
|
||||
}
|
||||
|
||||
cl_iterator& operator++() {
|
||||
ptr_ = static_cast<node_pointer>(ptr_->next_);
|
||||
if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_)
|
||||
!= bucket_)
|
||||
ptr_ = node_pointer();
|
||||
return *this;
|
||||
}
|
||||
|
||||
cl_iterator operator++(int) {
|
||||
cl_iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator==(cl_iterator const& x, cl_iterator const& y) {
|
||||
return x.ptr_ == y.ptr_;
|
||||
}
|
||||
|
||||
friend bool operator!=(cl_iterator const& x, cl_iterator const& y) {
|
||||
return x.ptr_ != y.ptr_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NodePointer, typename Value>
|
||||
struct iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, Value, std::ptrdiff_t,
|
||||
NodePointer, Value&>
|
||||
{
|
||||
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
|
||||
template <typename, typename, typename>
|
||||
friend struct boost::unordered::iterator_detail::c_iterator;
|
||||
template <typename, typename, typename>
|
||||
friend struct boost::unordered::iterator_detail::l_iterator;
|
||||
template <typename, typename, typename, typename>
|
||||
friend struct boost::unordered::iterator_detail::cl_iterator;
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::table;
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::table_impl;
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::grouped_table_impl;
|
||||
private:
|
||||
#endif
|
||||
typedef NodePointer node_pointer;
|
||||
node_pointer node_;
|
||||
|
||||
public:
|
||||
|
||||
iterator() : node_() {}
|
||||
|
||||
explicit iterator(node_pointer const& x) : node_(x) {}
|
||||
|
||||
Value& operator*() const {
|
||||
return node_->value();
|
||||
}
|
||||
|
||||
Value* operator->() const {
|
||||
return &node_->value();
|
||||
}
|
||||
|
||||
iterator& operator++() {
|
||||
node_ = static_cast<node_pointer>(node_->next_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator tmp(node_);
|
||||
node_ = static_cast<node_pointer>(node_->next_);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(iterator const& x) const {
|
||||
return node_ == x.node_;
|
||||
}
|
||||
|
||||
bool operator!=(iterator const& x) const {
|
||||
return node_ != x.node_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ConstNodePointer, typename NodePointer, typename Value>
|
||||
struct c_iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, Value, std::ptrdiff_t,
|
||||
ConstNodePointer, Value const&>
|
||||
{
|
||||
friend struct boost::unordered::iterator_detail::iterator<
|
||||
NodePointer, Value>;
|
||||
|
||||
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::table;
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::table_impl;
|
||||
template <typename>
|
||||
friend struct boost::unordered::detail::grouped_table_impl;
|
||||
|
||||
private:
|
||||
#endif
|
||||
|
||||
typedef NodePointer node_pointer;
|
||||
typedef boost::unordered::iterator_detail::iterator<NodePointer, Value>
|
||||
iterator;
|
||||
node_pointer node_;
|
||||
|
||||
public:
|
||||
|
||||
c_iterator() : node_() {}
|
||||
|
||||
explicit c_iterator(node_pointer const& x) : node_(x) {}
|
||||
|
||||
c_iterator(boost::unordered::iterator_detail::iterator<
|
||||
NodePointer, Value> const& x) : node_(x.node_) {}
|
||||
|
||||
Value const& operator*() const {
|
||||
return node_->value();
|
||||
}
|
||||
|
||||
Value const* operator->() const {
|
||||
return &node_->value();
|
||||
}
|
||||
|
||||
c_iterator& operator++() {
|
||||
node_ = static_cast<node_pointer>(node_->next_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
c_iterator operator++(int) {
|
||||
c_iterator tmp(node_);
|
||||
node_ = static_cast<node_pointer>(node_->next_);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend bool operator==(c_iterator const& x, c_iterator const& y) {
|
||||
return x.node_ == y.node_;
|
||||
}
|
||||
|
||||
friend bool operator!=(c_iterator const& x, c_iterator const& y) {
|
||||
return x.node_ != y.node_;
|
||||
}
|
||||
};
|
||||
}}}
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Node construction
|
||||
|
||||
template <typename NodeAlloc>
|
||||
struct node_constructor
|
||||
{
|
||||
private:
|
||||
|
||||
typedef NodeAlloc node_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
|
||||
node_allocator_traits;
|
||||
typedef typename node_allocator_traits::value_type node;
|
||||
typedef typename node_allocator_traits::pointer node_pointer;
|
||||
typedef typename node::value_type value_type;
|
||||
|
||||
protected:
|
||||
|
||||
node_allocator& alloc_;
|
||||
|
||||
private:
|
||||
|
||||
node_pointer node_;
|
||||
bool node_constructed_;
|
||||
bool value_constructed_;
|
||||
|
||||
public:
|
||||
|
||||
node_constructor(node_allocator& n) :
|
||||
alloc_(n),
|
||||
node_(),
|
||||
node_constructed_(false),
|
||||
value_constructed_(false)
|
||||
{
|
||||
}
|
||||
|
||||
~node_constructor();
|
||||
|
||||
void construct();
|
||||
|
||||
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
|
||||
void construct_with_value(BOOST_UNORDERED_EMPLACE_ARGS)
|
||||
{
|
||||
construct();
|
||||
boost::unordered::detail::construct_value_impl(
|
||||
alloc_, node_->value_ptr(), BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
value_constructed_ = true;
|
||||
}
|
||||
|
||||
template <typename A0>
|
||||
void construct_with_value2(BOOST_FWD_REF(A0) a0)
|
||||
{
|
||||
construct();
|
||||
boost::unordered::detail::construct_value_impl(
|
||||
alloc_, node_->value_ptr(),
|
||||
BOOST_UNORDERED_EMPLACE_ARGS1(boost::forward<A0>(a0)));
|
||||
value_constructed_ = true;
|
||||
}
|
||||
|
||||
value_type const& value() const {
|
||||
BOOST_ASSERT(node_ && node_constructed_ && value_constructed_);
|
||||
return node_->value();
|
||||
}
|
||||
|
||||
// no throw
|
||||
node_pointer release()
|
||||
{
|
||||
BOOST_ASSERT(node_ && node_constructed_);
|
||||
node_pointer p = node_;
|
||||
node_ = node_pointer();
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
node_constructor(node_constructor const&);
|
||||
node_constructor& operator=(node_constructor const&);
|
||||
};
|
||||
|
||||
template <typename Alloc>
|
||||
node_constructor<Alloc>::~node_constructor()
|
||||
{
|
||||
if (node_) {
|
||||
if (value_constructed_) {
|
||||
boost::unordered::detail::destroy_value_impl(alloc_,
|
||||
node_->value_ptr());
|
||||
}
|
||||
|
||||
if (node_constructed_) {
|
||||
node_allocator_traits::destroy(alloc_,
|
||||
boost::addressof(*node_));
|
||||
}
|
||||
|
||||
node_allocator_traits::deallocate(alloc_, node_, 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Alloc>
|
||||
void node_constructor<Alloc>::construct()
|
||||
{
|
||||
if(!node_) {
|
||||
node_constructed_ = false;
|
||||
value_constructed_ = false;
|
||||
|
||||
node_ = node_allocator_traits::allocate(alloc_, 1);
|
||||
|
||||
node_allocator_traits::construct(alloc_,
|
||||
boost::addressof(*node_), node());
|
||||
node_->init(static_cast<typename node::link_pointer>(node_));
|
||||
node_constructed_ = true;
|
||||
}
|
||||
else {
|
||||
BOOST_ASSERT(node_constructed_);
|
||||
|
||||
if (value_constructed_)
|
||||
{
|
||||
boost::unordered::detail::destroy_value_impl(alloc_,
|
||||
node_->value_ptr());
|
||||
value_constructed_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Node Holder
|
||||
//
|
||||
// Temporary store for nodes. Deletes any that aren't used.
|
||||
|
||||
template <typename NodeAlloc>
|
||||
struct node_holder : private node_constructor<NodeAlloc>
|
||||
{
|
||||
private:
|
||||
typedef node_constructor<NodeAlloc> base;
|
||||
|
||||
typedef NodeAlloc node_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
|
||||
node_allocator_traits;
|
||||
typedef typename node_allocator_traits::value_type node;
|
||||
typedef typename node_allocator_traits::pointer node_pointer;
|
||||
typedef typename node::value_type value_type;
|
||||
typedef typename node::link_pointer link_pointer;
|
||||
typedef boost::unordered::iterator_detail::
|
||||
iterator<node_pointer, value_type> iterator;
|
||||
|
||||
node_pointer nodes_;
|
||||
|
||||
public:
|
||||
|
||||
template <typename Table>
|
||||
explicit node_holder(Table& b) :
|
||||
base(b.node_alloc()),
|
||||
nodes_()
|
||||
{
|
||||
if (b.size_) {
|
||||
typename Table::previous_pointer prev = b.get_previous_start();
|
||||
nodes_ = static_cast<node_pointer>(prev->next_);
|
||||
prev->next_ = link_pointer();
|
||||
b.size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~node_holder();
|
||||
|
||||
template <typename T>
|
||||
inline void assign_impl(T const& v) {
|
||||
nodes_->value() = v;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline void assign_impl(std::pair<T1 const, T2> const& v) {
|
||||
const_cast<T1&>(nodes_->value().first) = v.first;
|
||||
nodes_->value().second = v.second;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void move_assign_impl(T& v) {
|
||||
nodes_->value() = boost::move(v);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline void move_assign_impl(std::pair<T1 const, T2>& v) {
|
||||
// TODO: Move key as well?
|
||||
const_cast<T1&>(nodes_->value().first) =
|
||||
boost::move(const_cast<T1&>(v.first));
|
||||
nodes_->value().second = boost::move(v.second);
|
||||
}
|
||||
|
||||
node_pointer copy_of(value_type const& v)
|
||||
{
|
||||
if (nodes_) {
|
||||
assign_impl(v);
|
||||
node_pointer p = nodes_;
|
||||
nodes_ = static_cast<node_pointer>(p->next_);
|
||||
p->init(static_cast<typename node::link_pointer>(p));
|
||||
p->next_ = link_pointer();
|
||||
return p;
|
||||
}
|
||||
else {
|
||||
this->construct_with_value2(v);
|
||||
return base::release();
|
||||
}
|
||||
}
|
||||
|
||||
node_pointer move_copy_of(value_type& v)
|
||||
{
|
||||
if (nodes_) {
|
||||
move_assign_impl(v);
|
||||
node_pointer p = nodes_;
|
||||
nodes_ = static_cast<node_pointer>(p->next_);
|
||||
p->init(static_cast<typename node::link_pointer>(p));
|
||||
p->next_ = link_pointer();
|
||||
return p;
|
||||
}
|
||||
else {
|
||||
this->construct_with_value2(boost::move(v));
|
||||
return base::release();
|
||||
}
|
||||
}
|
||||
|
||||
iterator begin() const
|
||||
{
|
||||
return iterator(nodes_);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Alloc>
|
||||
node_holder<Alloc>::~node_holder()
|
||||
{
|
||||
while (nodes_) {
|
||||
node_pointer p = nodes_;
|
||||
nodes_ = static_cast<node_pointer>(p->next_);
|
||||
|
||||
boost::unordered::detail::destroy_value_impl(this->alloc_,
|
||||
p->value_ptr());
|
||||
node_allocator_traits::destroy(this->alloc_, boost::addressof(*p));
|
||||
node_allocator_traits::deallocate(this->alloc_, p, 1);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Bucket
|
||||
|
||||
template <typename NodePointer>
|
||||
struct bucket
|
||||
{
|
||||
typedef NodePointer previous_pointer;
|
||||
previous_pointer next_;
|
||||
|
||||
bucket() : next_() {}
|
||||
|
||||
previous_pointer first_from_start()
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
enum { extra_node = true };
|
||||
};
|
||||
|
||||
struct ptr_bucket
|
||||
{
|
||||
typedef ptr_bucket* previous_pointer;
|
||||
previous_pointer next_;
|
||||
|
||||
ptr_bucket() : next_(0) {}
|
||||
|
||||
previous_pointer first_from_start()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
enum { extra_node = false };
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Hash Policy
|
||||
//
|
||||
// Don't really want table to derive from this, but will for now.
|
||||
|
||||
template <typename SizeT>
|
||||
struct prime_policy
|
||||
{
|
||||
template <typename Hash, typename T>
|
||||
static inline SizeT apply_hash(Hash const& hf, T const& x) {
|
||||
return hf(x);
|
||||
}
|
||||
|
||||
static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) {
|
||||
return hash % bucket_count;
|
||||
}
|
||||
|
||||
static inline SizeT new_bucket_count(SizeT min) {
|
||||
return boost::unordered::detail::next_prime(min);
|
||||
}
|
||||
|
||||
static inline SizeT prev_bucket_count(SizeT max) {
|
||||
return boost::unordered::detail::prev_prime(max);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SizeT>
|
||||
struct mix64_policy
|
||||
{
|
||||
template <typename Hash, typename T>
|
||||
static inline SizeT apply_hash(Hash const& hf, T const& x) {
|
||||
SizeT key = hf(x);
|
||||
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
|
||||
key = key ^ (key >> 24);
|
||||
key = (key + (key << 3)) + (key << 8); // key * 265
|
||||
key = key ^ (key >> 14);
|
||||
key = (key + (key << 2)) + (key << 4); // key * 21
|
||||
key = key ^ (key >> 28);
|
||||
key = key + (key << 31);
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) {
|
||||
return hash & (bucket_count - 1);
|
||||
}
|
||||
|
||||
static inline SizeT new_bucket_count(SizeT min) {
|
||||
if (min <= 4) return 4;
|
||||
--min;
|
||||
min |= min >> 1;
|
||||
min |= min >> 2;
|
||||
min |= min >> 4;
|
||||
min |= min >> 8;
|
||||
min |= min >> 16;
|
||||
min |= min >> 32;
|
||||
return min + 1;
|
||||
}
|
||||
|
||||
static inline SizeT prev_bucket_count(SizeT max) {
|
||||
max |= max >> 1;
|
||||
max |= max >> 2;
|
||||
max |= max >> 4;
|
||||
max |= max >> 8;
|
||||
max |= max >> 16;
|
||||
max |= max >> 32;
|
||||
return (max >> 1) + 1;
|
||||
}
|
||||
};
|
||||
|
||||
template <int digits, int radix>
|
||||
struct pick_policy_impl {
|
||||
typedef prime_policy<std::size_t> type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct pick_policy_impl<64, 2> {
|
||||
typedef mix64_policy<std::size_t> type;
|
||||
};
|
||||
|
||||
struct pick_policy :
|
||||
pick_policy_impl<
|
||||
std::numeric_limits<std::size_t>::digits,
|
||||
std::numeric_limits<std::size_t>::radix> {};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Functions
|
||||
|
||||
// Assigning and swapping the equality and hash function objects
|
||||
// needs strong exception safety. To implement that normally we'd
|
||||
// require one of them to be known to not throw and the other to
|
||||
// guarantee strong exception safety. Unfortunately they both only
|
||||
// have basic exception safety. So to acheive strong exception
|
||||
// safety we have storage space for two copies, and assign the new
|
||||
// copies to the unused space. Then switch to using that to use
|
||||
// them. This is implemented in 'set_hash_functions' which
|
||||
// atomically assigns the new function objects in a strongly
|
||||
// exception safe manner.
|
||||
|
||||
template <class H, class P> class set_hash_functions;
|
||||
|
||||
template <class H, class P>
|
||||
class functions
|
||||
{
|
||||
friend class boost::unordered::detail::set_hash_functions<H, P>;
|
||||
functions& operator=(functions const&);
|
||||
|
||||
typedef compressed<H, P> function_pair;
|
||||
|
||||
typedef typename boost::aligned_storage<
|
||||
sizeof(function_pair),
|
||||
boost::alignment_of<function_pair>::value>::type aligned_function;
|
||||
|
||||
bool current_; // The currently active functions.
|
||||
aligned_function funcs_[2];
|
||||
|
||||
function_pair const& current() const {
|
||||
return *static_cast<function_pair const*>(
|
||||
static_cast<void const*>(&funcs_[current_]));
|
||||
}
|
||||
|
||||
void construct(bool which, H const& hf, P const& eq)
|
||||
{
|
||||
new((void*) &funcs_[which]) function_pair(hf, eq);
|
||||
}
|
||||
|
||||
void construct(bool which, function_pair const& f)
|
||||
{
|
||||
new((void*) &funcs_[which]) function_pair(f);
|
||||
}
|
||||
|
||||
void destroy(bool which)
|
||||
{
|
||||
boost::unordered::detail::destroy((function_pair*)(&funcs_[which]));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
functions(H const& hf, P const& eq)
|
||||
: current_(false)
|
||||
{
|
||||
construct(current_, hf, eq);
|
||||
}
|
||||
|
||||
functions(functions const& bf)
|
||||
: current_(false)
|
||||
{
|
||||
construct(current_, bf.current());
|
||||
}
|
||||
|
||||
~functions() {
|
||||
this->destroy(current_);
|
||||
}
|
||||
|
||||
H const& hash_function() const {
|
||||
return current().first();
|
||||
}
|
||||
|
||||
P const& key_eq() const {
|
||||
return current().second();
|
||||
}
|
||||
};
|
||||
|
||||
template <class H, class P>
|
||||
class set_hash_functions
|
||||
{
|
||||
set_hash_functions(set_hash_functions const&);
|
||||
set_hash_functions& operator=(set_hash_functions const&);
|
||||
|
||||
functions<H,P>& functions_;
|
||||
bool tmp_functions_;
|
||||
|
||||
public:
|
||||
|
||||
set_hash_functions(functions<H,P>& f, H const& h, P const& p)
|
||||
: functions_(f),
|
||||
tmp_functions_(!f.current_)
|
||||
{
|
||||
f.construct(tmp_functions_, h, p);
|
||||
}
|
||||
|
||||
set_hash_functions(functions<H,P>& f, functions<H,P> const& other)
|
||||
: functions_(f),
|
||||
tmp_functions_(!f.current_)
|
||||
{
|
||||
f.construct(tmp_functions_, other.current());
|
||||
}
|
||||
|
||||
~set_hash_functions()
|
||||
{
|
||||
functions_.destroy(tmp_functions_);
|
||||
}
|
||||
|
||||
void commit()
|
||||
{
|
||||
functions_.current_ = tmp_functions_;
|
||||
tmp_functions_ = !tmp_functions_;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// rvalue parameters when type can't be a BOOST_RV_REF(T) parameter
|
||||
// e.g. for int
|
||||
|
||||
#if !defined(BOOST_NO_RVALUE_REFERENCES)
|
||||
# define BOOST_UNORDERED_RV_REF(T) BOOST_RV_REF(T)
|
||||
#else
|
||||
struct please_ignore_this_overload {
|
||||
typedef please_ignore_this_overload type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct rv_ref_impl {
|
||||
typedef BOOST_RV_REF(T) type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct rv_ref :
|
||||
boost::detail::if_true<
|
||||
boost::is_class<T>::value
|
||||
>::BOOST_NESTED_TEMPLATE then <
|
||||
boost::unordered::detail::rv_ref_impl<T>,
|
||||
please_ignore_this_overload
|
||||
>::type
|
||||
{};
|
||||
|
||||
# define BOOST_UNORDERED_RV_REF(T) \
|
||||
typename boost::unordered::detail::rv_ref<T>::type
|
||||
#endif
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,781 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/table.hpp>
|
||||
#include <boost/unordered/detail/extract_key.hpp>
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
template <typename A, typename T> struct grouped_node;
|
||||
template <typename T> struct grouped_ptr_node;
|
||||
template <typename Types> struct grouped_table_impl;
|
||||
|
||||
template <typename A, typename T>
|
||||
struct grouped_node :
|
||||
boost::unordered::detail::value_base<T>
|
||||
{
|
||||
typedef typename ::boost::unordered::detail::rebind_wrap<
|
||||
A, grouped_node<A, T> >::type::pointer link_pointer;
|
||||
|
||||
link_pointer next_;
|
||||
link_pointer group_prev_;
|
||||
std::size_t hash_;
|
||||
|
||||
grouped_node() :
|
||||
next_(),
|
||||
group_prev_(),
|
||||
hash_(0)
|
||||
{}
|
||||
|
||||
void init(link_pointer self)
|
||||
{
|
||||
group_prev_ = self;
|
||||
}
|
||||
|
||||
private:
|
||||
grouped_node& operator=(grouped_node const&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct grouped_ptr_node :
|
||||
boost::unordered::detail::value_base<T>,
|
||||
boost::unordered::detail::ptr_bucket
|
||||
{
|
||||
typedef boost::unordered::detail::ptr_bucket bucket_base;
|
||||
typedef ptr_bucket* link_pointer;
|
||||
|
||||
link_pointer group_prev_;
|
||||
std::size_t hash_;
|
||||
|
||||
grouped_ptr_node() :
|
||||
bucket_base(),
|
||||
group_prev_(0),
|
||||
hash_(0)
|
||||
{}
|
||||
|
||||
void init(link_pointer self)
|
||||
{
|
||||
group_prev_ = self;
|
||||
}
|
||||
|
||||
private:
|
||||
grouped_ptr_node& operator=(grouped_ptr_node const&);
|
||||
};
|
||||
|
||||
// If the allocator uses raw pointers use grouped_ptr_node
|
||||
// Otherwise use grouped_node.
|
||||
|
||||
template <typename A, typename T, typename NodePtr, typename BucketPtr>
|
||||
struct pick_grouped_node2
|
||||
{
|
||||
typedef boost::unordered::detail::grouped_node<A, T> node;
|
||||
|
||||
typedef typename boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A, node>::type
|
||||
>::pointer node_pointer;
|
||||
|
||||
typedef boost::unordered::detail::bucket<node_pointer> bucket;
|
||||
typedef node_pointer link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T>
|
||||
struct pick_grouped_node2<A, T,
|
||||
boost::unordered::detail::grouped_ptr_node<T>*,
|
||||
boost::unordered::detail::ptr_bucket*>
|
||||
{
|
||||
typedef boost::unordered::detail::grouped_ptr_node<T> node;
|
||||
typedef boost::unordered::detail::ptr_bucket bucket;
|
||||
typedef bucket* link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T>
|
||||
struct pick_grouped_node
|
||||
{
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::grouped_ptr_node<T> >::type
|
||||
> tentative_node_traits;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::ptr_bucket >::type
|
||||
> tentative_bucket_traits;
|
||||
|
||||
typedef pick_grouped_node2<A, T,
|
||||
typename tentative_node_traits::pointer,
|
||||
typename tentative_bucket_traits::pointer> pick;
|
||||
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T, typename H, typename P>
|
||||
struct multiset
|
||||
{
|
||||
typedef boost::unordered::detail::multiset<A, T, H, P> types;
|
||||
|
||||
typedef A allocator;
|
||||
typedef T value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef T key_type;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<allocator> traits;
|
||||
typedef boost::unordered::detail::pick_grouped_node<allocator,
|
||||
value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::grouped_table_impl<types> table;
|
||||
typedef boost::unordered::detail::set_extractor<value_type> extractor;
|
||||
|
||||
typedef boost::unordered::detail::pick_policy::type policy;
|
||||
};
|
||||
|
||||
template <typename A, typename K, typename M, typename H, typename P>
|
||||
struct multimap
|
||||
{
|
||||
typedef boost::unordered::detail::multimap<A, K, M, H, P> types;
|
||||
|
||||
typedef A allocator;
|
||||
typedef std::pair<K const, M> value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef K key_type;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<allocator> traits;
|
||||
typedef boost::unordered::detail::pick_grouped_node<allocator,
|
||||
value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::grouped_table_impl<types> table;
|
||||
typedef boost::unordered::detail::map_extractor<key_type, value_type>
|
||||
extractor;
|
||||
|
||||
typedef boost::unordered::detail::pick_policy::type policy;
|
||||
};
|
||||
|
||||
template <typename Types>
|
||||
struct grouped_table_impl : boost::unordered::detail::table<Types>
|
||||
{
|
||||
typedef boost::unordered::detail::table<Types> table;
|
||||
typedef typename table::value_type value_type;
|
||||
typedef typename table::bucket bucket;
|
||||
typedef typename table::policy policy;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::node_allocator node_allocator;
|
||||
typedef typename table::node_allocator_traits node_allocator_traits;
|
||||
typedef typename table::bucket_pointer bucket_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
typedef typename table::previous_pointer previous_pointer;
|
||||
typedef typename table::hasher hasher;
|
||||
typedef typename table::key_equal key_equal;
|
||||
typedef typename table::key_type key_type;
|
||||
typedef typename table::node_constructor node_constructor;
|
||||
typedef typename table::extractor extractor;
|
||||
typedef typename table::iterator iterator;
|
||||
typedef typename table::c_iterator c_iterator;
|
||||
|
||||
// Constructors
|
||||
|
||||
grouped_table_impl(std::size_t n,
|
||||
hasher const& hf,
|
||||
key_equal const& eq,
|
||||
node_allocator const& a)
|
||||
: table(n, hf, eq, a)
|
||||
{}
|
||||
|
||||
grouped_table_impl(grouped_table_impl const& x)
|
||||
: table(x, node_allocator_traits::
|
||||
select_on_container_copy_construction(x.node_alloc()))
|
||||
{
|
||||
this->init(x);
|
||||
}
|
||||
|
||||
grouped_table_impl(grouped_table_impl const& x,
|
||||
node_allocator const& a)
|
||||
: table(x, a)
|
||||
{
|
||||
this->init(x);
|
||||
}
|
||||
|
||||
grouped_table_impl(grouped_table_impl& x,
|
||||
boost::unordered::detail::move_tag m)
|
||||
: table(x, m)
|
||||
{}
|
||||
|
||||
grouped_table_impl(grouped_table_impl& x,
|
||||
node_allocator const& a,
|
||||
boost::unordered::detail::move_tag m)
|
||||
: table(x, a, m)
|
||||
{
|
||||
this->move_init(x);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
template <class Key, class Pred>
|
||||
iterator find_node_impl(
|
||||
std::size_t key_hash,
|
||||
Key const& k,
|
||||
Pred const& eq) const
|
||||
{
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, key_hash);
|
||||
iterator n = this->begin(bucket_index);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!n.node_) return n;
|
||||
|
||||
std::size_t node_hash = n.node_->hash_;
|
||||
if (key_hash == node_hash)
|
||||
{
|
||||
if (eq(k, this->get_key(*n)))
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (policy::to_bucket(this->bucket_count_, node_hash)
|
||||
!= bucket_index)
|
||||
return iterator();
|
||||
}
|
||||
|
||||
n = iterator(static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(n.node_->group_prev_)->next_));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t count(key_type const& k) const
|
||||
{
|
||||
iterator n = this->find_node(k);
|
||||
if (!n.node_) return 0;
|
||||
|
||||
std::size_t x = 0;
|
||||
node_pointer it = n.node_;
|
||||
do {
|
||||
it = static_cast<node_pointer>(it->group_prev_);
|
||||
++x;
|
||||
} while(it != n.node_);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(key_type const& k) const
|
||||
{
|
||||
iterator n = this->find_node(k);
|
||||
return std::make_pair(
|
||||
n, n.node_ ? iterator(
|
||||
static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(n.node_->group_prev_)->next_
|
||||
)) : n);
|
||||
}
|
||||
|
||||
// Equality
|
||||
|
||||
bool equals(grouped_table_impl const& other) const
|
||||
{
|
||||
if(this->size_ != other.size_) return false;
|
||||
|
||||
for(iterator n1 = this->begin(); n1.node_;)
|
||||
{
|
||||
iterator n2 = other.find_matching_node(n1);
|
||||
if (!n2.node_) return false;
|
||||
iterator end1(static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(n1.node_->group_prev_)->next_));
|
||||
iterator end2(static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(n2.node_->group_prev_)->next_));
|
||||
if (!group_equals(n1, end1, n2, end2)) return false;
|
||||
n1 = end1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DEPRECATED_EQUALITY)
|
||||
|
||||
static bool group_equals(iterator n1, iterator end1,
|
||||
iterator n2, iterator end2)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if (*n1 != *n2) break;
|
||||
|
||||
++n1;
|
||||
++n2;
|
||||
|
||||
if (n1 == end1) return n2 == end2;
|
||||
if (n2 == end2) return false;
|
||||
}
|
||||
|
||||
for(iterator n1a = n1, n2a = n2;;)
|
||||
{
|
||||
++n1a;
|
||||
++n2a;
|
||||
|
||||
if (n1a == end1)
|
||||
{
|
||||
if (n2a == end2) break;
|
||||
else return false;
|
||||
}
|
||||
|
||||
if (n2a == end2) return false;
|
||||
}
|
||||
|
||||
iterator start = n1;
|
||||
for(;n1 != end1; ++n1)
|
||||
{
|
||||
value_type const& v = *n1;
|
||||
if (find(start, n1, v)) continue;
|
||||
std::size_t matches = count_equal(n2, end2, v);
|
||||
if (!matches) return false;
|
||||
iterator next = n1;
|
||||
++next;
|
||||
if (matches != 1 + count_equal(next, end1, v)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool find(iterator n, iterator end, value_type const& v)
|
||||
{
|
||||
for(;n != end; ++n)
|
||||
if (*n == v)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::size_t count_equal(iterator n, iterator end,
|
||||
value_type const& v)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
for(;n != end; ++n)
|
||||
if (*n == v) ++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool group_equals(iterator n1, iterator end1,
|
||||
iterator n2, iterator end2)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(!extractor::compare_mapped(*n1, *n2))
|
||||
return false;
|
||||
|
||||
++n1;
|
||||
++n2;
|
||||
|
||||
if (n1 == end1) return n2 == end2;
|
||||
if (n2 == end2) return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Emplace/Insert
|
||||
|
||||
static inline void add_after_node(
|
||||
node_pointer n,
|
||||
node_pointer pos)
|
||||
{
|
||||
n->next_ = static_cast<node_pointer>(pos->group_prev_)->next_;
|
||||
n->group_prev_ = pos->group_prev_;
|
||||
static_cast<node_pointer>(pos->group_prev_)->next_ =
|
||||
static_cast<link_pointer>(n);
|
||||
pos->group_prev_ = static_cast<link_pointer>(n);
|
||||
}
|
||||
|
||||
inline iterator add_node(
|
||||
node_constructor& a,
|
||||
std::size_t key_hash,
|
||||
iterator pos)
|
||||
{
|
||||
node_pointer n = a.release();
|
||||
n->hash_ = key_hash;
|
||||
if (pos.node_) {
|
||||
this->add_after_node(n, pos.node_);
|
||||
if (n->next_) {
|
||||
std::size_t next_bucket = policy::to_bucket(
|
||||
this->bucket_count_,
|
||||
static_cast<node_pointer>(n->next_)->hash_);
|
||||
if (next_bucket !=
|
||||
policy::to_bucket(this->bucket_count_, key_hash)) {
|
||||
this->get_bucket(next_bucket)->next_ = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bucket_pointer b = this->get_bucket(
|
||||
policy::to_bucket(this->bucket_count_, key_hash));
|
||||
|
||||
if (!b->next_)
|
||||
{
|
||||
previous_pointer start_node = this->get_previous_start();
|
||||
|
||||
if (start_node->next_) {
|
||||
this->get_bucket(policy::to_bucket(this->bucket_count_,
|
||||
static_cast<node_pointer>(start_node->next_)->hash_
|
||||
))->next_ = n;
|
||||
}
|
||||
|
||||
b->next_ = start_node;
|
||||
n->next_ = start_node->next_;
|
||||
start_node->next_ = static_cast<link_pointer>(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
n->next_ = b->next_->next_;
|
||||
b->next_->next_ = static_cast<link_pointer>(n);
|
||||
}
|
||||
}
|
||||
++this->size_;
|
||||
return iterator(n);
|
||||
}
|
||||
|
||||
iterator emplace_impl(node_constructor& a)
|
||||
{
|
||||
key_type const& k = this->get_key(a.value());
|
||||
std::size_t key_hash = this->hash(k);
|
||||
iterator position = this->find_node(key_hash, k);
|
||||
|
||||
// reserve has basic exception safety if the hash function
|
||||
// throws, strong otherwise.
|
||||
this->reserve_for_insert(this->size_ + 1);
|
||||
return this->add_node(a, key_hash, position);
|
||||
}
|
||||
|
||||
void emplace_impl_no_rehash(node_constructor& a)
|
||||
{
|
||||
key_type const& k = this->get_key(a.value());
|
||||
std::size_t key_hash = this->hash(k);
|
||||
this->add_node(a, key_hash, this->find_node(key_hash, k));
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_RVALUE_REFERENCES)
|
||||
# if defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
iterator emplace(boost::unordered::detail::emplace_args1<
|
||||
boost::unordered::detail::please_ignore_this_overload> const&)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
return iterator();
|
||||
}
|
||||
# else
|
||||
iterator emplace(
|
||||
boost::unordered::detail::please_ignore_this_overload const&)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
return iterator();
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
|
||||
iterator emplace(BOOST_UNORDERED_EMPLACE_ARGS)
|
||||
{
|
||||
node_constructor a(this->node_alloc());
|
||||
a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
|
||||
return iterator(emplace_impl(a));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Insert range methods
|
||||
|
||||
// if hash function throws, or inserting > 1 element, basic exception
|
||||
// safety. Strong otherwise
|
||||
template <class I>
|
||||
typename boost::unordered::detail::enable_if_forward<I, void>::type
|
||||
insert_range(I i, I j)
|
||||
{
|
||||
if(i == j) return;
|
||||
|
||||
std::size_t distance = boost::unordered::detail::distance(i, j);
|
||||
if(distance == 1) {
|
||||
node_constructor a(this->node_alloc());
|
||||
a.construct_with_value2(*i);
|
||||
emplace_impl(a);
|
||||
}
|
||||
else {
|
||||
// Only require basic exception safety here
|
||||
this->reserve_for_insert(this->size_ + distance);
|
||||
|
||||
node_constructor a(this->node_alloc());
|
||||
for (; i != j; ++i) {
|
||||
a.construct_with_value2(*i);
|
||||
emplace_impl_no_rehash(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class I>
|
||||
typename boost::unordered::detail::disable_if_forward<I, void>::type
|
||||
insert_range(I i, I j)
|
||||
{
|
||||
node_constructor a(this->node_alloc());
|
||||
for (; i != j; ++i) {
|
||||
a.construct_with_value2(*i);
|
||||
emplace_impl(a);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Erase
|
||||
//
|
||||
// no throw
|
||||
|
||||
std::size_t erase_key(key_type const& k)
|
||||
{
|
||||
if(!this->size_) return 0;
|
||||
|
||||
std::size_t key_hash = this->hash(k);
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, key_hash);
|
||||
bucket_pointer this_bucket = this->get_bucket(bucket_index);
|
||||
|
||||
previous_pointer prev = this_bucket->next_;
|
||||
if (!prev) return 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!prev->next_) return 0;
|
||||
std::size_t node_hash =
|
||||
static_cast<node_pointer>(prev->next_)->hash_;
|
||||
if (policy::to_bucket(this->bucket_count_, node_hash)
|
||||
!= bucket_index)
|
||||
return 0;
|
||||
if (node_hash == key_hash &&
|
||||
this->key_eq()(k, this->get_key(
|
||||
static_cast<node_pointer>(prev->next_)->value())))
|
||||
break;
|
||||
prev = static_cast<previous_pointer>(
|
||||
static_cast<node_pointer>(prev->next_)->group_prev_);
|
||||
}
|
||||
|
||||
node_pointer pos = static_cast<node_pointer>(prev->next_);
|
||||
link_pointer end1 =
|
||||
static_cast<node_pointer>(pos->group_prev_)->next_;
|
||||
node_pointer end = static_cast<node_pointer>(end1);
|
||||
prev->next_ = end1;
|
||||
this->fix_buckets(this_bucket, prev, end);
|
||||
return this->delete_nodes(c_iterator(pos), c_iterator(end));
|
||||
}
|
||||
|
||||
iterator erase(c_iterator r)
|
||||
{
|
||||
BOOST_ASSERT(r.node_);
|
||||
iterator next(r.node_);
|
||||
++next;
|
||||
|
||||
bucket_pointer this_bucket = this->get_bucket(
|
||||
policy::to_bucket(this->bucket_count_, r.node_->hash_));
|
||||
previous_pointer prev = unlink_node(*this_bucket, r.node_);
|
||||
|
||||
this->fix_buckets(this_bucket, prev, next.node_);
|
||||
|
||||
this->delete_node(r);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
iterator erase_range(c_iterator r1, c_iterator r2)
|
||||
{
|
||||
if (r1 == r2) return iterator(r2.node_);
|
||||
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, r1.node_->hash_);
|
||||
previous_pointer prev = unlink_nodes(
|
||||
*this->get_bucket(bucket_index), r1.node_, r2.node_);
|
||||
this->fix_buckets_range(bucket_index, prev, r1.node_, r2.node_);
|
||||
this->delete_nodes(r1, r2);
|
||||
|
||||
return iterator(r2.node_);
|
||||
}
|
||||
|
||||
static previous_pointer unlink_node(bucket& b, node_pointer n)
|
||||
{
|
||||
node_pointer next = static_cast<node_pointer>(n->next_);
|
||||
previous_pointer prev =
|
||||
static_cast<previous_pointer>(n->group_prev_);
|
||||
|
||||
if(prev->next_ != n) {
|
||||
// The node is at the beginning of a group.
|
||||
|
||||
// Find the previous node pointer:
|
||||
prev = b.next_;
|
||||
while(prev->next_ != n) {
|
||||
prev = static_cast<previous_pointer>(
|
||||
static_cast<node_pointer>(prev->next_)->group_prev_);
|
||||
}
|
||||
|
||||
// Remove from group
|
||||
if (next && next->group_prev_ == static_cast<link_pointer>(n))
|
||||
{
|
||||
next->group_prev_ = n->group_prev_;
|
||||
}
|
||||
}
|
||||
else if (next && next->group_prev_ == static_cast<link_pointer>(n))
|
||||
{
|
||||
// The deleted node is not at the end of the group, so
|
||||
// change the link from the next node.
|
||||
next->group_prev_ = n->group_prev_;
|
||||
}
|
||||
else {
|
||||
// The deleted node is at the end of the group, so the
|
||||
// first node in the group is pointing to it.
|
||||
// Find that to change its pointer.
|
||||
node_pointer x = static_cast<node_pointer>(n->group_prev_);
|
||||
while(x->group_prev_ != static_cast<link_pointer>(n)) {
|
||||
x = static_cast<node_pointer>(x->group_prev_);
|
||||
}
|
||||
x->group_prev_ = n->group_prev_;
|
||||
}
|
||||
|
||||
prev->next_ = static_cast<link_pointer>(next);
|
||||
return prev;
|
||||
}
|
||||
|
||||
static previous_pointer unlink_nodes(bucket& b,
|
||||
node_pointer begin, node_pointer end)
|
||||
{
|
||||
previous_pointer prev = static_cast<previous_pointer>(
|
||||
begin->group_prev_);
|
||||
|
||||
if(prev->next_ != static_cast<link_pointer>(begin)) {
|
||||
// The node is at the beginning of a group.
|
||||
|
||||
// Find the previous node pointer:
|
||||
prev = b.next_;
|
||||
while(prev->next_ != static_cast<link_pointer>(begin))
|
||||
prev = static_cast<previous_pointer>(
|
||||
static_cast<node_pointer>(prev->next_)->group_prev_);
|
||||
|
||||
if (end) split_group(end);
|
||||
}
|
||||
else {
|
||||
node_pointer group1 = split_group(begin);
|
||||
|
||||
if (end) {
|
||||
node_pointer group2 = split_group(end);
|
||||
|
||||
if(begin == group2) {
|
||||
link_pointer end1 = group1->group_prev_;
|
||||
link_pointer end2 = end->group_prev_;
|
||||
group1->group_prev_ = end2;
|
||||
end->group_prev_ = end1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prev->next_ = static_cast<link_pointer>(end);
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Break a ciruclar list into two, with split as the beginning
|
||||
// of the second group (if split is at the beginning then don't
|
||||
// split).
|
||||
static node_pointer split_group(node_pointer split)
|
||||
{
|
||||
// Find first node in group.
|
||||
node_pointer first = split;
|
||||
while (static_cast<node_pointer>(first->group_prev_)->next_ ==
|
||||
static_cast<link_pointer>(first))
|
||||
first = static_cast<node_pointer>(first->group_prev_);
|
||||
|
||||
if(first == split) return split;
|
||||
|
||||
link_pointer last = first->group_prev_;
|
||||
first->group_prev_ = split->group_prev_;
|
||||
split->group_prev_ = last;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// fill_buckets
|
||||
|
||||
template <class NodeCreator>
|
||||
static void fill_buckets(iterator n, table& dst,
|
||||
NodeCreator& creator)
|
||||
{
|
||||
previous_pointer prev = dst.get_previous_start();
|
||||
|
||||
while (n.node_) {
|
||||
std::size_t key_hash = n.node_->hash_;
|
||||
iterator group_end(
|
||||
static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(n.node_->group_prev_)->next_
|
||||
));
|
||||
|
||||
node_pointer first_node = creator.create(*n);
|
||||
node_pointer end = first_node;
|
||||
first_node->hash_ = key_hash;
|
||||
prev->next_ = static_cast<link_pointer>(first_node);
|
||||
++dst.size_;
|
||||
|
||||
for (++n; n != group_end; ++n)
|
||||
{
|
||||
end = creator.create(*n);
|
||||
end->hash_ = key_hash;
|
||||
add_after_node(end, first_node);
|
||||
++dst.size_;
|
||||
}
|
||||
|
||||
prev = place_in_bucket(dst, prev, end);
|
||||
}
|
||||
}
|
||||
|
||||
// strong otherwise exception safety
|
||||
void rehash_impl(std::size_t num_buckets)
|
||||
{
|
||||
BOOST_ASSERT(this->buckets_);
|
||||
|
||||
this->create_buckets(num_buckets);
|
||||
previous_pointer prev = this->get_previous_start();
|
||||
while (prev->next_)
|
||||
prev = place_in_bucket(*this, prev,
|
||||
static_cast<node_pointer>(
|
||||
static_cast<node_pointer>(prev->next_)->group_prev_));
|
||||
}
|
||||
|
||||
// Iterate through the nodes placing them in the correct buckets.
|
||||
// pre: prev->next_ is not null.
|
||||
static previous_pointer place_in_bucket(table& dst,
|
||||
previous_pointer prev, node_pointer end)
|
||||
{
|
||||
bucket_pointer b = dst.get_bucket(policy::to_bucket(
|
||||
dst.bucket_count_, end->hash_));
|
||||
|
||||
if (!b->next_) {
|
||||
b->next_ = static_cast<node_pointer>(prev);
|
||||
return static_cast<previous_pointer>(end);
|
||||
}
|
||||
else {
|
||||
link_pointer next = end->next_;
|
||||
end->next_ = b->next_->next_;
|
||||
b->next_->next_ = prev->next_;
|
||||
prev->next_ = next;
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,206 @@
|
||||
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_EXTRACT_KEY_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_EXTRACT_KEY_HPP_INCLUDED
|
||||
|
||||
#include <boost/unordered/detail/table.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
|
||||
// key extractors
|
||||
//
|
||||
// no throw
|
||||
//
|
||||
// 'extract_key' is called with the emplace parameters to return a
|
||||
// key if available or 'no_key' is one isn't and will need to be
|
||||
// constructed. This could be done by overloading the emplace implementation
|
||||
// for the different cases, but that's a bit tricky on compilers without
|
||||
// variadic templates.
|
||||
|
||||
struct no_key {
|
||||
no_key() {}
|
||||
template <class T> no_key(T const&) {}
|
||||
};
|
||||
|
||||
template <typename Key, typename T>
|
||||
struct is_key {
|
||||
template <typename T2>
|
||||
static choice1::type test(T2 const&);
|
||||
static choice2::type test(Key const&);
|
||||
|
||||
enum { value = sizeof(test(boost::unordered::detail::make<T>())) ==
|
||||
sizeof(choice2::type) };
|
||||
|
||||
typedef typename boost::detail::if_true<value>::
|
||||
BOOST_NESTED_TEMPLATE then<Key const&, no_key>::type type;
|
||||
};
|
||||
|
||||
template <class ValueType>
|
||||
struct set_extractor
|
||||
{
|
||||
typedef ValueType value_type;
|
||||
typedef ValueType key_type;
|
||||
|
||||
static key_type const& extract(key_type const& v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
static no_key extract()
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template <class... Args>
|
||||
static no_key extract(Args const&...)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
#else
|
||||
template <class Arg>
|
||||
static no_key extract(Arg const&)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static no_key extract(Arg1 const&, Arg2 const&)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool compare_mapped(value_type const&, value_type const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Key, class ValueType>
|
||||
struct map_extractor
|
||||
{
|
||||
typedef ValueType value_type;
|
||||
typedef typename boost::remove_const<Key>::type key_type;
|
||||
|
||||
static key_type const& extract(value_type const& v)
|
||||
{
|
||||
return v.first;
|
||||
}
|
||||
|
||||
static key_type const& extract(key_type const& v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class Second>
|
||||
static key_type const& extract(std::pair<key_type, Second> const& v)
|
||||
{
|
||||
return v.first;
|
||||
}
|
||||
|
||||
template <class Second>
|
||||
static key_type const& extract(
|
||||
std::pair<key_type const, Second> const& v)
|
||||
{
|
||||
return v.first;
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template <class Arg1, class... Args>
|
||||
static key_type const& extract(key_type const& k,
|
||||
Arg1 const&, Args const&...)
|
||||
{
|
||||
return k;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
static no_key extract(Args const&...)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
#else
|
||||
|
||||
template <class Arg1>
|
||||
static key_type const& extract(key_type const& k, Arg1 const&)
|
||||
{
|
||||
return k;
|
||||
}
|
||||
|
||||
static no_key extract()
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
static no_key extract(Arg const&)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
|
||||
template <class Arg, class Arg1>
|
||||
static no_key extract(Arg const&, Arg1 const&)
|
||||
{
|
||||
return no_key();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
|
||||
#define BOOST_UNORDERED_KEY_FROM_TUPLE(namespace_) \
|
||||
template <typename T2> \
|
||||
static no_key extract(boost::unordered::piecewise_construct_t, \
|
||||
namespace_::tuple<> const&, BOOST_FWD_REF(T2)) \
|
||||
{ \
|
||||
return no_key(); \
|
||||
} \
|
||||
\
|
||||
template <typename T, typename T2> \
|
||||
static typename is_key<key_type, T>::type \
|
||||
extract(boost::unordered::piecewise_construct_t, \
|
||||
namespace_::tuple<T> const& k, BOOST_FWD_REF(T2)) \
|
||||
{ \
|
||||
return typename is_key<key_type, T>::type( \
|
||||
namespace_::get<0>(k)); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_KEY_FROM_TUPLE(namespace_) \
|
||||
static no_key extract(boost::unordered::piecewise_construct_t, \
|
||||
namespace_::tuple<> const&) \
|
||||
{ \
|
||||
return no_key(); \
|
||||
} \
|
||||
\
|
||||
template <typename T> \
|
||||
static typename is_key<key_type, T>::type \
|
||||
extract(boost::unordered::piecewise_construct_t, \
|
||||
namespace_::tuple<T> const& k) \
|
||||
{ \
|
||||
return typename is_key<key_type, T>::type( \
|
||||
namespace_::get<0>(k)); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_UNORDERED_KEY_FROM_TUPLE(boost)
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
BOOST_UNORDERED_KEY_FROM_TUPLE(std)
|
||||
#endif
|
||||
|
||||
|
||||
static bool compare_mapped(value_type const& x, value_type const& y)
|
||||
{
|
||||
return x.second == y.second;
|
||||
}
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -1,63 +1,23 @@
|
||||
|
||||
// Copyright (C) 2008-2016 Daniel James.
|
||||
// Copyright (C) 2008-2011 Daniel James.
|
||||
// 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_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/predef.h>
|
||||
|
||||
#if defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
|
||||
// Already defined.
|
||||
#elif defined(BOOST_LIBSTDCXX11)
|
||||
// https://github.com/gcc-mirror/gcc/blob/gcc-4_6-branch/libstdc++-v3/include/bits/stl_pair.h#L70
|
||||
#if BOOST_LIBSTDCXX_VERSION > 40600
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#elif BOOST_LIB_STD_CXX
|
||||
// https://github.com/llvm-mirror/libcxx/blob/release_30/include/utility#L206
|
||||
#if BOOST_LIB_STD_CXX >= BOOST_VERSION_NUMBER(3, 0, 0)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#elif defined(BOOST_LIB_STD_DINKUMWARE)
|
||||
// Apparently C++11 standard supported in Visual Studio 2012
|
||||
// https://msdn.microsoft.com/en-us/library/hh567368.aspx#stl
|
||||
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
|
||||
// I have no idea when Dinkumware added it, probably a lot
|
||||
// earlier than this check.
|
||||
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 50, 0) || \
|
||||
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Assume that an unknown library does not support piecewise construction.
|
||||
#if !defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
using std::piecewise_construct_t;
|
||||
using std::piecewise_construct;
|
||||
#else
|
||||
struct piecewise_construct_t
|
||||
{
|
||||
};
|
||||
namespace boost
|
||||
{
|
||||
namespace unordered
|
||||
{
|
||||
struct piecewise_construct_t {};
|
||||
const piecewise_construct_t piecewise_construct = piecewise_construct_t();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#include <boost/unordered/unordered_map_fwd.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <typename A, typename K, typename M, typename H, typename P>
|
||||
struct map
|
||||
{
|
||||
typedef boost::unordered::detail::map<A, K, M, H, P> types;
|
||||
|
||||
typedef std::pair<K const, M> value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef K const const_key_type;
|
||||
|
||||
typedef
|
||||
typename ::boost::unordered::detail::rebind_wrap<A, value_type>::type
|
||||
value_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<value_allocator>
|
||||
value_allocator_traits;
|
||||
|
||||
typedef boost::unordered::detail::pick_node<A, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table<types> table;
|
||||
typedef boost::unordered::detail::map_extractor<value_type> extractor;
|
||||
|
||||
typedef typename boost::unordered::detail::pick_policy<K>::type policy;
|
||||
|
||||
typedef boost::unordered::iterator_detail::iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
|
||||
typedef boost::unordered::node_handle_map<node, K, M, A> node_type;
|
||||
typedef boost::unordered::insert_return_type_map<node, K, M, A>
|
||||
insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_map
|
||||
{
|
||||
typedef boost::unordered_map<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_multimap
|
||||
{
|
||||
typedef boost::unordered_multimap<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#include <boost/unordered/unordered_set_fwd.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <typename A, typename T, typename H, typename P> struct set
|
||||
{
|
||||
typedef boost::unordered::detail::set<A, T, H, P> types;
|
||||
|
||||
typedef T value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef T const const_key_type;
|
||||
|
||||
typedef
|
||||
typename ::boost::unordered::detail::rebind_wrap<A, value_type>::type
|
||||
value_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<value_allocator>
|
||||
value_allocator_traits;
|
||||
|
||||
typedef boost::unordered::detail::pick_node<A, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table<types> table;
|
||||
typedef boost::unordered::detail::set_extractor<value_type> extractor;
|
||||
|
||||
typedef typename boost::unordered::detail::pick_policy<T>::type policy;
|
||||
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
|
||||
typedef boost::unordered::node_handle_set<node, T, A> node_type;
|
||||
typedef boost::unordered::insert_return_type_set<node, T, A>
|
||||
insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_set
|
||||
{
|
||||
typedef boost::unordered_set<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_multiset
|
||||
{
|
||||
typedef boost::unordered_multiset<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,907 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_ALL_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_ALL_HPP_INCLUDED
|
||||
|
||||
#include <boost/unordered/detail/buckets.hpp>
|
||||
#include <boost/unordered/detail/util.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// convert double to std::size_t
|
||||
|
||||
inline std::size_t double_to_size(double f)
|
||||
{
|
||||
return f >= static_cast<double>(
|
||||
(std::numeric_limits<std::size_t>::max)()) ?
|
||||
(std::numeric_limits<std::size_t>::max)() :
|
||||
static_cast<std::size_t>(f);
|
||||
}
|
||||
|
||||
// The space used to store values in a node.
|
||||
|
||||
template <typename ValueType>
|
||||
struct value_base
|
||||
{
|
||||
typedef ValueType value_type;
|
||||
|
||||
typename boost::aligned_storage<
|
||||
sizeof(value_type),
|
||||
boost::alignment_of<value_type>::value>::type data_;
|
||||
|
||||
void* address() {
|
||||
return this;
|
||||
}
|
||||
|
||||
value_type& value() {
|
||||
return *(ValueType*) this;
|
||||
}
|
||||
|
||||
value_type* value_ptr() {
|
||||
return (ValueType*) this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
value_base& operator=(value_base const&);
|
||||
};
|
||||
|
||||
template <typename NodeAlloc>
|
||||
struct copy_nodes
|
||||
{
|
||||
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
|
||||
node_allocator_traits;
|
||||
|
||||
node_constructor<NodeAlloc> constructor;
|
||||
|
||||
explicit copy_nodes(NodeAlloc& a) : constructor(a) {}
|
||||
|
||||
typename node_allocator_traits::pointer create(
|
||||
typename node_allocator_traits::value_type::value_type const& v)
|
||||
{
|
||||
constructor.construct_with_value2(v);
|
||||
return constructor.release();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NodeAlloc>
|
||||
struct move_nodes
|
||||
{
|
||||
typedef boost::unordered::detail::allocator_traits<NodeAlloc>
|
||||
node_allocator_traits;
|
||||
|
||||
node_constructor<NodeAlloc> constructor;
|
||||
|
||||
explicit move_nodes(NodeAlloc& a) : constructor(a) {}
|
||||
|
||||
typename node_allocator_traits::pointer create(
|
||||
typename node_allocator_traits::value_type::value_type& v)
|
||||
{
|
||||
constructor.construct_with_value2(boost::move(v));
|
||||
return constructor.release();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Buckets>
|
||||
struct assign_nodes
|
||||
{
|
||||
node_holder<typename Buckets::node_allocator> holder;
|
||||
|
||||
explicit assign_nodes(Buckets& b) : holder(b) {}
|
||||
|
||||
typename Buckets::node_pointer create(
|
||||
typename Buckets::value_type const& v)
|
||||
{
|
||||
return holder.copy_of(v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Buckets>
|
||||
struct move_assign_nodes
|
||||
{
|
||||
node_holder<typename Buckets::node_allocator> holder;
|
||||
|
||||
explicit move_assign_nodes(Buckets& b) : holder(b) {}
|
||||
|
||||
typename Buckets::node_pointer create(
|
||||
typename Buckets::value_type& v)
|
||||
{
|
||||
return holder.move_copy_of(v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Types>
|
||||
struct table :
|
||||
Types::policy,
|
||||
boost::unordered::detail::functions<
|
||||
typename Types::hasher,
|
||||
typename Types::key_equal>
|
||||
{
|
||||
private:
|
||||
table(table const&);
|
||||
table& operator=(table const&);
|
||||
public:
|
||||
typedef typename Types::node node;
|
||||
typedef typename Types::bucket bucket;
|
||||
typedef typename Types::hasher hasher;
|
||||
typedef typename Types::key_equal key_equal;
|
||||
typedef typename Types::key_type key_type;
|
||||
typedef typename Types::extractor extractor;
|
||||
typedef typename Types::value_type value_type;
|
||||
typedef typename Types::table table_impl;
|
||||
typedef typename Types::link_pointer link_pointer;
|
||||
typedef typename Types::policy policy;
|
||||
|
||||
typedef boost::unordered::detail::functions<
|
||||
typename Types::hasher,
|
||||
typename Types::key_equal> functions;
|
||||
|
||||
typedef typename Types::allocator allocator;
|
||||
typedef typename boost::unordered::detail::
|
||||
rebind_wrap<allocator, node>::type node_allocator;
|
||||
typedef typename boost::unordered::detail::
|
||||
rebind_wrap<allocator, bucket>::type bucket_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<node_allocator>
|
||||
node_allocator_traits;
|
||||
typedef boost::unordered::detail::allocator_traits<bucket_allocator>
|
||||
bucket_allocator_traits;
|
||||
typedef typename node_allocator_traits::pointer
|
||||
node_pointer;
|
||||
typedef typename node_allocator_traits::const_pointer
|
||||
const_node_pointer;
|
||||
typedef typename bucket_allocator_traits::pointer
|
||||
bucket_pointer;
|
||||
typedef typename bucket::previous_pointer
|
||||
previous_pointer;
|
||||
typedef boost::unordered::detail::node_constructor<node_allocator>
|
||||
node_constructor;
|
||||
|
||||
typedef boost::unordered::iterator_detail::
|
||||
iterator<node_pointer, value_type> iterator;
|
||||
typedef boost::unordered::iterator_detail::
|
||||
c_iterator<const_node_pointer, node_pointer, value_type> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::
|
||||
l_iterator<node_pointer, value_type, policy> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::
|
||||
cl_iterator<const_node_pointer, node_pointer, value_type, policy>
|
||||
cl_iterator;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Members
|
||||
|
||||
boost::unordered::detail::compressed<bucket_allocator, node_allocator>
|
||||
allocators_;
|
||||
std::size_t bucket_count_;
|
||||
std::size_t size_;
|
||||
float mlf_;
|
||||
std::size_t max_load_;
|
||||
bucket_pointer buckets_;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Data access
|
||||
|
||||
bucket_allocator const& bucket_alloc() const
|
||||
{
|
||||
return allocators_.first();
|
||||
}
|
||||
|
||||
node_allocator const& node_alloc() const
|
||||
{
|
||||
return allocators_.second();
|
||||
}
|
||||
|
||||
bucket_allocator& bucket_alloc()
|
||||
{
|
||||
return allocators_.first();
|
||||
}
|
||||
|
||||
node_allocator& node_alloc()
|
||||
{
|
||||
return allocators_.second();
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const
|
||||
{
|
||||
// -1 to account for the start bucket.
|
||||
return policy::prev_bucket_count(
|
||||
bucket_allocator_traits::max_size(bucket_alloc()) - 1);
|
||||
}
|
||||
|
||||
bucket_pointer get_bucket(std::size_t bucket_index) const
|
||||
{
|
||||
BOOST_ASSERT(buckets_);
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(bucket_index);
|
||||
}
|
||||
|
||||
previous_pointer get_previous_start() const
|
||||
{
|
||||
return get_bucket(bucket_count_)->first_from_start();
|
||||
}
|
||||
|
||||
previous_pointer get_previous_start(std::size_t bucket_index) const
|
||||
{
|
||||
return get_bucket(bucket_index)->next_;
|
||||
}
|
||||
|
||||
iterator begin() const
|
||||
{
|
||||
return size_ ? iterator(static_cast<node_pointer>(
|
||||
get_previous_start()->next_)) : iterator();
|
||||
}
|
||||
|
||||
iterator begin(std::size_t bucket_index) const
|
||||
{
|
||||
if (!size_) return iterator();
|
||||
previous_pointer prev = get_previous_start(bucket_index);
|
||||
return prev ? iterator(static_cast<node_pointer>(prev->next_)) :
|
||||
iterator();
|
||||
}
|
||||
|
||||
float load_factor() const
|
||||
{
|
||||
BOOST_ASSERT(bucket_count_ != 0);
|
||||
return static_cast<float>(size_)
|
||||
/ static_cast<float>(bucket_count_);
|
||||
}
|
||||
|
||||
std::size_t bucket_size(std::size_t index) const
|
||||
{
|
||||
iterator it = begin(index);
|
||||
if (!it.node_) return 0;
|
||||
|
||||
std::size_t count = 0;
|
||||
while(it.node_ && policy::to_bucket(
|
||||
bucket_count_, it.node_->hash_) == index)
|
||||
{
|
||||
++count;
|
||||
++it;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Load methods
|
||||
|
||||
std::size_t max_size() const
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
// size < mlf_ * count
|
||||
return boost::unordered::detail::double_to_size(ceil(
|
||||
static_cast<double>(mlf_) *
|
||||
static_cast<double>(max_bucket_count())
|
||||
)) - 1;
|
||||
}
|
||||
|
||||
void recalculate_max_load()
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
// From 6.3.1/13:
|
||||
// Only resize when size >= mlf_ * count
|
||||
max_load_ = buckets_ ? boost::unordered::detail::double_to_size(ceil(
|
||||
static_cast<double>(mlf_) *
|
||||
static_cast<double>(bucket_count_)
|
||||
)) : 0;
|
||||
|
||||
}
|
||||
|
||||
void max_load_factor(float z)
|
||||
{
|
||||
BOOST_ASSERT(z > 0);
|
||||
mlf_ = (std::max)(z, minimum_max_load_factor);
|
||||
recalculate_max_load();
|
||||
}
|
||||
|
||||
std::size_t min_buckets_for_size(std::size_t size) const
|
||||
{
|
||||
BOOST_ASSERT(mlf_ >= minimum_max_load_factor);
|
||||
|
||||
using namespace std;
|
||||
|
||||
// From 6.3.1/13:
|
||||
// size < mlf_ * count
|
||||
// => count > size / mlf_
|
||||
//
|
||||
// Or from rehash post-condition:
|
||||
// count > size / mlf_
|
||||
|
||||
return policy::new_bucket_count(
|
||||
boost::unordered::detail::double_to_size(floor(
|
||||
static_cast<double>(size) /
|
||||
static_cast<double>(mlf_))) + 1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Constructors
|
||||
|
||||
table(std::size_t num_buckets,
|
||||
hasher const& hf,
|
||||
key_equal const& eq,
|
||||
node_allocator const& a) :
|
||||
functions(hf, eq),
|
||||
allocators_(a,a),
|
||||
bucket_count_(policy::new_bucket_count(num_buckets)),
|
||||
size_(0),
|
||||
mlf_(1.0f),
|
||||
max_load_(0),
|
||||
buckets_()
|
||||
{}
|
||||
|
||||
table(table const& x, node_allocator const& a) :
|
||||
functions(x),
|
||||
allocators_(a,a),
|
||||
bucket_count_(x.min_buckets_for_size(x.size_)),
|
||||
size_(0),
|
||||
mlf_(x.mlf_),
|
||||
max_load_(0),
|
||||
buckets_()
|
||||
{}
|
||||
|
||||
table(table& x, boost::unordered::detail::move_tag m) :
|
||||
functions(x),
|
||||
allocators_(x.allocators_, m),
|
||||
bucket_count_(x.bucket_count_),
|
||||
size_(x.size_),
|
||||
mlf_(x.mlf_),
|
||||
max_load_(x.max_load_),
|
||||
buckets_(x.buckets_)
|
||||
{
|
||||
x.buckets_ = bucket_pointer();
|
||||
x.size_ = 0;
|
||||
x.max_load_ = 0;
|
||||
}
|
||||
|
||||
table(table& x, node_allocator const& a,
|
||||
boost::unordered::detail::move_tag) :
|
||||
functions(x),
|
||||
allocators_(a, a),
|
||||
bucket_count_(x.bucket_count_),
|
||||
size_(0),
|
||||
mlf_(x.mlf_),
|
||||
max_load_(x.max_load_),
|
||||
buckets_()
|
||||
{}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Initialisation.
|
||||
|
||||
void init(table const& x)
|
||||
{
|
||||
if (x.size_) {
|
||||
create_buckets(bucket_count_);
|
||||
copy_nodes<node_allocator> copy(node_alloc());
|
||||
table_impl::fill_buckets(x.begin(), *this, copy);
|
||||
}
|
||||
}
|
||||
|
||||
void move_init(table& x)
|
||||
{
|
||||
if(node_alloc() == x.node_alloc()) {
|
||||
move_buckets_from(x);
|
||||
}
|
||||
else if(x.size_) {
|
||||
// TODO: Could pick new bucket size?
|
||||
create_buckets(bucket_count_);
|
||||
|
||||
move_nodes<node_allocator> move(node_alloc());
|
||||
node_holder<node_allocator> nodes(x);
|
||||
table_impl::fill_buckets(nodes.begin(), *this, move);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Create buckets
|
||||
|
||||
void create_buckets(std::size_t new_count)
|
||||
{
|
||||
boost::unordered::detail::array_constructor<bucket_allocator>
|
||||
constructor(bucket_alloc());
|
||||
|
||||
// Creates an extra bucket to act as the start node.
|
||||
constructor.construct(bucket(), new_count + 1);
|
||||
|
||||
if (buckets_)
|
||||
{
|
||||
// Copy the nodes to the new buckets, including the dummy
|
||||
// node if there is one.
|
||||
(constructor.get() +
|
||||
static_cast<std::ptrdiff_t>(new_count))->next_ =
|
||||
(buckets_ + static_cast<std::ptrdiff_t>(
|
||||
bucket_count_))->next_;
|
||||
destroy_buckets();
|
||||
}
|
||||
else if (bucket::extra_node)
|
||||
{
|
||||
node_constructor a(node_alloc());
|
||||
a.construct();
|
||||
|
||||
(constructor.get() +
|
||||
static_cast<std::ptrdiff_t>(new_count))->next_ =
|
||||
a.release();
|
||||
}
|
||||
|
||||
bucket_count_ = new_count;
|
||||
buckets_ = constructor.release();
|
||||
recalculate_max_load();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Swap and Move
|
||||
|
||||
void swap_allocators(table& other, false_type)
|
||||
{
|
||||
// According to 23.2.1.8, if propagate_on_container_swap is
|
||||
// false the behaviour is undefined unless the allocators
|
||||
// are equal.
|
||||
BOOST_ASSERT(node_alloc() == other.node_alloc());
|
||||
}
|
||||
|
||||
void swap_allocators(table& other, true_type)
|
||||
{
|
||||
allocators_.swap(other.allocators_);
|
||||
}
|
||||
|
||||
// Only swaps the allocators if propagate_on_container_swap
|
||||
void swap(table& x)
|
||||
{
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
op1(*this, x);
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
op2(x, *this);
|
||||
|
||||
// I think swap can throw if Propagate::value,
|
||||
// since the allocators' swap can throw. Not sure though.
|
||||
swap_allocators(x,
|
||||
boost::unordered::detail::integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_swap::value>());
|
||||
|
||||
boost::swap(buckets_, x.buckets_);
|
||||
boost::swap(bucket_count_, x.bucket_count_);
|
||||
boost::swap(size_, x.size_);
|
||||
std::swap(mlf_, x.mlf_);
|
||||
std::swap(max_load_, x.max_load_);
|
||||
op1.commit();
|
||||
op2.commit();
|
||||
}
|
||||
|
||||
void move_buckets_from(table& other)
|
||||
{
|
||||
BOOST_ASSERT(node_alloc() == other.node_alloc());
|
||||
BOOST_ASSERT(!buckets_);
|
||||
buckets_ = other.buckets_;
|
||||
bucket_count_ = other.bucket_count_;
|
||||
size_ = other.size_;
|
||||
other.buckets_ = bucket_pointer();
|
||||
other.size_ = 0;
|
||||
other.max_load_ = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Delete/destruct
|
||||
|
||||
~table()
|
||||
{
|
||||
delete_buckets();
|
||||
}
|
||||
|
||||
void delete_node(c_iterator n)
|
||||
{
|
||||
boost::unordered::detail::destroy_value_impl(node_alloc(),
|
||||
n.node_->value_ptr());
|
||||
node_allocator_traits::destroy(node_alloc(),
|
||||
boost::addressof(*n.node_));
|
||||
node_allocator_traits::deallocate(node_alloc(), n.node_, 1);
|
||||
--size_;
|
||||
}
|
||||
|
||||
std::size_t delete_nodes(c_iterator begin, c_iterator end)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
|
||||
while(begin != end) {
|
||||
c_iterator n = begin;
|
||||
++begin;
|
||||
delete_node(n);
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void delete_buckets()
|
||||
{
|
||||
if(buckets_) {
|
||||
delete_nodes(begin(), iterator());
|
||||
|
||||
if (bucket::extra_node) {
|
||||
node_pointer n = static_cast<node_pointer>(
|
||||
get_bucket(bucket_count_)->next_);
|
||||
node_allocator_traits::destroy(node_alloc(),
|
||||
boost::addressof(*n));
|
||||
node_allocator_traits::deallocate(node_alloc(), n, 1);
|
||||
}
|
||||
|
||||
destroy_buckets();
|
||||
buckets_ = bucket_pointer();
|
||||
max_load_ = 0;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(!size_);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if(!size_) return;
|
||||
|
||||
delete_nodes(begin(), iterator());
|
||||
get_previous_start()->next_ = link_pointer();
|
||||
clear_buckets();
|
||||
|
||||
BOOST_ASSERT(!size_);
|
||||
}
|
||||
|
||||
void clear_buckets()
|
||||
{
|
||||
bucket_pointer end = get_bucket(bucket_count_);
|
||||
for(bucket_pointer it = buckets_; it != end; ++it)
|
||||
{
|
||||
it->next_ = node_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
void destroy_buckets()
|
||||
{
|
||||
bucket_pointer end = get_bucket(bucket_count_ + 1);
|
||||
for(bucket_pointer it = buckets_; it != end; ++it)
|
||||
{
|
||||
bucket_allocator_traits::destroy(bucket_alloc(),
|
||||
boost::addressof(*it));
|
||||
}
|
||||
|
||||
bucket_allocator_traits::deallocate(bucket_alloc(),
|
||||
buckets_, bucket_count_ + 1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Fix buckets after erase
|
||||
|
||||
// This is called after erasing a node or group of nodes to fix up
|
||||
// the bucket pointers.
|
||||
void fix_buckets(bucket_pointer this_bucket,
|
||||
previous_pointer prev, node_pointer next)
|
||||
{
|
||||
if (!next)
|
||||
{
|
||||
if (this_bucket->next_ == prev)
|
||||
this_bucket->next_ = node_pointer();
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket_pointer next_bucket = get_bucket(
|
||||
policy::to_bucket(bucket_count_, next->hash_));
|
||||
|
||||
if (next_bucket != this_bucket)
|
||||
{
|
||||
next_bucket->next_ = prev;
|
||||
if (this_bucket->next_ == prev)
|
||||
this_bucket->next_ = node_pointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is called after erasing a range of nodes to fix any bucket
|
||||
// pointers into that range.
|
||||
void fix_buckets_range(std::size_t bucket_index,
|
||||
previous_pointer prev, node_pointer begin, node_pointer end)
|
||||
{
|
||||
node_pointer n = begin;
|
||||
|
||||
// If we're not at the start of the current bucket, then
|
||||
// go to the start of the next bucket.
|
||||
if (get_bucket(bucket_index)->next_ != prev)
|
||||
{
|
||||
for(;;) {
|
||||
n = static_cast<node_pointer>(n->next_);
|
||||
if (n == end) {
|
||||
if (n) {
|
||||
std::size_t new_bucket_index =
|
||||
policy::to_bucket(bucket_count_, n->hash_);
|
||||
if (bucket_index != new_bucket_index) {
|
||||
get_bucket(new_bucket_index)->next_ = prev;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t new_bucket_index =
|
||||
policy::to_bucket(bucket_count_, n->hash_);
|
||||
if (bucket_index != new_bucket_index) {
|
||||
bucket_index = new_bucket_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through the remaining nodes, clearing out the bucket
|
||||
// pointers.
|
||||
get_bucket(bucket_index)->next_ = previous_pointer();
|
||||
for(;;) {
|
||||
n = static_cast<node_pointer>(n->next_);
|
||||
if (n == end) break;
|
||||
|
||||
std::size_t new_bucket_index =
|
||||
policy::to_bucket(bucket_count_, n->hash_);
|
||||
if (bucket_index != new_bucket_index) {
|
||||
bucket_index = new_bucket_index;
|
||||
get_bucket(bucket_index)->next_ = previous_pointer();
|
||||
}
|
||||
};
|
||||
|
||||
// Finally fix the bucket containing the trailing node.
|
||||
if (n) {
|
||||
get_bucket(
|
||||
policy::to_bucket(bucket_count_, n->hash_))->next_
|
||||
= prev;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Assignment
|
||||
|
||||
void assign(table const& x)
|
||||
{
|
||||
if (this != boost::addressof(x))
|
||||
{
|
||||
assign(x,
|
||||
boost::unordered::detail::integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_copy_assignment::value>());
|
||||
}
|
||||
}
|
||||
|
||||
void assign(table const& x, false_type)
|
||||
{
|
||||
// Strong exception safety.
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
new_func_this(*this, x);
|
||||
new_func_this.commit();
|
||||
mlf_ = x.mlf_;
|
||||
recalculate_max_load();
|
||||
|
||||
if (!size_ && !x.size_) return;
|
||||
|
||||
if (x.size_ >= max_load_) {
|
||||
create_buckets(min_buckets_for_size(x.size_));
|
||||
}
|
||||
else {
|
||||
clear_buckets();
|
||||
}
|
||||
|
||||
// assign_nodes takes ownership of the container's elements,
|
||||
// assigning to them if possible, and deleting any that are
|
||||
// left over.
|
||||
assign_nodes<table> assign(*this);
|
||||
table_impl::fill_buckets(x.begin(), *this, assign);
|
||||
}
|
||||
|
||||
void assign(table const& x, true_type)
|
||||
{
|
||||
if (node_alloc() == x.node_alloc()) {
|
||||
allocators_.assign(x.allocators_);
|
||||
assign(x, false_type());
|
||||
}
|
||||
else {
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
new_func_this(*this, x);
|
||||
|
||||
// Delete everything with current allocators before assigning
|
||||
// the new ones.
|
||||
delete_buckets();
|
||||
allocators_.assign(x.allocators_);
|
||||
|
||||
// Copy over other data, all no throw.
|
||||
new_func_this.commit();
|
||||
mlf_ = x.mlf_;
|
||||
bucket_count_ = min_buckets_for_size(x.size_);
|
||||
max_load_ = 0;
|
||||
|
||||
// Finally copy the elements.
|
||||
if (x.size_) {
|
||||
create_buckets(bucket_count_);
|
||||
copy_nodes<node_allocator> copy(node_alloc());
|
||||
table_impl::fill_buckets(x.begin(), *this, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void move_assign(table& x)
|
||||
{
|
||||
if (this != boost::addressof(x))
|
||||
{
|
||||
move_assign(x,
|
||||
boost::unordered::detail::integral_constant<bool,
|
||||
allocator_traits<node_allocator>::
|
||||
propagate_on_container_move_assignment::value>());
|
||||
}
|
||||
}
|
||||
|
||||
void move_assign(table& x, true_type)
|
||||
{
|
||||
delete_buckets();
|
||||
allocators_.move_assign(x.allocators_);
|
||||
move_assign_no_alloc(x);
|
||||
}
|
||||
|
||||
void move_assign(table& x, false_type)
|
||||
{
|
||||
if (node_alloc() == x.node_alloc()) {
|
||||
delete_buckets();
|
||||
move_assign_no_alloc(x);
|
||||
}
|
||||
else {
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
new_func_this(*this, x);
|
||||
new_func_this.commit();
|
||||
mlf_ = x.mlf_;
|
||||
recalculate_max_load();
|
||||
|
||||
if (!size_ && !x.size_) return;
|
||||
|
||||
if (x.size_ >= max_load_) {
|
||||
create_buckets(min_buckets_for_size(x.size_));
|
||||
}
|
||||
else {
|
||||
clear_buckets();
|
||||
}
|
||||
|
||||
// move_assign_nodes takes ownership of the container's
|
||||
// elements, assigning to them if possible, and deleting
|
||||
// any that are left over.
|
||||
move_assign_nodes<table> assign(*this);
|
||||
node_holder<node_allocator> nodes(x);
|
||||
table_impl::fill_buckets(nodes.begin(), *this, assign);
|
||||
}
|
||||
}
|
||||
|
||||
void move_assign_no_alloc(table& x)
|
||||
{
|
||||
boost::unordered::detail::set_hash_functions<hasher, key_equal>
|
||||
new_func_this(*this, x);
|
||||
// No throw from here.
|
||||
mlf_ = x.mlf_;
|
||||
max_load_ = x.max_load_;
|
||||
move_buckets_from(x);
|
||||
new_func_this.commit();
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
key_type const& get_key(value_type const& x) const
|
||||
{
|
||||
return extractor::extract(x);
|
||||
}
|
||||
|
||||
std::size_t hash(key_type const& k) const
|
||||
{
|
||||
return policy::apply_hash(this->hash_function(), k);
|
||||
}
|
||||
|
||||
// Find Node
|
||||
|
||||
template <typename Key, typename Hash, typename Pred>
|
||||
iterator generic_find_node(
|
||||
Key const& k,
|
||||
Hash const& hf,
|
||||
Pred const& eq) const
|
||||
{
|
||||
return static_cast<table_impl const*>(this)->
|
||||
find_node_impl(policy::apply_hash(hf, k), k, eq);
|
||||
}
|
||||
|
||||
iterator find_node(
|
||||
std::size_t key_hash,
|
||||
key_type const& k) const
|
||||
{
|
||||
return static_cast<table_impl const*>(this)->
|
||||
find_node_impl(key_hash, k, this->key_eq());
|
||||
}
|
||||
|
||||
iterator find_node(key_type const& k) const
|
||||
{
|
||||
return static_cast<table_impl const*>(this)->
|
||||
find_node_impl(hash(k), k, this->key_eq());
|
||||
}
|
||||
|
||||
iterator find_matching_node(iterator n) const
|
||||
{
|
||||
// TODO: Does this apply to C++11?
|
||||
//
|
||||
// For some stupid reason, I decided to support equality comparison
|
||||
// when different hash functions are used. So I can't use the hash
|
||||
// value from the node here.
|
||||
|
||||
return find_node(get_key(*n));
|
||||
}
|
||||
|
||||
// Reserve and rehash
|
||||
|
||||
void reserve_for_insert(std::size_t);
|
||||
void rehash(std::size_t);
|
||||
void reserve(std::size_t);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Reserve & Rehash
|
||||
|
||||
// basic exception safety
|
||||
template <typename Types>
|
||||
inline void table<Types>::reserve_for_insert(std::size_t size)
|
||||
{
|
||||
if (!buckets_) {
|
||||
create_buckets((std::max)(bucket_count_,
|
||||
min_buckets_for_size(size)));
|
||||
}
|
||||
// According to the standard this should be 'size >= max_load_',
|
||||
// but I think this is better, defect report filed.
|
||||
else if(size > max_load_) {
|
||||
std::size_t num_buckets
|
||||
= min_buckets_for_size((std::max)(size,
|
||||
size_ + (size_ >> 1)));
|
||||
|
||||
if (num_buckets != bucket_count_)
|
||||
static_cast<table_impl*>(this)->rehash_impl(num_buckets);
|
||||
}
|
||||
}
|
||||
|
||||
// if hash function throws, basic exception safety
|
||||
// strong otherwise.
|
||||
|
||||
template <typename Types>
|
||||
inline void table<Types>::rehash(std::size_t min_buckets)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
if(!size_) {
|
||||
delete_buckets();
|
||||
bucket_count_ = policy::new_bucket_count(min_buckets);
|
||||
}
|
||||
else {
|
||||
min_buckets = policy::new_bucket_count((std::max)(min_buckets,
|
||||
boost::unordered::detail::double_to_size(floor(
|
||||
static_cast<double>(size_) /
|
||||
static_cast<double>(mlf_))) + 1));
|
||||
|
||||
if(min_buckets != bucket_count_)
|
||||
static_cast<table_impl*>(this)->rehash_impl(min_buckets);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Types>
|
||||
inline void table<Types>::reserve(std::size_t num_elements)
|
||||
{
|
||||
rehash(static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>(num_elements) / mlf_)));
|
||||
}
|
||||
}}}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,651 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/table.hpp>
|
||||
#include <boost/unordered/detail/extract_key.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
template <typename A, typename T> struct unique_node;
|
||||
template <typename T> struct ptr_node;
|
||||
template <typename Types> struct table_impl;
|
||||
|
||||
template <typename A, typename T>
|
||||
struct unique_node :
|
||||
boost::unordered::detail::value_base<T>
|
||||
{
|
||||
typedef typename ::boost::unordered::detail::rebind_wrap<
|
||||
A, unique_node<A, T> >::type::pointer link_pointer;
|
||||
|
||||
link_pointer next_;
|
||||
std::size_t hash_;
|
||||
|
||||
unique_node() :
|
||||
next_(),
|
||||
hash_(0)
|
||||
{}
|
||||
|
||||
void init(link_pointer)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
unique_node& operator=(unique_node const&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ptr_node :
|
||||
boost::unordered::detail::value_base<T>,
|
||||
boost::unordered::detail::ptr_bucket
|
||||
{
|
||||
typedef boost::unordered::detail::ptr_bucket bucket_base;
|
||||
typedef ptr_bucket* link_pointer;
|
||||
|
||||
std::size_t hash_;
|
||||
|
||||
ptr_node() :
|
||||
bucket_base(),
|
||||
hash_(0)
|
||||
{}
|
||||
|
||||
void init(link_pointer)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ptr_node& operator=(ptr_node const&);
|
||||
};
|
||||
|
||||
// If the allocator uses raw pointers use ptr_node
|
||||
// Otherwise use node.
|
||||
|
||||
template <typename A, typename T, typename NodePtr, typename BucketPtr>
|
||||
struct pick_node2
|
||||
{
|
||||
typedef boost::unordered::detail::unique_node<A, T> node;
|
||||
|
||||
typedef typename boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A, node>::type
|
||||
>::pointer node_pointer;
|
||||
|
||||
typedef boost::unordered::detail::bucket<node_pointer> bucket;
|
||||
typedef node_pointer link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T>
|
||||
struct pick_node2<A, T,
|
||||
boost::unordered::detail::ptr_node<T>*,
|
||||
boost::unordered::detail::ptr_bucket*>
|
||||
{
|
||||
typedef boost::unordered::detail::ptr_node<T> node;
|
||||
typedef boost::unordered::detail::ptr_bucket bucket;
|
||||
typedef bucket* link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T>
|
||||
struct pick_node
|
||||
{
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::ptr_node<T> >::type
|
||||
> tentative_node_traits;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::ptr_bucket >::type
|
||||
> tentative_bucket_traits;
|
||||
|
||||
typedef pick_node2<A, T,
|
||||
typename tentative_node_traits::pointer,
|
||||
typename tentative_bucket_traits::pointer> pick;
|
||||
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
};
|
||||
|
||||
template <typename A, typename T, typename H, typename P>
|
||||
struct set
|
||||
{
|
||||
typedef boost::unordered::detail::set<A, T, H, P> types;
|
||||
|
||||
typedef A allocator;
|
||||
typedef T value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef T key_type;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<allocator> traits;
|
||||
typedef boost::unordered::detail::pick_node<allocator, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table_impl<types> table;
|
||||
typedef boost::unordered::detail::set_extractor<value_type> extractor;
|
||||
|
||||
typedef boost::unordered::detail::pick_policy::type policy;
|
||||
};
|
||||
|
||||
template <typename A, typename K, typename M, typename H, typename P>
|
||||
struct map
|
||||
{
|
||||
typedef boost::unordered::detail::map<A, K, M, H, P> types;
|
||||
|
||||
typedef A allocator;
|
||||
typedef std::pair<K const, M> value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef K key_type;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<allocator>
|
||||
traits;
|
||||
typedef boost::unordered::detail::pick_node<allocator, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table_impl<types> table;
|
||||
typedef boost::unordered::detail::map_extractor<key_type, value_type>
|
||||
extractor;
|
||||
|
||||
typedef boost::unordered::detail::pick_policy::type policy;
|
||||
};
|
||||
|
||||
template <typename Types>
|
||||
struct table_impl : boost::unordered::detail::table<Types>
|
||||
{
|
||||
typedef boost::unordered::detail::table<Types> table;
|
||||
typedef typename table::value_type value_type;
|
||||
typedef typename table::bucket bucket;
|
||||
typedef typename table::policy policy;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::node_allocator node_allocator;
|
||||
typedef typename table::node_allocator_traits node_allocator_traits;
|
||||
typedef typename table::bucket_pointer bucket_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
typedef typename table::previous_pointer previous_pointer;
|
||||
typedef typename table::hasher hasher;
|
||||
typedef typename table::key_equal key_equal;
|
||||
typedef typename table::key_type key_type;
|
||||
typedef typename table::node_constructor node_constructor;
|
||||
typedef typename table::extractor extractor;
|
||||
typedef typename table::iterator iterator;
|
||||
typedef typename table::c_iterator c_iterator;
|
||||
|
||||
typedef std::pair<iterator, bool> emplace_return;
|
||||
|
||||
// Constructors
|
||||
|
||||
table_impl(std::size_t n,
|
||||
hasher const& hf,
|
||||
key_equal const& eq,
|
||||
node_allocator const& a)
|
||||
: table(n, hf, eq, a)
|
||||
{}
|
||||
|
||||
table_impl(table_impl const& x)
|
||||
: table(x, node_allocator_traits::
|
||||
select_on_container_copy_construction(x.node_alloc()))
|
||||
{
|
||||
this->init(x);
|
||||
}
|
||||
|
||||
table_impl(table_impl const& x,
|
||||
node_allocator const& a)
|
||||
: table(x, a)
|
||||
{
|
||||
this->init(x);
|
||||
}
|
||||
|
||||
table_impl(table_impl& x,
|
||||
boost::unordered::detail::move_tag m)
|
||||
: table(x, m)
|
||||
{}
|
||||
|
||||
table_impl(table_impl& x,
|
||||
node_allocator const& a,
|
||||
boost::unordered::detail::move_tag m)
|
||||
: table(x, a, m)
|
||||
{
|
||||
this->move_init(x);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
template <class Key, class Pred>
|
||||
iterator find_node_impl(
|
||||
std::size_t key_hash,
|
||||
Key const& k,
|
||||
Pred const& eq) const
|
||||
{
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, key_hash);
|
||||
iterator n = this->begin(bucket_index);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!n.node_) return n;
|
||||
|
||||
std::size_t node_hash = n.node_->hash_;
|
||||
if (key_hash == node_hash)
|
||||
{
|
||||
if (eq(k, this->get_key(*n)))
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (policy::to_bucket(this->bucket_count_, node_hash)
|
||||
!= bucket_index)
|
||||
return iterator();
|
||||
}
|
||||
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t count(key_type const& k) const
|
||||
{
|
||||
return this->find_node(k).node_ ? 1 : 0;
|
||||
}
|
||||
|
||||
value_type& at(key_type const& k) const
|
||||
{
|
||||
if (this->size_) {
|
||||
iterator it = this->find_node(k);
|
||||
if (it.node_) return *it;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
std::out_of_range("Unable to find key in unordered_map."));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(key_type const& k) const
|
||||
{
|
||||
iterator n = this->find_node(k);
|
||||
iterator n2 = n;
|
||||
if (n2.node_) ++n2;
|
||||
return std::make_pair(n, n2);
|
||||
}
|
||||
|
||||
// equals
|
||||
|
||||
bool equals(table_impl const& other) const
|
||||
{
|
||||
if(this->size_ != other.size_) return false;
|
||||
|
||||
for(iterator n1 = this->begin(); n1.node_; ++n1)
|
||||
{
|
||||
iterator n2 = other.find_matching_node(n1);
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DEPRECATED_EQUALITY)
|
||||
if (!n2.node_ || *n1 != *n2)
|
||||
return false;
|
||||
#else
|
||||
if (!n2.node_ || !extractor::compare_mapped(*n1, *n2))
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Emplace/Insert
|
||||
|
||||
inline iterator add_node(
|
||||
node_constructor& a,
|
||||
std::size_t key_hash)
|
||||
{
|
||||
node_pointer n = a.release();
|
||||
n->hash_ = key_hash;
|
||||
|
||||
bucket_pointer b = this->get_bucket(
|
||||
policy::to_bucket(this->bucket_count_, key_hash));
|
||||
|
||||
if (!b->next_)
|
||||
{
|
||||
previous_pointer start_node = this->get_previous_start();
|
||||
|
||||
if (start_node->next_) {
|
||||
this->get_bucket(policy::to_bucket(this->bucket_count_,
|
||||
static_cast<node_pointer>(start_node->next_)->hash_)
|
||||
)->next_ = n;
|
||||
}
|
||||
|
||||
b->next_ = start_node;
|
||||
n->next_ = start_node->next_;
|
||||
start_node->next_ = static_cast<link_pointer>(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
n->next_ = b->next_->next_;
|
||||
b->next_->next_ = static_cast<link_pointer>(n);
|
||||
}
|
||||
|
||||
++this->size_;
|
||||
return iterator(n);
|
||||
}
|
||||
|
||||
value_type& operator[](key_type const& k)
|
||||
{
|
||||
typedef typename value_type::second_type mapped_type;
|
||||
|
||||
std::size_t key_hash = this->hash(k);
|
||||
iterator pos = this->find_node(key_hash, k);
|
||||
|
||||
if (pos.node_) return *pos;
|
||||
|
||||
// Create the node before rehashing in case it throws an
|
||||
// exception (need strong safety in such a case).
|
||||
node_constructor a(this->node_alloc());
|
||||
a.construct_with_value(BOOST_UNORDERED_EMPLACE_ARGS3(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(k),
|
||||
boost::make_tuple()));
|
||||
|
||||
this->reserve_for_insert(this->size_ + 1);
|
||||
return *add_node(a, key_hash);
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_RVALUE_REFERENCES)
|
||||
# if defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
emplace_return emplace(boost::unordered::detail::emplace_args1<
|
||||
boost::unordered::detail::please_ignore_this_overload> const&)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
return emplace_return(this->begin(), false);
|
||||
}
|
||||
# else
|
||||
emplace_return emplace(
|
||||
boost::unordered::detail::please_ignore_this_overload const&)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
return emplace_return(this->begin(), false);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
|
||||
emplace_return emplace(BOOST_UNORDERED_EMPLACE_ARGS)
|
||||
{
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
return emplace_impl(
|
||||
extractor::extract(BOOST_UNORDERED_EMPLACE_FORWARD),
|
||||
BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
#else
|
||||
return emplace_impl(
|
||||
extractor::extract(args.a0, args.a1),
|
||||
BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template <typename A0>
|
||||
emplace_return emplace(
|
||||
boost::unordered::detail::emplace_args1<A0> const& args)
|
||||
{
|
||||
return emplace_impl(extractor::extract(args.a0), args);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
|
||||
emplace_return emplace_impl(key_type const& k,
|
||||
BOOST_UNORDERED_EMPLACE_ARGS)
|
||||
{
|
||||
std::size_t key_hash = this->hash(k);
|
||||
iterator pos = this->find_node(key_hash, k);
|
||||
|
||||
if (pos.node_) return emplace_return(pos, false);
|
||||
|
||||
// Create the node before rehashing in case it throws an
|
||||
// exception (need strong safety in such a case).
|
||||
node_constructor a(this->node_alloc());
|
||||
a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
|
||||
// reserve has basic exception safety if the hash function
|
||||
// throws, strong otherwise.
|
||||
this->reserve_for_insert(this->size_ + 1);
|
||||
return emplace_return(this->add_node(a, key_hash), true);
|
||||
}
|
||||
|
||||
emplace_return emplace_impl_with_node(node_constructor& a)
|
||||
{
|
||||
key_type const& k = this->get_key(a.value());
|
||||
std::size_t key_hash = this->hash(k);
|
||||
iterator pos = this->find_node(key_hash, k);
|
||||
|
||||
if (pos.node_) return emplace_return(pos, false);
|
||||
|
||||
// reserve has basic exception safety if the hash function
|
||||
// throws, strong otherwise.
|
||||
this->reserve_for_insert(this->size_ + 1);
|
||||
return emplace_return(this->add_node(a, key_hash), true);
|
||||
}
|
||||
|
||||
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
|
||||
emplace_return emplace_impl(no_key, BOOST_UNORDERED_EMPLACE_ARGS)
|
||||
{
|
||||
// Don't have a key, so construct the node first in order
|
||||
// to be able to lookup the position.
|
||||
node_constructor a(this->node_alloc());
|
||||
a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD);
|
||||
return emplace_impl_with_node(a);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Insert range methods
|
||||
//
|
||||
// if hash function throws, or inserting > 1 element, basic exception
|
||||
// safety strong otherwise
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range(InputIt i, InputIt j)
|
||||
{
|
||||
if(i != j)
|
||||
return insert_range_impl(extractor::extract(*i), i, j);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range_impl(key_type const& k, InputIt i, InputIt j)
|
||||
{
|
||||
node_constructor a(this->node_alloc());
|
||||
|
||||
insert_range_impl2(a, k, i, j);
|
||||
|
||||
while(++i != j) {
|
||||
// Note: can't use get_key as '*i' might not be value_type - it
|
||||
// could be a pair with first_types as key_type without const or
|
||||
// a different second_type.
|
||||
//
|
||||
// TODO: Might be worth storing the value_type instead of the
|
||||
// key here. Could be more efficient if '*i' is expensive. Could
|
||||
// be less efficient if copying the full value_type is
|
||||
// expensive.
|
||||
insert_range_impl2(a, extractor::extract(*i), i, j);
|
||||
}
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range_impl2(node_constructor& a, key_type const& k,
|
||||
InputIt i, InputIt j)
|
||||
{
|
||||
// No side effects in this initial code
|
||||
std::size_t key_hash = this->hash(k);
|
||||
iterator pos = this->find_node(key_hash, k);
|
||||
|
||||
if (!pos.node_) {
|
||||
a.construct_with_value2(*i);
|
||||
if(this->size_ + 1 > this->max_load_)
|
||||
this->reserve_for_insert(this->size_ +
|
||||
boost::unordered::detail::insert_size(i, j));
|
||||
|
||||
// Nothing after this point can throw.
|
||||
this->add_node(a, key_hash);
|
||||
}
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void insert_range_impl(no_key, InputIt i, InputIt j)
|
||||
{
|
||||
node_constructor a(this->node_alloc());
|
||||
|
||||
do {
|
||||
a.construct_with_value2(*i);
|
||||
emplace_impl_with_node(a);
|
||||
} while(++i != j);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Erase
|
||||
//
|
||||
// no throw
|
||||
|
||||
std::size_t erase_key(key_type const& k)
|
||||
{
|
||||
if(!this->size_) return 0;
|
||||
|
||||
std::size_t key_hash = this->hash(k);
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, key_hash);
|
||||
bucket_pointer this_bucket = this->get_bucket(bucket_index);
|
||||
|
||||
previous_pointer prev = this_bucket->next_;
|
||||
if (!prev) return 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!prev->next_) return 0;
|
||||
std::size_t node_hash =
|
||||
static_cast<node_pointer>(prev->next_)->hash_;
|
||||
if (policy::to_bucket(this->bucket_count_, node_hash)
|
||||
!= bucket_index)
|
||||
return 0;
|
||||
if (node_hash == key_hash &&
|
||||
this->key_eq()(k, this->get_key(
|
||||
static_cast<node_pointer>(prev->next_)->value())))
|
||||
break;
|
||||
prev = static_cast<previous_pointer>(prev->next_);
|
||||
}
|
||||
|
||||
node_pointer pos = static_cast<node_pointer>(prev->next_);
|
||||
node_pointer end = static_cast<node_pointer>(pos->next_);
|
||||
prev->next_ = pos->next_;
|
||||
this->fix_buckets(this_bucket, prev, end);
|
||||
return this->delete_nodes(c_iterator(pos), c_iterator(end));
|
||||
}
|
||||
|
||||
iterator erase(c_iterator r)
|
||||
{
|
||||
BOOST_ASSERT(r.node_);
|
||||
iterator next(r.node_);
|
||||
++next;
|
||||
|
||||
bucket_pointer this_bucket = this->get_bucket(
|
||||
policy::to_bucket(this->bucket_count_, r.node_->hash_));
|
||||
previous_pointer prev = unlink_node(*this_bucket, r.node_);
|
||||
|
||||
this->fix_buckets(this_bucket, prev, next.node_);
|
||||
|
||||
this->delete_node(r);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
iterator erase_range(c_iterator r1, c_iterator r2)
|
||||
{
|
||||
if (r1 == r2) return iterator(r2.node_);
|
||||
|
||||
std::size_t bucket_index =
|
||||
policy::to_bucket(this->bucket_count_, r1.node_->hash_);
|
||||
previous_pointer prev = unlink_nodes(
|
||||
*this->get_bucket(bucket_index), r1.node_, r2.node_);
|
||||
this->fix_buckets_range(bucket_index, prev, r1.node_, r2.node_);
|
||||
this->delete_nodes(r1, r2);
|
||||
|
||||
return iterator(r2.node_);
|
||||
}
|
||||
|
||||
static previous_pointer unlink_node(bucket& b, node_pointer n)
|
||||
{
|
||||
return unlink_nodes(b, n, static_cast<node_pointer>(n->next_));
|
||||
}
|
||||
|
||||
static previous_pointer unlink_nodes(bucket& b,
|
||||
node_pointer begin, node_pointer end)
|
||||
{
|
||||
previous_pointer prev = b.next_;
|
||||
link_pointer begin_void = static_cast<link_pointer>(begin);
|
||||
while(prev->next_ != begin_void)
|
||||
prev = static_cast<previous_pointer>(prev->next_);
|
||||
prev->next_ = static_cast<link_pointer>(end);
|
||||
return prev;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// fill_buckets
|
||||
|
||||
template <class NodeCreator>
|
||||
static void fill_buckets(iterator n, table& dst,
|
||||
NodeCreator& creator)
|
||||
{
|
||||
previous_pointer prev = dst.get_previous_start();
|
||||
|
||||
while (n.node_) {
|
||||
node_pointer node = creator.create(*n);
|
||||
node->hash_ = n.node_->hash_;
|
||||
prev->next_ = static_cast<link_pointer>(node);
|
||||
++dst.size_;
|
||||
++n;
|
||||
|
||||
prev = place_in_bucket(dst, prev);
|
||||
}
|
||||
}
|
||||
|
||||
// strong otherwise exception safety
|
||||
void rehash_impl(std::size_t num_buckets)
|
||||
{
|
||||
BOOST_ASSERT(this->buckets_);
|
||||
|
||||
this->create_buckets(num_buckets);
|
||||
previous_pointer prev = this->get_previous_start();
|
||||
while (prev->next_)
|
||||
prev = place_in_bucket(*this, prev);
|
||||
}
|
||||
|
||||
// Iterate through the nodes placing them in the correct buckets.
|
||||
// pre: prev->next_ is not null.
|
||||
static previous_pointer place_in_bucket(table& dst,
|
||||
previous_pointer prev)
|
||||
{
|
||||
node_pointer n = static_cast<node_pointer>(prev->next_);
|
||||
bucket_pointer b = dst.get_bucket(
|
||||
table::to_bucket(dst.bucket_count_, n->hash_));
|
||||
|
||||
if (!b->next_) {
|
||||
b->next_ = prev;
|
||||
return static_cast<previous_pointer>(n);
|
||||
}
|
||||
else {
|
||||
prev->next_ = n->next_;
|
||||
n->next_ = b->next_->next_;
|
||||
b->next_->next_ = static_cast<link_pointer>(n);
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,260 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_UTIL_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_UTIL_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
#include <boost/type_traits/is_empty.hpp>
|
||||
#include <boost/iterator/iterator_categories.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/detail/select_type.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
#include <boost/preprocessor/seq/size.hpp>
|
||||
#include <boost/preprocessor/seq/enum.hpp>
|
||||
#include <boost/swap.hpp>
|
||||
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
static const float minimum_max_load_factor = 1e-3f;
|
||||
static const std::size_t default_bucket_count = 11;
|
||||
struct move_tag {};
|
||||
struct empty_emplace {};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// iterator SFINAE
|
||||
|
||||
template <typename I>
|
||||
struct is_forward :
|
||||
boost::is_convertible<
|
||||
typename boost::iterator_traversal<I>::type,
|
||||
boost::forward_traversal_tag>
|
||||
{};
|
||||
|
||||
template <typename I, typename ReturnType>
|
||||
struct enable_if_forward :
|
||||
boost::enable_if_c<
|
||||
boost::unordered::detail::is_forward<I>::value,
|
||||
ReturnType>
|
||||
{};
|
||||
|
||||
template <typename I, typename ReturnType>
|
||||
struct disable_if_forward :
|
||||
boost::disable_if_c<
|
||||
boost::unordered::detail::is_forward<I>::value,
|
||||
ReturnType>
|
||||
{};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// primes
|
||||
|
||||
#define BOOST_UNORDERED_PRIMES \
|
||||
(17ul)(29ul)(37ul)(53ul)(67ul)(79ul) \
|
||||
(97ul)(131ul)(193ul)(257ul)(389ul)(521ul)(769ul) \
|
||||
(1031ul)(1543ul)(2053ul)(3079ul)(6151ul)(12289ul)(24593ul) \
|
||||
(49157ul)(98317ul)(196613ul)(393241ul)(786433ul) \
|
||||
(1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul) \
|
||||
(50331653ul)(100663319ul)(201326611ul)(402653189ul)(805306457ul) \
|
||||
(1610612741ul)(3221225473ul)(4294967291ul)
|
||||
|
||||
template<class T> struct prime_list_template
|
||||
{
|
||||
static std::size_t const value[];
|
||||
|
||||
#if !defined(SUNPRO_CC)
|
||||
static std::ptrdiff_t const length;
|
||||
#else
|
||||
static std::ptrdiff_t const length
|
||||
= BOOST_PP_SEQ_SIZE(BOOST_UNORDERED_PRIMES);
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class T>
|
||||
std::size_t const prime_list_template<T>::value[] = {
|
||||
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIMES)
|
||||
};
|
||||
|
||||
#if !defined(SUNPRO_CC)
|
||||
template<class T>
|
||||
std::ptrdiff_t const prime_list_template<T>::length
|
||||
= BOOST_PP_SEQ_SIZE(BOOST_UNORDERED_PRIMES);
|
||||
#endif
|
||||
|
||||
#undef BOOST_UNORDERED_PRIMES
|
||||
|
||||
typedef prime_list_template<std::size_t> prime_list;
|
||||
|
||||
// no throw
|
||||
inline std::size_t next_prime(std::size_t num) {
|
||||
std::size_t const* const prime_list_begin = prime_list::value;
|
||||
std::size_t const* const prime_list_end = prime_list_begin +
|
||||
prime_list::length;
|
||||
std::size_t const* bound =
|
||||
std::lower_bound(prime_list_begin, prime_list_end, num);
|
||||
if(bound == prime_list_end)
|
||||
bound--;
|
||||
return *bound;
|
||||
}
|
||||
|
||||
// no throw
|
||||
inline std::size_t prev_prime(std::size_t num) {
|
||||
std::size_t const* const prime_list_begin = prime_list::value;
|
||||
std::size_t const* const prime_list_end = prime_list_begin +
|
||||
prime_list::length;
|
||||
std::size_t const* bound =
|
||||
std::upper_bound(prime_list_begin,prime_list_end, num);
|
||||
if(bound != prime_list_begin)
|
||||
bound--;
|
||||
return *bound;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// insert_size/initial_size
|
||||
|
||||
#if !defined(BOOST_NO_STD_DISTANCE)
|
||||
|
||||
using ::std::distance;
|
||||
|
||||
#else
|
||||
|
||||
template <class ForwardIterator>
|
||||
inline std::size_t distance(ForwardIterator i, ForwardIterator j) {
|
||||
std::size_t x;
|
||||
std::distance(i, j, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template <class I>
|
||||
inline typename
|
||||
boost::unordered::detail::enable_if_forward<I, std::size_t>::type
|
||||
insert_size(I i, I j)
|
||||
{
|
||||
return std::distance(i, j);
|
||||
}
|
||||
|
||||
template <class I>
|
||||
inline typename
|
||||
boost::unordered::detail::disable_if_forward<I, std::size_t>::type
|
||||
insert_size(I, I)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class I>
|
||||
inline std::size_t initial_size(I i, I j,
|
||||
std::size_t num_buckets =
|
||||
boost::unordered::detail::default_bucket_count)
|
||||
{
|
||||
// TODO: Why +1?
|
||||
return (std::max)(
|
||||
boost::unordered::detail::insert_size(i, j) + 1,
|
||||
num_buckets);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// compressed
|
||||
|
||||
template <typename T, int Index>
|
||||
struct compressed_base : private T
|
||||
{
|
||||
compressed_base(T const& x) : T(x) {}
|
||||
compressed_base(T& x, move_tag) : T(boost::move(x)) {}
|
||||
|
||||
T& get() { return *this; }
|
||||
T const& get() const { return *this; }
|
||||
};
|
||||
|
||||
template <typename T, int Index>
|
||||
struct uncompressed_base
|
||||
{
|
||||
uncompressed_base(T const& x) : value_(x) {}
|
||||
uncompressed_base(T& x, move_tag) : value_(boost::move(x)) {}
|
||||
|
||||
T& get() { return value_; }
|
||||
T const& get() const { return value_; }
|
||||
private:
|
||||
T value_;
|
||||
};
|
||||
|
||||
template <typename T, int Index>
|
||||
struct generate_base
|
||||
: boost::detail::if_true<
|
||||
boost::is_empty<T>::value
|
||||
>:: BOOST_NESTED_TEMPLATE then<
|
||||
boost::unordered::detail::compressed_base<T, Index>,
|
||||
boost::unordered::detail::uncompressed_base<T, Index>
|
||||
>
|
||||
{};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct compressed
|
||||
: private boost::unordered::detail::generate_base<T1, 1>::type,
|
||||
private boost::unordered::detail::generate_base<T2, 2>::type
|
||||
{
|
||||
typedef typename generate_base<T1, 1>::type base1;
|
||||
typedef typename generate_base<T2, 2>::type base2;
|
||||
|
||||
typedef T1 first_type;
|
||||
typedef T2 second_type;
|
||||
|
||||
first_type& first() {
|
||||
return static_cast<base1*>(this)->get();
|
||||
}
|
||||
|
||||
first_type const& first() const {
|
||||
return static_cast<base1 const*>(this)->get();
|
||||
}
|
||||
|
||||
second_type& second() {
|
||||
return static_cast<base2*>(this)->get();
|
||||
}
|
||||
|
||||
second_type const& second() const {
|
||||
return static_cast<base2 const*>(this)->get();
|
||||
}
|
||||
|
||||
template <typename First, typename Second>
|
||||
compressed(First const& x1, Second const& x2)
|
||||
: base1(x1), base2(x2) {}
|
||||
|
||||
compressed(compressed const& x)
|
||||
: base1(x.first()), base2(x.second()) {}
|
||||
|
||||
compressed(compressed& x, move_tag m)
|
||||
: base1(x.first(), m), base2(x.second(), m) {}
|
||||
|
||||
void assign(compressed const& x)
|
||||
{
|
||||
first() = x.first();
|
||||
second() = x.second();
|
||||
}
|
||||
|
||||
void move_assign(compressed& x)
|
||||
{
|
||||
first() = boost::move(x.first());
|
||||
second() = boost::move(x.second());
|
||||
}
|
||||
|
||||
void swap(compressed& x)
|
||||
{
|
||||
boost::swap(first(), x.first());
|
||||
boost::swap(second(), x.second());
|
||||
}
|
||||
|
||||
private:
|
||||
// Prevent assignment just to make use of assign or
|
||||
// move_assign explicit.
|
||||
compressed& operator=(compressed const&);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif
|
||||
+1152
-2301
File diff suppressed because it is too large
Load Diff
@@ -6,67 +6,60 @@
|
||||
#ifndef BOOST_UNORDERED_MAP_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_MAP_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <boost/unordered/detail/fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class K, class T, class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
class unordered_map;
|
||||
namespace boost
|
||||
{
|
||||
namespace unordered
|
||||
{
|
||||
template <class K,
|
||||
class T,
|
||||
class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
class unordered_map;
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator==(
|
||||
unordered_map<K, T, H, P, A> const&, unordered_map<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator!=(
|
||||
unordered_map<K, T, H, P, A> const&, unordered_map<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline void swap(
|
||||
unordered_map<K, T, H, P, A>& m1, unordered_map<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_map<K, T, H, P, A> const&,
|
||||
unordered_map<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_map<K, T, H, P, A> const&,
|
||||
unordered_map<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline void swap(unordered_map<K, T, H, P, A>&,
|
||||
unordered_map<K, T, H, P, A>&);
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_map<K, T, H, P, A>& c, Predicate pred);
|
||||
template <class K,
|
||||
class T,
|
||||
class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
class unordered_multimap;
|
||||
|
||||
template <class K, class T, class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
class unordered_multimap;
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_multimap<K, T, H, P, A> const&,
|
||||
unordered_multimap<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_multimap<K, T, H, P, A> const&,
|
||||
unordered_multimap<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline void swap(unordered_multimap<K, T, H, P, A>&,
|
||||
unordered_multimap<K, T, H, P, A>&);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_multimap<K, T, H, P, A> const&,
|
||||
unordered_multimap<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_multimap<K, T, H, P, A> const&,
|
||||
unordered_multimap<K, T, H, P, A> const&);
|
||||
template <class K, class T, class H, class P, class A>
|
||||
inline void swap(unordered_multimap<K, T, H, P, A>& m1,
|
||||
unordered_multimap<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_multimap<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class K, class T, class A> class node_handle_map;
|
||||
template <class N, class K, class T, class A> struct insert_return_type_map;
|
||||
}
|
||||
|
||||
using boost::unordered::unordered_map;
|
||||
using boost::unordered::unordered_multimap;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
using boost::unordered::unordered_map;
|
||||
using boost::unordered::unordered_multimap;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+1061
-1777
File diff suppressed because it is too large
Load Diff
@@ -6,65 +6,58 @@
|
||||
#ifndef BOOST_UNORDERED_SET_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_SET_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <boost/unordered/detail/fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_set;
|
||||
namespace boost
|
||||
{
|
||||
namespace unordered
|
||||
{
|
||||
template <class T,
|
||||
class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_set;
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator==(
|
||||
unordered_set<T, H, P, A> const&, unordered_set<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator!=(
|
||||
unordered_set<T, H, P, A> const&, unordered_set<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline void swap(
|
||||
unordered_set<T, H, P, A>& m1, unordered_set<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_set<T, H, P, A> const&,
|
||||
unordered_set<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_set<T, H, P, A> const&,
|
||||
unordered_set<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline void swap(unordered_set<T, H, P, A> &m1,
|
||||
unordered_set<T, H, P, A> &m2);
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type erase_if(
|
||||
unordered_set<K, H, P, A>& c, Predicate pred);
|
||||
template <class T,
|
||||
class H = boost::hash<T>,
|
||||
class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_multiset;
|
||||
|
||||
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_multiset;
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_multiset<T, H, P, A> const&,
|
||||
unordered_multiset<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_multiset<T, H, P, A> const&,
|
||||
unordered_multiset<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline void swap(unordered_multiset<T, H, P, A> &m1,
|
||||
unordered_multiset<T, H, P, A> &m2);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator==(unordered_multiset<T, H, P, A> const&,
|
||||
unordered_multiset<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline bool operator!=(unordered_multiset<T, H, P, A> const&,
|
||||
unordered_multiset<T, H, P, A> const&);
|
||||
template <class T, class H, class P, class A>
|
||||
inline void swap(
|
||||
unordered_multiset<T, H, P, A>& m1, unordered_multiset<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type erase_if(
|
||||
unordered_multiset<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class T, class A> class node_handle_set;
|
||||
template <class N, class T, class A> struct insert_return_type_set;
|
||||
}
|
||||
|
||||
using boost::unordered::unordered_set;
|
||||
using boost::unordered::unordered_multiset;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
using boost::unordered::unordered_set;
|
||||
using boost::unordered::unordered_multiset;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
#ifndef BOOST_UNORDERED_MAP_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_MAP_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
#ifndef BOOST_UNORDERED_SET_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_SET_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/unordered_set.hpp>
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; URL=doc/html/unordered.html">
|
||||
<meta http-equiv="refresh" content="0; URL=../../doc/html/unordered.html">
|
||||
</head>
|
||||
<body>
|
||||
Automatic redirection failed, please go to
|
||||
<a href="doc/html/unordered.html">doc/html/unordered.html</a>
|
||||
<a href="../../doc/html/unordered.html">../../doc/html/unordered.html</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017-2018 Daniel James
|
||||
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)
|
||||
-->
|
||||
<explicit-failures-markup>
|
||||
<!-- unordered -->
|
||||
<library name="unordered">
|
||||
<mark-expected-failures>
|
||||
<test name="unnecessary_copy_tests"/>
|
||||
<toolset name="borland-*"/>
|
||||
<toolset name="sun-*"/>
|
||||
<note author="Daniel James">
|
||||
This tests whether inserting elements creates as few copies as I think
|
||||
is possible. If this fails it just means that the container might be
|
||||
a little inefficient.
|
||||
</note>
|
||||
</mark-expected-failures>
|
||||
|
||||
<mark-expected-failures>
|
||||
<test name="compile_map_unordered_allocator"/>
|
||||
<toolset name="msvc-7.1"/>
|
||||
<note author="Daniel James">
|
||||
This test fail because it's using unordered's internal
|
||||
allocator traits, which doesn't work on Visual C++ 7.1.
|
||||
It normally uses the one from Boost.Container by default.
|
||||
</note>
|
||||
</mark-expected-failures>
|
||||
|
||||
<mark-expected-failures>
|
||||
<test name="noexcept_tests"/>
|
||||
<toolset name="gcc-4.3c+"/>
|
||||
<note author="Daniel James">
|
||||
boost::is_nothrow_move_constructible and
|
||||
boost::is_nothrow_move_assignable don't seem to work on this
|
||||
compiler. I'd hope that anyone wanting noexcept support would
|
||||
use a more recent compiler anyway.
|
||||
</note>
|
||||
</mark-expected-failures>
|
||||
</library>
|
||||
</explicit-failures-markup>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"key": "unordered",
|
||||
"name": "Unordered",
|
||||
"authors": [
|
||||
"Daniel James"
|
||||
],
|
||||
"maintainers": [
|
||||
"Daniel James <dnljms -at- gmail.com>"
|
||||
],
|
||||
"description": "Unordered associative containers.",
|
||||
"std": [
|
||||
"tr1"
|
||||
],
|
||||
"category": [
|
||||
"Containers"
|
||||
],
|
||||
"cxxstd": "03"
|
||||
}
|
||||
+2
-103
@@ -5,106 +5,5 @@
|
||||
|
||||
import testing ;
|
||||
|
||||
# Adding -Wundef is blocked on (at least)
|
||||
# https://github.com/boostorg/type_traits/issues/165
|
||||
|
||||
local gcc-flags = -Wsign-promo -Wconversion -Wsign-conversion -Wfloat-equal -Wshadow -Wno-variadic-macros ;
|
||||
local clang-flags = $(gcc-flags) -Wno-c99-extensions ;
|
||||
local msvc-flags = /wd4494 ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
|
||||
<toolset>gcc:<cxxflags>$(gcc-flags)
|
||||
<toolset>darwin:<cxxflags>$(gcc-flags)
|
||||
<toolset>clang:<cxxflags>$(clang-flags)
|
||||
<toolset>msvc:<cxxflags>$(msvc-flags)
|
||||
|
||||
<toolset>gcc:<warnings-as-errors>on
|
||||
<toolset>clang:<warnings-as-errors>on
|
||||
<toolset>msvc:<warnings-as-errors>on
|
||||
;
|
||||
|
||||
#alias framework : /boost/test//boost_unit_test_framework ;
|
||||
alias framework : ;
|
||||
|
||||
test-suite unordered
|
||||
:
|
||||
[ run unordered/fwd_set_test.cpp ]
|
||||
[ run unordered/fwd_map_test.cpp ]
|
||||
[ run unordered/allocator_traits.cpp ]
|
||||
[ run unordered/minimal_allocator.cpp ]
|
||||
[ run unordered/compile_set.cpp ]
|
||||
[ run unordered/compile_map.cpp ]
|
||||
[ run unordered/noexcept_tests.cpp ]
|
||||
[ run unordered/link_test_1.cpp unordered/link_test_2.cpp ]
|
||||
[ run unordered/incomplete_test.cpp ]
|
||||
[ run unordered/simple_tests.cpp ]
|
||||
[ run unordered/equivalent_keys_tests.cpp ]
|
||||
[ run unordered/constructor_tests.cpp ]
|
||||
[ run unordered/copy_tests.cpp ]
|
||||
[ run unordered/move_tests.cpp ]
|
||||
[ run unordered/assign_tests.cpp ]
|
||||
[ run unordered/insert_tests.cpp ]
|
||||
[ run unordered/insert_stable_tests.cpp ]
|
||||
[ run unordered/insert_hint_tests.cpp ]
|
||||
[ run unordered/emplace_tests.cpp ]
|
||||
[ run unordered/unnecessary_copy_tests.cpp ]
|
||||
[ run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ]
|
||||
[ run unordered/erase_equiv_tests.cpp ]
|
||||
[ run unordered/extract_tests.cpp ]
|
||||
[ run unordered/node_handle_tests.cpp ]
|
||||
[ run unordered/merge_tests.cpp ]
|
||||
[ compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ]
|
||||
[ compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ]
|
||||
[ compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ]
|
||||
[ compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ]
|
||||
[ run unordered/find_tests.cpp ]
|
||||
[ run unordered/at_tests.cpp ]
|
||||
[ run unordered/bucket_tests.cpp ]
|
||||
[ run unordered/load_factor_tests.cpp ]
|
||||
[ run unordered/rehash_tests.cpp ]
|
||||
[ run unordered/equality_tests.cpp ]
|
||||
[ run unordered/swap_tests.cpp ]
|
||||
[ run unordered/detail_tests.cpp ]
|
||||
[ run unordered/deduction_tests.cpp ]
|
||||
[ run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ]
|
||||
[ run unordered/transparent_tests.cpp ]
|
||||
[ run unordered/reserve_tests.cpp ]
|
||||
[ run unordered/contains_tests.cpp ]
|
||||
[ run unordered/mix_policy.cpp ]
|
||||
[ run unordered/erase_if.cpp ]
|
||||
|
||||
[ run unordered/compile_set.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_compile_set ]
|
||||
[ run unordered/compile_map.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_compile_map ]
|
||||
[ run unordered/copy_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_copy ]
|
||||
[ run unordered/move_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_move ]
|
||||
[ run unordered/assign_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_assign ]
|
||||
;
|
||||
|
||||
test-suite unordered-exception
|
||||
:
|
||||
[ run exception/constructor_exception_tests.cpp framework ]
|
||||
[ run exception/copy_exception_tests.cpp framework ]
|
||||
[ run exception/assign_exception_tests.cpp framework ]
|
||||
[ run exception/move_assign_exception_tests.cpp framework ]
|
||||
[ run exception/insert_exception_tests.cpp framework ]
|
||||
[ run exception/erase_exception_tests.cpp framework ]
|
||||
[ run exception/rehash_exception_tests.cpp framework ]
|
||||
[ run exception/swap_exception_tests.cpp framework : : :
|
||||
<define>BOOST_UNORDERED_SWAP_METHOD=2 ]
|
||||
[ run exception/merge_exception_tests.cpp framework ]
|
||||
;
|
||||
build-project unordered ;
|
||||
build-project exception ;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
# Copyright 2006-2008 Daniel James.
|
||||
# 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)
|
||||
|
||||
import testing ;
|
||||
|
||||
#alias framework : /boost/test//boost_unit_test_framework ;
|
||||
alias framework : ;
|
||||
|
||||
project unordered-test/exception-tests
|
||||
: requirements
|
||||
<warnings>all
|
||||
<toolset>intel:<warnings>on
|
||||
<toolset>gcc:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter"
|
||||
<toolset>darwin:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter"
|
||||
#<toolset>gcc:<define>_GLIBCXX_DEBUG
|
||||
#<toolset>darwin:<define>_GLIBCXX_DEBUG
|
||||
#<toolset>msvc:<warnings-as-errors>on
|
||||
#<toolset>gcc:<warnings-as-errors>on
|
||||
#<toolset>darwin:<warnings-as-errors>on
|
||||
;
|
||||
|
||||
test-suite unordered-exception
|
||||
:
|
||||
[ run constructor_exception_tests.cpp framework ]
|
||||
[ run copy_exception_tests.cpp framework ]
|
||||
[ run assign_exception_tests.cpp framework ]
|
||||
[ run insert_exception_tests.cpp framework ]
|
||||
[ run erase_exception_tests.cpp framework ]
|
||||
[ run rehash_exception_tests.cpp framework ]
|
||||
[ run swap_exception_tests.cpp framework : : :
|
||||
<define>BOOST_UNORDERED_SWAP_METHOD=2 ]
|
||||
;
|
||||
@@ -4,188 +4,108 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(disable : 4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
#if __has_warning("-Wself-assign-overloaded")
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#pragma warning(disable:4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
test::seed_t initialize_seed(12847);
|
||||
|
||||
template <class T> struct self_assign_base : public test::exception_base
|
||||
template <class T>
|
||||
struct self_assign_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
self_assign_base(std::size_t count = 0) : values(count, test::limited_range)
|
||||
{
|
||||
}
|
||||
test::random_values<T> values;
|
||||
self_assign_base(int count = 0) : values(count) {}
|
||||
|
||||
typedef T data_type;
|
||||
T init() const { return T(values.begin(), values.end()); }
|
||||
|
||||
void run(T& x) const
|
||||
{
|
||||
x = x;
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
||||
{
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
typedef T data_type;
|
||||
T init() const { return T(values.begin(), values.end()); }
|
||||
void run(T& x) const { x = x; }
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
||||
{ test::check_equivalent_keys(x); }
|
||||
};
|
||||
|
||||
template <class T> struct self_assign_test1 : self_assign_base<T>
|
||||
template <class T>
|
||||
struct self_assign_test1 : self_assign_base<T> {};
|
||||
|
||||
template <class T>
|
||||
struct self_assign_test2 : self_assign_base<T>
|
||||
{
|
||||
self_assign_test2() : self_assign_base<T>(100) {}
|
||||
};
|
||||
|
||||
template <class T> struct self_assign_test2 : self_assign_base<T>
|
||||
template <class T>
|
||||
struct assign_base : public test::exception_base
|
||||
{
|
||||
self_assign_test2() : self_assign_base<T>(100) {}
|
||||
};
|
||||
const test::random_values<T> x_values, y_values;
|
||||
T x,y;
|
||||
|
||||
template <class T> struct assign_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> x_values, y_values;
|
||||
T x, y;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::key_equal key_equal;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
|
||||
|
||||
typedef typename T::hasher hasher;
|
||||
typedef typename T::key_equal key_equal;
|
||||
typedef typename T::allocator_type allocator_type;
|
||||
assign_base(unsigned int count1, unsigned int count2, int tag1, int tag2,
|
||||
float mlf1 = 1.0, float mlf2 = 1.0) :
|
||||
x_values(count1),
|
||||
y_values(count2),
|
||||
x(x_values.begin(), x_values.end(), 0, hasher(tag1), key_equal(tag1),
|
||||
allocator_type(tag1)),
|
||||
y(y_values.begin(), y_values.end(), 0, hasher(tag2), key_equal(tag2),
|
||||
allocator_type(tag2))
|
||||
{
|
||||
x.max_load_factor(mlf1);
|
||||
y.max_load_factor(mlf2);
|
||||
}
|
||||
|
||||
assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
||||
: x_values(), y_values(),
|
||||
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
||||
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2))
|
||||
{
|
||||
x.max_load_factor(mlf1);
|
||||
y.max_load_factor(mlf2);
|
||||
}
|
||||
typedef T data_type;
|
||||
T init() const { return T(x); }
|
||||
void run(T& x1) const { x1 = y; }
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x1) const
|
||||
{
|
||||
test::check_equivalent_keys(x1);
|
||||
|
||||
typedef T data_type;
|
||||
T init() const { return T(x); }
|
||||
|
||||
void run(T& x1) const
|
||||
{
|
||||
x1 = y;
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x1, y_values);
|
||||
test::check_equivalent_keys(x1);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x1) const
|
||||
{
|
||||
test::check_equivalent_keys(x1);
|
||||
|
||||
// If the container is empty at the point of the exception, the
|
||||
// internal structure is hidden, this exposes it, at the cost of
|
||||
// messing up the data.
|
||||
if (x_values.size()) {
|
||||
T& x2 = const_cast<T&>(x1);
|
||||
x2.emplace(*x_values.begin());
|
||||
test::check_equivalent_keys(x2);
|
||||
// If the container is empty at the point of the exception, the
|
||||
// internal structure is hidden, this exposes it.
|
||||
T& y = const_cast<T&>(x1);
|
||||
if (x_values.size()) {
|
||||
y.emplace(*x_values.begin());
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct assign_values : assign_base<T>
|
||||
template <class T>
|
||||
struct assign_test1 : assign_base<T>
|
||||
{
|
||||
assign_values(unsigned int count1, unsigned int count2, int tag1, int tag2,
|
||||
test::random_generator gen = test::default_generator, float mlf1 = 1.0,
|
||||
float mlf2 = 1.0)
|
||||
: assign_base<T>(tag1, tag2, mlf1, mlf2)
|
||||
{
|
||||
this->x_values.fill(count1, gen);
|
||||
this->y_values.fill(count2, gen);
|
||||
this->x.insert(this->x_values.begin(), this->x_values.end());
|
||||
this->y.insert(this->y_values.begin(), this->y_values.end());
|
||||
}
|
||||
assign_test1() : assign_base<T>(0, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test1 : assign_values<T>
|
||||
template <class T>
|
||||
struct assign_test2 : assign_base<T>
|
||||
{
|
||||
assign_test1() : assign_values<T>(0, 0, 0, 0) {}
|
||||
assign_test2() : assign_base<T>(60, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test2 : assign_values<T>
|
||||
template <class T>
|
||||
struct assign_test3 : assign_base<T>
|
||||
{
|
||||
assign_test2() : assign_values<T>(60, 0, 0, 0) {}
|
||||
assign_test3() : assign_base<T>(0, 60, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test2a : assign_values<T>
|
||||
template <class T>
|
||||
struct assign_test4 : assign_base<T>
|
||||
{
|
||||
assign_test2a() : assign_values<T>(60, 0, 0, 0, test::limited_range) {}
|
||||
assign_test4() : assign_base<T>(10, 10, 1, 2) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test3 : assign_values<T>
|
||||
template <class T>
|
||||
struct assign_test5 : assign_base<T>
|
||||
{
|
||||
assign_test3() : assign_values<T>(0, 60, 0, 0) {}
|
||||
assign_test5() : assign_base<T>(5, 60, 0, 0, 1.0, 0.1) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test3a : assign_values<T>
|
||||
{
|
||||
assign_test3a() : assign_values<T>(0, 60, 0, 0, test::limited_range) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test4 : assign_values<T>
|
||||
{
|
||||
assign_test4() : assign_values<T>(10, 10, 1, 2) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test4a : assign_values<T>
|
||||
{
|
||||
assign_test4a() : assign_values<T>(10, 100, 1, 2) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test4b : assign_values<T>
|
||||
{
|
||||
assign_test4b() : assign_values<T>(10, 100, 1, 2, test::limited_range) {}
|
||||
};
|
||||
|
||||
template <class T> struct assign_test5 : assign_values<T>
|
||||
{
|
||||
assign_test5()
|
||||
: assign_values<T>(5, 60, 0, 0, test::default_generator, 1.0f, 0.1f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct equivalent_test1 : assign_base<T>
|
||||
{
|
||||
equivalent_test1() : assign_base<T>(0, 0)
|
||||
{
|
||||
test::random_values<T> x_values2(10);
|
||||
this->x_values.insert(x_values2.begin(), x_values2.end());
|
||||
this->x_values.insert(x_values2.begin(), x_values2.end());
|
||||
test::random_values<T> y_values2(10);
|
||||
this->y_values.insert(y_values2.begin(), y_values2.end());
|
||||
this->y_values.insert(y_values2.begin(), y_values2.end());
|
||||
this->x.insert(this->x_values.begin(), this->x_values.end());
|
||||
this->y.insert(this->y_values.begin(), this->y_values.end());
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS_REPEAT(5,
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(self_assign_test1)(self_assign_test2)
|
||||
(assign_test1)(assign_test2)(assign_test2a)
|
||||
(assign_test3)(assign_test3a)
|
||||
(assign_test4)(assign_test4a)(assign_test4b)
|
||||
(assign_test5)
|
||||
(equivalent_test1),
|
||||
(assign_test1)(assign_test2)(assign_test3)(assign_test4)(assign_test5),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/input_iterator.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/input_iterator.hpp"
|
||||
|
||||
template <typename T> inline void avoid_unused_warning(T const&) {}
|
||||
|
||||
@@ -16,197 +13,164 @@ test::seed_t initialize_seed(91274);
|
||||
|
||||
struct objects
|
||||
{
|
||||
test::exception::object obj;
|
||||
test::exception::hash hash;
|
||||
test::exception::equal_to equal_to;
|
||||
test::exception::allocator<test::exception::object> allocator;
|
||||
test::exception::object obj;
|
||||
test::exception::hash hash;
|
||||
test::exception::equal_to equal_to;
|
||||
test::exception::allocator<test::exception::object> allocator;
|
||||
};
|
||||
|
||||
template <class T> struct construct_test1 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test1 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x;
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x;
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct construct_test2 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test2 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(300);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(300);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct construct_test3 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test3 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(0, hash);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(0, hash);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct construct_test4 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test4 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(0, hash, equal_to);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(0, hash, equal_to);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct construct_test5 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test5 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(50, hash, equal_to, allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(50, hash, equal_to, allocator);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct construct_test6 : public objects, test::exception_base
|
||||
template <class T>
|
||||
struct construct_test6 : public objects, test::exception_base
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(allocator);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct range : public test::exception_base
|
||||
template <class T>
|
||||
struct range : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
test::random_values<T> values;
|
||||
|
||||
range() : values(5, test::limited_range) {}
|
||||
range(unsigned int count) : values(count, test::limited_range) {}
|
||||
range() : values(5) {}
|
||||
range(unsigned int count) : values(count) {}
|
||||
};
|
||||
|
||||
template <class T> struct range_construct_test1 : public range<T>, objects
|
||||
template <class T>
|
||||
struct range_construct_test1 : public range<T>, objects
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(this->values.begin(), this->values.end());
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(this->values.begin(), this->values.end());
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct range_construct_test2 : public range<T>, objects
|
||||
template <class T>
|
||||
struct range_construct_test2 : public range<T>, objects
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(this->values.begin(), this->values.end(), 0);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(this->values.begin(), this->values.end(), 0);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct range_construct_test3 : public range<T>, objects
|
||||
template <class T>
|
||||
struct range_construct_test3 : public range<T>, objects
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(this->values.begin(), this->values.end(), 0, hash);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(this->values.begin(), this->values.end(), 0, hash);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct range_construct_test4 : public range<T>, objects
|
||||
template <class T>
|
||||
struct range_construct_test4 : public range<T>, objects
|
||||
{
|
||||
void run() const
|
||||
{
|
||||
T x(this->values.begin(), this->values.end(), 100, hash, equal_to);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(this->values.begin(), this->values.end(), 100, hash, equal_to);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
// Need to run at least one test with a fairly large number
|
||||
// of objects in case it triggers a rehash.
|
||||
template <class T> struct range_construct_test5 : public range<T>, objects
|
||||
template <class T>
|
||||
struct range_construct_test5 : public range<T>, objects
|
||||
{
|
||||
range_construct_test5() : range<T>(60) {}
|
||||
range_construct_test5() : range<T>(60) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T x(this->values.begin(), this->values.end(), 0, hash, equal_to, allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(this->values.begin(), this->values.end(), 0,
|
||||
hash, equal_to, allocator);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct input_range_construct_test : public range<T>, objects
|
||||
template <class T>
|
||||
struct input_range_construct_test : public range<T>, objects
|
||||
{
|
||||
input_range_construct_test() : range<T>(60) {}
|
||||
input_range_construct_test() : range<T>(60) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
typename test::random_values<T>::const_iterator begin =
|
||||
this->values.begin(),
|
||||
end = this->values.end();
|
||||
T x(test::input_iterator(begin), test::input_iterator(end), 0, hash,
|
||||
equal_to, allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
begin = this->values.begin(), end = this->values.end();
|
||||
T x(test::input_iterator(begin), test::input_iterator(end),
|
||||
0, hash, equal_to, allocator);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct copy_range_construct_test : public range<T>, objects
|
||||
template <class T>
|
||||
struct copy_range_construct_test : public range<T>, objects
|
||||
{
|
||||
copy_range_construct_test() : range<T>(60) {}
|
||||
copy_range_construct_test() : range<T>(60) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T x(test::copy_iterator(this->values.begin()),
|
||||
test::copy_iterator(this->values.end()), 0, hash, equal_to, allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void run() const {
|
||||
T x(test::copy_iterator(this->values.begin()),
|
||||
test::copy_iterator(this->values.end()),
|
||||
0, hash, equal_to, allocator);
|
||||
avoid_unused_warning(x);
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS(
|
||||
(construct_test1)(construct_test2)(construct_test3)(construct_test4)
|
||||
(construct_test5)(construct_test6)(range_construct_test1)
|
||||
(range_construct_test2)(range_construct_test3)(range_construct_test4)
|
||||
(range_construct_test5)(input_range_construct_test)
|
||||
(copy_range_construct_test),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(construct_test1)
|
||||
(construct_test2)
|
||||
(construct_test3)
|
||||
(construct_test4)
|
||||
(construct_test5)
|
||||
(construct_test6)
|
||||
(range_construct_test1)
|
||||
(range_construct_test2)
|
||||
(range_construct_test3)
|
||||
(range_construct_test4)
|
||||
(range_construct_test5)
|
||||
(input_range_construct_test)
|
||||
(copy_range_construct_test),
|
||||
CONTAINER_SEQ)
|
||||
|
||||
@@ -3,42 +3,33 @@
|
||||
// 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)
|
||||
|
||||
// clang-format off
|
||||
#include "../helpers/prefix.hpp"
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include "../helpers/postfix.hpp"
|
||||
// clang-format on
|
||||
|
||||
#include "../objects/exception.hpp"
|
||||
|
||||
typedef boost::unordered_set<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_set;
|
||||
typedef boost::unordered_multiset<test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >
|
||||
test_multiset;
|
||||
typedef boost::unordered_map<test::exception::object, test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >
|
||||
test_map;
|
||||
typedef boost::unordered_multimap<test::exception::object,
|
||||
test::exception::object, test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_multimap;
|
||||
typedef boost::unordered_set<
|
||||
std::pair<test::exception::object, test::exception::object>,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >
|
||||
test_pair_set;
|
||||
test::exception::object,
|
||||
test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> > test_set;
|
||||
typedef boost::unordered_multiset<
|
||||
std::pair<test::exception::object, test::exception::object>,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >
|
||||
test_pair_multiset;
|
||||
test::exception::object,
|
||||
test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> > test_multiset;
|
||||
typedef boost::unordered_map<
|
||||
test::exception::object,
|
||||
test::exception::object,
|
||||
test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> > test_map;
|
||||
typedef boost::unordered_multimap<
|
||||
test::exception::object,
|
||||
test::exception::object,
|
||||
test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> > test_multimap;
|
||||
|
||||
#define CONTAINER_SEQ (test_set)(test_multiset)(test_map)(test_multimap)
|
||||
#define CONTAINER_PAIR_SEQ \
|
||||
(test_pair_set)(test_pair_multiset)(test_map)(test_multimap)
|
||||
|
||||
@@ -4,107 +4,66 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
|
||||
template <typename T> inline void avoid_unused_warning(T const&) {}
|
||||
|
||||
test::seed_t initialize_seed(73041);
|
||||
|
||||
template <class T> struct copy_test1 : public test::exception_base
|
||||
template <class T>
|
||||
struct copy_test1 : public test::exception_base
|
||||
{
|
||||
T x;
|
||||
T x;
|
||||
|
||||
void run() const
|
||||
{
|
||||
T y(x);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(y.empty());
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
void run() const {
|
||||
T y(x);
|
||||
avoid_unused_warning(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct copy_test2 : public test::exception_base
|
||||
template <class T>
|
||||
struct copy_test2 : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
|
||||
copy_test2() : values(5, test::limited_range), x(values.begin(), values.end())
|
||||
{
|
||||
}
|
||||
copy_test2() : values(5), x(values.begin(), values.end()) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T y(x);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(y, this->values);
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
void run() const {
|
||||
T y(x);
|
||||
avoid_unused_warning(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct copy_test3 : public test::exception_base
|
||||
template <class T>
|
||||
struct copy_test3 : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
|
||||
copy_test3() : values(100), x(values.begin(), values.end()) {}
|
||||
copy_test3() : values(100), x(values.begin(), values.end()) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T y(x);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(y, this->values);
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
void run() const {
|
||||
T y(x);
|
||||
avoid_unused_warning(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct copy_test3a : public test::exception_base
|
||||
template <class T>
|
||||
struct copy_with_allocator_test : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
test::exception::allocator<test::exception::object> allocator;
|
||||
|
||||
copy_test3a()
|
||||
: values(100, test::limited_range), x(values.begin(), values.end())
|
||||
{
|
||||
}
|
||||
copy_with_allocator_test() : values(100), x(values.begin(), values.end()) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T y(x);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(y, this->values);
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
void run() const {
|
||||
T y(x, allocator);
|
||||
avoid_unused_warning(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct copy_with_allocator_test : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
T x;
|
||||
test::exception::allocator<test::exception::object> allocator;
|
||||
|
||||
copy_with_allocator_test() : values(100), x(values.begin(), values.end()) {}
|
||||
|
||||
void run() const
|
||||
{
|
||||
T y(x, allocator);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(y, this->values);
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS(
|
||||
(copy_test1)(copy_test2)(copy_test3)(copy_test3a)(copy_with_allocator_test),
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(copy_test1)(copy_test2)(copy_test3)(copy_with_allocator_test),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -4,52 +4,51 @@
|
||||
// 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/invariants.hpp"
|
||||
#include "../helpers/helpers.hpp"
|
||||
|
||||
test::seed_t initialize_seed(835193);
|
||||
|
||||
template <class T> struct erase_test_base : public test::exception_base
|
||||
template <class T>
|
||||
struct erase_test_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
erase_test_base(unsigned int count = 5) : values(count, test::limited_range)
|
||||
{
|
||||
}
|
||||
test::random_values<T> values;
|
||||
erase_test_base(unsigned int count = 5) : values(count) {}
|
||||
|
||||
typedef T data_type;
|
||||
typedef T data_type;
|
||||
|
||||
data_type init() const { return T(values.begin(), values.end()); }
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
BOOST_TEST(scope.find("hash::") != std::string::npos ||
|
||||
scope.find("equal_to::") != std::string::npos ||
|
||||
scope == "operator==(object, object)");
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct erase_by_key_test1 : public erase_test_base<T>
|
||||
{
|
||||
void run(T& x) const
|
||||
{
|
||||
typedef typename test::random_values<T>::const_iterator iterator;
|
||||
|
||||
for (iterator it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it) {
|
||||
x.erase(test::get_key<T>(*it));
|
||||
data_type init() const {
|
||||
return T(values.begin(), values.end());
|
||||
}
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
BOOST_TEST(x.empty());
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const {
|
||||
std::string scope(test::scope);
|
||||
|
||||
BOOST_TEST(scope.find("hash::") != std::string::npos ||
|
||||
scope.find("equal_to::") != std::string::npos ||
|
||||
scope == "operator==(object, object)");
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
EXCEPTION_TESTS((erase_by_key_test1), CONTAINER_SEQ)
|
||||
RUN_TESTS()
|
||||
template <class T>
|
||||
struct erase_by_key_test1 : public erase_test_base<T>
|
||||
{
|
||||
void run(T& x) const
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME
|
||||
test::random_values<T>::const_iterator iterator;
|
||||
|
||||
for(iterator it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
x.erase(test::get_key<T>(*it));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(erase_by_key_test1),
|
||||
CONTAINER_SEQ)
|
||||
|
||||
@@ -2,415 +2,245 @@
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// 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 <cmath>
|
||||
#include "./containers.hpp"
|
||||
#include <string>
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/strong.hpp"
|
||||
#include <boost/utility.hpp>
|
||||
#include <cmath>
|
||||
|
||||
test::seed_t initialize_seed(747373);
|
||||
|
||||
// Fill in a container so that it's about to rehash
|
||||
template <typename T> void rehash_prep(T& x)
|
||||
template <class T>
|
||||
struct insert_test_base : public test::exception_base
|
||||
{
|
||||
using namespace std;
|
||||
typedef typename T::size_type size_type;
|
||||
test::random_values<T> values;
|
||||
insert_test_base(unsigned int count = 5) : values(count) {}
|
||||
|
||||
x.max_load_factor(0.25);
|
||||
size_type bucket_count = x.bucket_count();
|
||||
size_type initial_elements = static_cast<size_type>(
|
||||
ceil((double)bucket_count * (double)x.max_load_factor()) - 1);
|
||||
test::random_values<T> v(initial_elements);
|
||||
x.insert(v.begin(), v.end());
|
||||
BOOST_TEST(bucket_count == x.bucket_count());
|
||||
}
|
||||
typedef T data_type;
|
||||
typedef test::strong<T> strong_type;
|
||||
|
||||
// Overload to generate inserters that need type information.
|
||||
|
||||
template <typename Inserter, typename T>
|
||||
Inserter generate(Inserter inserter, T&)
|
||||
{
|
||||
return inserter;
|
||||
}
|
||||
|
||||
// Get the iterator returned from an insert/emplace.
|
||||
|
||||
template <typename T> T get_iterator(T const& x) { return x; }
|
||||
|
||||
template <typename T> T get_iterator(std::pair<T, bool> const& x)
|
||||
{
|
||||
return x.first;
|
||||
}
|
||||
|
||||
// Generic insert exception test for typical single element inserts..
|
||||
|
||||
template <typename T, typename Inserter, typename Values>
|
||||
void insert_exception_test_impl(T x, Inserter insert, Values const& v)
|
||||
{
|
||||
test::strong<T> strong;
|
||||
|
||||
test::ordered<T> tracker;
|
||||
tracker.insert(x.begin(), x.end());
|
||||
|
||||
try {
|
||||
ENABLE_EXCEPTIONS;
|
||||
|
||||
for (typename Values::const_iterator it = v.begin(); it != v.end(); ++it) {
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
insert(x, it);
|
||||
data_type init() const {
|
||||
return T();
|
||||
}
|
||||
} catch (...) {
|
||||
test::check_equivalent_keys(x);
|
||||
insert.exception_check(x, strong);
|
||||
throw;
|
||||
}
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
insert.track(tracker, v.begin(), v.end());
|
||||
tracker.compare(x);
|
||||
}
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(
|
||||
T const& x, strong_type const& strong) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
// Simple insert exception test
|
||||
|
||||
template <typename T, typename Inserter>
|
||||
void insert_exception_test(T*, Inserter insert, test::random_generator gen)
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
test::random_values<T> v(10, gen);
|
||||
T x;
|
||||
|
||||
EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v));
|
||||
}
|
||||
}
|
||||
|
||||
// Insert into a container which is about to hit its max load, so that it
|
||||
// rehashes.
|
||||
|
||||
template <typename T, typename Inserter>
|
||||
void insert_rehash_exception_test(
|
||||
T*, Inserter insert, test::random_generator gen)
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
T x;
|
||||
rehash_prep(x);
|
||||
|
||||
test::random_values<T> v2(5, gen);
|
||||
EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v2));
|
||||
}
|
||||
}
|
||||
|
||||
// Various methods for inserting a single element
|
||||
|
||||
struct inserter_base
|
||||
{
|
||||
template <typename T> void exception_check(T& x, test::strong<T>& strong)
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
if (scope.find("hash::operator()") == std::string::npos)
|
||||
strong.test(x, test::detail::tracker.count_allocations);
|
||||
}
|
||||
|
||||
template <typename T, typename Iterator>
|
||||
void track(T& tracker, Iterator begin, Iterator end)
|
||||
{
|
||||
tracker.insert(begin, end);
|
||||
}
|
||||
};
|
||||
|
||||
struct insert_lvalue_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.insert(*it);
|
||||
}
|
||||
} insert_lvalue;
|
||||
|
||||
struct insert_lvalue_begin_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.insert(x.begin(), *it);
|
||||
}
|
||||
} insert_lvalue_begin;
|
||||
|
||||
struct insert_lvalue_end_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.insert(x.end(), *it);
|
||||
}
|
||||
} insert_lvalue_end;
|
||||
|
||||
template <typename T> struct insert_lvalue_pos_type_impl : inserter_base
|
||||
{
|
||||
typename T::iterator pos;
|
||||
|
||||
insert_lvalue_pos_type_impl(T& x) : pos(x.begin()) {}
|
||||
|
||||
template <typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
pos = get_iterator(x.insert(pos, *it));
|
||||
}
|
||||
};
|
||||
|
||||
struct insert_lvalue_pos_type
|
||||
{
|
||||
template <typename T>
|
||||
friend insert_lvalue_pos_type_impl<T> generate(insert_lvalue_pos_type, T& x)
|
||||
{
|
||||
return insert_lvalue_pos_type_impl<T>(x);
|
||||
}
|
||||
} insert_lvalue_pos;
|
||||
|
||||
struct insert_single_item_range_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.insert(it, test::next(it));
|
||||
}
|
||||
} insert_single_item_range;
|
||||
|
||||
struct emplace_lvalue_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.emplace(*it);
|
||||
}
|
||||
} emplace_lvalue;
|
||||
|
||||
struct emplace_lvalue_begin_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.emplace_hint(x.begin(), *it);
|
||||
}
|
||||
} emplace_lvalue_begin;
|
||||
|
||||
struct emplace_lvalue_end_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.emplace_hint(x.end(), *it);
|
||||
}
|
||||
} emplace_lvalue_end;
|
||||
|
||||
template <typename T> struct emplace_lvalue_pos_type_impl : inserter_base
|
||||
{
|
||||
typename T::iterator pos;
|
||||
|
||||
emplace_lvalue_pos_type_impl(T& x) : pos(x.begin()) {}
|
||||
|
||||
template <typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
pos = get_iterator(x.emplace_hint(pos, *it));
|
||||
}
|
||||
};
|
||||
|
||||
struct emplace_lvalue_pos_type
|
||||
{
|
||||
template <typename T>
|
||||
friend emplace_lvalue_pos_type_impl<T> generate(emplace_lvalue_pos_type, T& x)
|
||||
{
|
||||
return emplace_lvalue_pos_type_impl<T>(x);
|
||||
}
|
||||
} emplace_lvalue_pos;
|
||||
|
||||
// Run the exception tests in various combinations.
|
||||
|
||||
test_set* test_set_;
|
||||
test_multiset* test_multiset_;
|
||||
test_map* test_map_;
|
||||
test_multimap* test_multimap_;
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
using test::generate_collisions;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_set_)(test_multiset_)(test_map_)(test_multimap_))
|
||||
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
|
||||
(insert_lvalue_pos)(insert_single_item_range)
|
||||
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
|
||||
(emplace_lvalue_pos)
|
||||
)
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
|
||||
UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((test_set_)(test_multiset_)(test_map_)(test_multimap_))
|
||||
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
|
||||
(insert_lvalue_pos)(insert_single_item_range)
|
||||
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
|
||||
(emplace_lvalue_pos)
|
||||
)
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
// Repeat insert tests with pairs
|
||||
|
||||
struct pair_emplace_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.emplace(boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(it->first), boost::make_tuple(it->second));
|
||||
}
|
||||
} pair_emplace;
|
||||
|
||||
struct pair_emplace2_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.emplace_hint(x.begin(), boost::unordered::piecewise_construct,
|
||||
boost::make_tuple(it->first),
|
||||
boost::make_tuple(it->second.tag1_, it->second.tag2_));
|
||||
}
|
||||
} pair_emplace2;
|
||||
|
||||
test_pair_set* test_pair_set_;
|
||||
test_pair_multiset* test_pair_multiset_;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_))
|
||||
((pair_emplace)(pair_emplace2))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
UNORDERED_TEST(insert_rehash_exception_test,
|
||||
((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_))
|
||||
((pair_emplace)(pair_emplace2))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
// Test inserting using operator[]
|
||||
|
||||
struct try_emplace_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.try_emplace(it->first, it->second);
|
||||
}
|
||||
} try_emplace;
|
||||
|
||||
struct try_emplace2_type : inserter_base
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.try_emplace(it->first, it->second.tag1_, it->second.tag2_);
|
||||
}
|
||||
} try_emplace2;
|
||||
|
||||
struct map_inserter_base
|
||||
{
|
||||
template <typename T> void exception_check(T& x, test::strong<T>& strong)
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
if (scope.find("hash::operator()") == std::string::npos &&
|
||||
scope.find("::operator=") == std::string::npos)
|
||||
strong.test(x, test::detail::tracker.count_allocations);
|
||||
}
|
||||
|
||||
template <typename T, typename Iterator>
|
||||
void track(T& tracker, Iterator begin, Iterator end)
|
||||
{
|
||||
for (; begin != end; ++begin) {
|
||||
tracker[begin->first] = begin->second;
|
||||
if(scope.find("hash::operator()") == std::string::npos)
|
||||
strong.test(x, test::detail::tracker.count_allocations);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct map_insert_operator_type : map_inserter_base
|
||||
#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
|
||||
template <class T>
|
||||
struct emplace_test1 : public insert_test_base<T>
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x[it->first] = it->second;
|
||||
}
|
||||
} map_insert_operator;
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
struct map_insert_or_assign_type : map_inserter_base
|
||||
void run(T& x, strong_type& strong) const {
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
x.emplace(*it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct insert_test1 : public insert_test_base<T>
|
||||
{
|
||||
template <typename T, typename Iterator> void operator()(T& x, Iterator it)
|
||||
{
|
||||
x.insert_or_assign(it->first, it->second);
|
||||
}
|
||||
} map_insert_or_assign;
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_exception_test,
|
||||
((test_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_))
|
||||
((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
void run(T& x, strong_type& strong) const {
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
x.insert(*it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Range insert tests
|
||||
|
||||
template <typename T, typename Values>
|
||||
void insert_range_exception_test_impl(T x, Values const& v)
|
||||
template <class T>
|
||||
struct insert_test2 : public insert_test_base<T>
|
||||
{
|
||||
test::ordered<T> tracker;
|
||||
tracker.insert(x.begin(), x.end());
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
try {
|
||||
ENABLE_EXCEPTIONS;
|
||||
x.insert(v.begin(), v.end());
|
||||
} catch (...) {
|
||||
test::check_equivalent_keys(x);
|
||||
throw;
|
||||
}
|
||||
void run(T& x, strong_type& strong) const {
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
x.insert(x.begin(), *it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
tracker.insert(v.begin(), v.end());
|
||||
tracker.compare(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void insert_range_exception_test(T*, test::random_generator gen)
|
||||
template <class T>
|
||||
struct insert_test3 : public insert_test_base<T>
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
test::random_values<T> v(10, gen);
|
||||
T x;
|
||||
void run(T& x) const {
|
||||
x.insert(this->values.begin(), this->values.end());
|
||||
}
|
||||
|
||||
EXCEPTION_LOOP(insert_range_exception_test_impl(x, v));
|
||||
}
|
||||
}
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const {
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void insert_range_rehash_exception_test(T*, test::random_generator gen)
|
||||
template <class T>
|
||||
struct insert_test4 : public insert_test_base<T>
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
T x;
|
||||
rehash_prep(x);
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
test::random_values<T> v2(5, gen);
|
||||
EXCEPTION_LOOP(insert_range_exception_test_impl(x, v2));
|
||||
}
|
||||
}
|
||||
void run(T& x, strong_type& strong) const {
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = this->values.begin(), end = this->values.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
x.insert(it, boost::next(it));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(insert_range_exception_test,
|
||||
((test_set_)(test_multiset_)(test_map_)(test_multimap_))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
template <class T>
|
||||
struct insert_test_rehash1 : public insert_test_base<T>
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
UNORDERED_TEST(insert_range_rehash_exception_test,
|
||||
((test_set_)(test_multiset_)(test_map_)(test_multimap_))
|
||||
((default_generator)(limited_range)(generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
insert_test_rehash1() : insert_test_base<T>(1000) {}
|
||||
|
||||
RUN_TESTS()
|
||||
T init() const {
|
||||
using namespace std;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::size_type size_type;
|
||||
|
||||
T x;
|
||||
x.max_load_factor(0.25);
|
||||
size_type bucket_count = x.bucket_count();
|
||||
size_type initial_elements = static_cast<size_type>(
|
||||
ceil(bucket_count * (double) x.max_load_factor()) - 1);
|
||||
BOOST_TEST(initial_elements < this->values.size());
|
||||
x.insert(this->values.begin(),
|
||||
boost::next(this->values.begin(), initial_elements));
|
||||
BOOST_TEST(bucket_count == x.bucket_count());
|
||||
return x;
|
||||
}
|
||||
|
||||
void run(T& x, strong_type& strong) const {
|
||||
BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count();
|
||||
int count = 0;
|
||||
BOOST_DEDUCED_TYPENAME T::const_iterator pos = x.cbegin();
|
||||
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = boost::next(this->values.begin(), x.size()),
|
||||
end = this->values.end();
|
||||
it != end && count < 10; ++it, ++count)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
pos = x.insert(pos, *it);
|
||||
}
|
||||
|
||||
// This isn't actually a failure, but it means the test isn't doing its
|
||||
// job.
|
||||
BOOST_TEST(x.bucket_count() != bucket_count);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct insert_test_rehash2 : public insert_test_rehash1<T>
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME insert_test_base<T>::strong_type strong_type;
|
||||
|
||||
void run(T& x, strong_type& strong) const {
|
||||
BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count();
|
||||
int count = 0;
|
||||
|
||||
for(BOOST_DEDUCED_TYPENAME test::random_values<T>::const_iterator
|
||||
it = boost::next(this->values.begin(), x.size()),
|
||||
end = this->values.end();
|
||||
it != end && count < 10; ++it, ++count)
|
||||
{
|
||||
strong.store(x, test::detail::tracker.count_allocations);
|
||||
x.insert(*it);
|
||||
}
|
||||
|
||||
// This isn't actually a failure, but it means the test isn't doing its
|
||||
// job.
|
||||
BOOST_TEST(x.bucket_count() != bucket_count);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct insert_test_rehash3 : public insert_test_base<T>
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME T::size_type mutable
|
||||
rehash_bucket_count, original_bucket_count;
|
||||
|
||||
insert_test_rehash3() : insert_test_base<T>(1000) {}
|
||||
|
||||
T init() const {
|
||||
using namespace std;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::size_type size_type;
|
||||
|
||||
T x;
|
||||
x.max_load_factor(0.25);
|
||||
|
||||
original_bucket_count = x.bucket_count();
|
||||
rehash_bucket_count = static_cast<size_type>(
|
||||
ceil(original_bucket_count * (double) x.max_load_factor())) - 1;
|
||||
|
||||
size_type initial_elements =
|
||||
rehash_bucket_count > 5 ? rehash_bucket_count - 5 : 1;
|
||||
|
||||
BOOST_TEST(initial_elements < this->values.size());
|
||||
x.insert(this->values.begin(),
|
||||
boost::next(this->values.begin(), initial_elements));
|
||||
BOOST_TEST(original_bucket_count == x.bucket_count());
|
||||
return x;
|
||||
}
|
||||
|
||||
void run(T& x) const {
|
||||
BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count();
|
||||
|
||||
x.insert(boost::next(this->values.begin(), x.size()),
|
||||
boost::next(this->values.begin(), x.size() + 20));
|
||||
|
||||
// This isn't actually a failure, but it means the test isn't doing its
|
||||
// job.
|
||||
BOOST_TEST(x.bucket_count() != bucket_count);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const {
|
||||
if(x.size() < rehash_bucket_count) {
|
||||
//BOOST_TEST(x.bucket_count() == original_bucket_count);
|
||||
}
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
#define BASIC_TESTS \
|
||||
(insert_test1)(insert_test2)(insert_test3)(insert_test4) \
|
||||
(insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3)
|
||||
|
||||
#if !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
#define ALL_TESTS (emplace_test1)BASIC_TESTS
|
||||
#else
|
||||
#define ALL_TESTS BASIC_TESTS
|
||||
#endif
|
||||
|
||||
|
||||
RUN_EXCEPTION_TESTS(ALL_TESTS, CONTAINER_SEQ)
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
|
||||
// Copyright 2017-2018 Daniel James.
|
||||
// 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/exception_test.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/metafunctions.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "./containers.hpp"
|
||||
|
||||
template <typename T1, typename T2> void merge_exception_test(T1 x, T2 y)
|
||||
{
|
||||
std::size_t size = x.size() + y.size();
|
||||
|
||||
try {
|
||||
ENABLE_EXCEPTIONS;
|
||||
x.merge(y);
|
||||
} catch (...) {
|
||||
test::check_equivalent_keys(x);
|
||||
test::check_equivalent_keys(y);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Not a full check, just want to make sure the merge completed.
|
||||
BOOST_TEST(size == x.size() + y.size());
|
||||
if (y.size()) {
|
||||
BOOST_TEST(test::has_unique_keys<T1>::value);
|
||||
for (typename T2::iterator it = y.begin(); it != y.end(); ++it) {
|
||||
BOOST_TEST(x.find(test::get_key<T2>(*it)) != x.end());
|
||||
}
|
||||
}
|
||||
test::check_equivalent_keys(x);
|
||||
test::check_equivalent_keys(y);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void merge_exception_test(T1 const*, T2 const*, std::size_t count12, int tag12,
|
||||
test::random_generator gen1, test::random_generator gen2)
|
||||
{
|
||||
std::size_t count1 = count12 / 256;
|
||||
std::size_t count2 = count12 % 256;
|
||||
int tag1 = tag12 / 256;
|
||||
int tag2 = tag12 % 256;
|
||||
test::random_values<T1> v1(count1, gen1);
|
||||
test::random_values<T2> v2(count2, gen2);
|
||||
T1 x(v1.begin(), v1.end(), 0, test::exception::hash(tag1),
|
||||
test::exception::equal_to(tag1));
|
||||
T2 y(v2.begin(), v2.end(), 0, test::exception::hash(tag2),
|
||||
test::exception::equal_to(tag2));
|
||||
|
||||
EXCEPTION_LOOP(merge_exception_test(x, y))
|
||||
}
|
||||
|
||||
boost::unordered_set<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >* test_set_;
|
||||
boost::unordered_multiset<test::exception::object, test::exception::hash,
|
||||
test::exception::equal_to,
|
||||
test::exception::allocator<test::exception::object> >* test_multiset_;
|
||||
boost::unordered_map<test::exception::object, test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >* test_map_;
|
||||
boost::unordered_multimap<test::exception::object, test::exception::object,
|
||||
test::exception::hash, test::exception::equal_to,
|
||||
test::exception::allocator2<test::exception::object> >* test_multimap_;
|
||||
|
||||
using test::default_generator;
|
||||
using test::generate_collisions;
|
||||
using test::limited_range;
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_MULTI_TEST(set_merge, merge_exception_test,
|
||||
((test_set_)(test_multiset_))
|
||||
((test_set_)(test_multiset_))
|
||||
((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232))
|
||||
((0x0000)(0x0001)(0x0102))
|
||||
((default_generator)(limited_range))
|
||||
((default_generator)(limited_range))
|
||||
)
|
||||
UNORDERED_MULTI_TEST(map_merge, merge_exception_test,
|
||||
((test_map_)(test_multimap_))
|
||||
((test_map_)(test_multimap_))
|
||||
((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(set_merge_collisions, merge_exception_test,
|
||||
((test_set_)(test_multiset_))
|
||||
((test_set_)(test_multiset_))
|
||||
((0x0a0a))
|
||||
((0x0202)(0x0100)(0x0201))
|
||||
((generate_collisions))
|
||||
((generate_collisions))
|
||||
)
|
||||
UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test,
|
||||
((test_map_)(test_multimap_))
|
||||
((test_map_)(test_multimap_))
|
||||
((0x0a0a))
|
||||
((0x0000)(0x0002)(0x0102))
|
||||
((generate_collisions))
|
||||
((generate_collisions))
|
||||
)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS_QUIET()
|
||||
@@ -1,132 +0,0 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// 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/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning( \
|
||||
disable : 4512) // move_assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
test::seed_t initialize_seed(12847);
|
||||
|
||||
template <class T> struct move_assign_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> x_values, y_values;
|
||||
T x, y;
|
||||
|
||||
typedef typename T::hasher hasher;
|
||||
typedef typename T::key_equal key_equal;
|
||||
typedef typename T::allocator_type allocator_type;
|
||||
|
||||
move_assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
||||
: x_values(), y_values(),
|
||||
x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)),
|
||||
y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2))
|
||||
{
|
||||
x.max_load_factor(mlf1);
|
||||
y.max_load_factor(mlf2);
|
||||
}
|
||||
|
||||
typedef T data_type;
|
||||
T init() const { return T(x); }
|
||||
void run(T& x1) const
|
||||
{
|
||||
test::exceptions_enable disable_exceptions(false);
|
||||
T y1 = y;
|
||||
disable_exceptions.release();
|
||||
x1 = boost::move(y1);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x1, y_values);
|
||||
test::check_equivalent_keys(x1);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x1) const
|
||||
{
|
||||
test::check_equivalent_keys(x1);
|
||||
|
||||
// If the container is empty at the point of the exception, the
|
||||
// internal structure is hidden, this exposes it, at the cost of
|
||||
// messing up the data.
|
||||
if (x_values.size()) {
|
||||
T& x2 = const_cast<T&>(x1);
|
||||
x2.emplace(*x_values.begin());
|
||||
test::check_equivalent_keys(x2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_values : move_assign_base<T>
|
||||
{
|
||||
move_assign_values(unsigned int count1, unsigned int count2, int tag1,
|
||||
int tag2, float mlf1 = 1.0, float mlf2 = 1.0)
|
||||
: move_assign_base<T>(tag1, tag2, mlf1, mlf2)
|
||||
{
|
||||
this->x_values.fill(count1, test::limited_range);
|
||||
this->y_values.fill(count2, test::limited_range);
|
||||
this->x.insert(this->x_values.begin(), this->x_values.end());
|
||||
this->y.insert(this->y_values.begin(), this->y_values.end());
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test1 : move_assign_values<T>
|
||||
{
|
||||
move_assign_test1() : move_assign_values<T>(0, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test2 : move_assign_values<T>
|
||||
{
|
||||
move_assign_test2() : move_assign_values<T>(60, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test3 : move_assign_values<T>
|
||||
{
|
||||
move_assign_test3() : move_assign_values<T>(0, 60, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test4 : move_assign_values<T>
|
||||
{
|
||||
move_assign_test4() : move_assign_values<T>(10, 10, 1, 2) {}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test4a : move_assign_values<T>
|
||||
{
|
||||
move_assign_test4a() : move_assign_values<T>(10, 100, 1, 2) {}
|
||||
};
|
||||
|
||||
template <class T> struct move_assign_test5 : move_assign_values<T>
|
||||
{
|
||||
move_assign_test5() : move_assign_values<T>(5, 60, 0, 0, 1.0f, 0.1f) {}
|
||||
};
|
||||
|
||||
template <class T> struct equivalent_test1 : move_assign_base<T>
|
||||
{
|
||||
equivalent_test1() : move_assign_base<T>(0, 0)
|
||||
{
|
||||
test::random_values<T> x_values2(10, test::limited_range);
|
||||
this->x_values.insert(x_values2.begin(), x_values2.end());
|
||||
this->x_values.insert(x_values2.begin(), x_values2.end());
|
||||
test::random_values<T> y_values2(10, test::limited_range);
|
||||
this->y_values.insert(y_values2.begin(), y_values2.end());
|
||||
this->y_values.insert(y_values2.begin(), y_values2.end());
|
||||
this->x.insert(this->x_values.begin(), this->x_values.end());
|
||||
this->y.insert(this->y_values.begin(), this->y_values.end());
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS(
|
||||
(move_assign_test1)(move_assign_test2)(move_assign_test3)
|
||||
(move_assign_test4)(move_assign_test4a)(move_assign_test5)
|
||||
(equivalent_test1),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -4,130 +4,82 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/strong.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include <string>
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/strong.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
test::seed_t initialize_seed(3298597);
|
||||
|
||||
template <class T> struct rehash_test_base : public test::exception_base
|
||||
template <class T>
|
||||
struct rehash_test_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
unsigned int n;
|
||||
rehash_test_base(unsigned int count = 100, unsigned int n_ = 0)
|
||||
: values(count, test::limited_range), n(n_)
|
||||
{
|
||||
}
|
||||
test::random_values<T> values;
|
||||
unsigned int n;
|
||||
rehash_test_base(unsigned int count = 100, unsigned int n = 0)
|
||||
: values(count), n(n)
|
||||
{}
|
||||
|
||||
typedef T data_type;
|
||||
typedef test::strong<T> strong_type;
|
||||
typedef T data_type;
|
||||
typedef test::strong<T> strong_type;
|
||||
|
||||
data_type init() const
|
||||
{
|
||||
T x(values.begin(), values.end(), n);
|
||||
return x;
|
||||
}
|
||||
data_type init() const {
|
||||
T x(values.begin(), values.end(), n);
|
||||
return x;
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(
|
||||
T const& x, strong_type const& strong) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x,
|
||||
strong_type const& strong) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
if (scope.find("hash::operator()") == std::string::npos &&
|
||||
scope.find("equal_to::operator()") == std::string::npos &&
|
||||
scope != "operator==(object, object)")
|
||||
strong.test(x);
|
||||
if(scope.find("hash::operator()") == std::string::npos &&
|
||||
scope.find("equal_to::operator()") == std::string::npos &&
|
||||
scope != "operator==(object, object)")
|
||||
strong.test(x);
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test0 : rehash_test_base<T>
|
||||
template <class T>
|
||||
struct rehash_test0 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test0() : rehash_test_base<T>(0) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(0);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
rehash_test0() : rehash_test_base<T>(0) {}
|
||||
void run(T& x) const { x.rehash(0); }
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test1 : rehash_test_base<T>
|
||||
template <class T>
|
||||
struct rehash_test1 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test1() : rehash_test_base<T>(0) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(200);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
rehash_test1() : rehash_test_base<T>(0) {}
|
||||
void run(T& x) const { x.rehash(200); }
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test2 : rehash_test_base<T>
|
||||
template <class T>
|
||||
struct rehash_test2 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test2() : rehash_test_base<T>(0, 200) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(0);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
rehash_test2() : rehash_test_base<T>(0, 200) {}
|
||||
void run(T& x) const { x.rehash(0); }
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test3 : rehash_test_base<T>
|
||||
template <class T>
|
||||
struct rehash_test3 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test3() : rehash_test_base<T>(10, 0) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(200);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
rehash_test3() : rehash_test_base<T>(10, 0) {}
|
||||
void run(T& x) const { x.rehash(200); }
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test4 : rehash_test_base<T>
|
||||
template <class T>
|
||||
struct rehash_test4 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test4() : rehash_test_base<T>(10, 200) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(0);
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
rehash_test4() : rehash_test_base<T>(10, 200) {}
|
||||
void run(T& x) const { x.rehash(0); }
|
||||
};
|
||||
|
||||
template <class T> struct rehash_test5 : rehash_test_base<T>
|
||||
{
|
||||
rehash_test5() : rehash_test_base<T>(200, 10) {}
|
||||
void run(T& x) const
|
||||
{
|
||||
x.rehash(0);
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4),
|
||||
CONTAINER_SEQ)
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS(
|
||||
(rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4)
|
||||
(rehash_test5),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -4,142 +4,121 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "./containers.hpp"
|
||||
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(disable : 4512) // assignment operator could not be generated
|
||||
#pragma warning(disable:4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
test::seed_t initialize_seed(9387);
|
||||
|
||||
template <class T> struct self_swap_base : public test::exception_base
|
||||
template <class T>
|
||||
struct self_swap_base : public test::exception_base
|
||||
{
|
||||
test::random_values<T> values;
|
||||
self_swap_base(std::size_t count = 0) : values(count, test::limited_range) {}
|
||||
test::random_values<T> values;
|
||||
self_swap_base(int count = 0) : values(count) {}
|
||||
|
||||
typedef T data_type;
|
||||
T init() const { return T(values.begin(), values.end()); }
|
||||
typedef T data_type;
|
||||
T init() const { return T(values.begin(), values.end()); }
|
||||
void run(T& x) const { x.swap(x); }
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const {
|
||||
std::string scope(test::scope);
|
||||
|
||||
void run(T& x) const
|
||||
{
|
||||
x.swap(x);
|
||||
// TODO: In C++11 exceptions are only allowed in the swap function.
|
||||
BOOST_TEST(
|
||||
scope == "hash::hash(hash)" ||
|
||||
scope == "hash::operator=(hash)" ||
|
||||
scope == "equal_to::equal_to(equal_to)" ||
|
||||
scope == "equal_to::operator=(equal_to)");
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(x, this->values);
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
// TODO: In C++11 exceptions are only allowed in the swap function.
|
||||
BOOST_TEST(scope == "hash::hash(hash)" ||
|
||||
scope == "hash::operator=(hash)" ||
|
||||
scope == "equal_to::equal_to(equal_to)" ||
|
||||
scope == "equal_to::operator=(equal_to)");
|
||||
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct self_swap_test1 : self_swap_base<T>
|
||||
{
|
||||
};
|
||||
|
||||
template <class T> struct self_swap_test2 : self_swap_base<T>
|
||||
{
|
||||
self_swap_test2() : self_swap_base<T>(100) {}
|
||||
};
|
||||
|
||||
template <class T> struct swap_base : public test::exception_base
|
||||
{
|
||||
const test::random_values<T> x_values, y_values;
|
||||
const T initial_x, initial_y;
|
||||
|
||||
typedef typename T::hasher hasher;
|
||||
typedef typename T::key_equal key_equal;
|
||||
typedef typename T::allocator_type allocator_type;
|
||||
|
||||
swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2)
|
||||
: x_values(count1, test::limited_range),
|
||||
y_values(count2, test::limited_range),
|
||||
initial_x(x_values.begin(), x_values.end(), 0, hasher(tag1),
|
||||
key_equal(tag1), allocator_type(tag1)),
|
||||
initial_y(y_values.begin(), y_values.end(), 0, hasher(tag2),
|
||||
key_equal(tag2),
|
||||
allocator_type(T::allocator_type::propagate_on_container_swap::value
|
||||
? tag2
|
||||
: tag1))
|
||||
{
|
||||
}
|
||||
|
||||
struct data_type
|
||||
{
|
||||
data_type(T const& x_, T const& y_) : x(x_), y(y_) {}
|
||||
|
||||
T x, y;
|
||||
};
|
||||
|
||||
data_type init() const { return data_type(initial_x, initial_y); }
|
||||
|
||||
void run(data_type& d) const
|
||||
{
|
||||
try {
|
||||
d.x.swap(d.y);
|
||||
} catch (std::runtime_error&) {
|
||||
test::check_equivalent_keys(x);
|
||||
}
|
||||
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_container(d.x, this->y_values);
|
||||
test::check_equivalent_keys(d.x);
|
||||
test::check_container(d.y, this->x_values);
|
||||
test::check_equivalent_keys(d.y);
|
||||
}
|
||||
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const
|
||||
{
|
||||
std::string scope(test::scope);
|
||||
|
||||
// TODO: In C++11 exceptions are only allowed in the swap function.
|
||||
BOOST_TEST(scope == "hash::hash(hash)" ||
|
||||
scope == "hash::operator=(hash)" ||
|
||||
scope == "equal_to::equal_to(equal_to)" ||
|
||||
scope == "equal_to::operator=(equal_to)");
|
||||
|
||||
test::check_equivalent_keys(d.x);
|
||||
test::check_equivalent_keys(d.y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct swap_test1 : swap_base<T>
|
||||
template <class T>
|
||||
struct self_swap_test1 : self_swap_base<T> {};
|
||||
|
||||
template <class T>
|
||||
struct self_swap_test2 : self_swap_base<T>
|
||||
{
|
||||
swap_test1() : swap_base<T>(0, 0, 0, 0) {}
|
||||
self_swap_test2() : self_swap_base<T>(100) {}
|
||||
};
|
||||
|
||||
template <class T> struct swap_test2 : swap_base<T>
|
||||
template <class T>
|
||||
struct swap_base : public test::exception_base
|
||||
{
|
||||
swap_test2() : swap_base<T>(60, 0, 0, 0) {}
|
||||
const test::random_values<T> x_values, y_values;
|
||||
const T initial_x, initial_y;
|
||||
|
||||
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::key_equal key_equal;
|
||||
typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type;
|
||||
|
||||
swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2)
|
||||
: x_values(count1), y_values(count2),
|
||||
initial_x(x_values.begin(), x_values.end(), 0, hasher(tag1),
|
||||
key_equal(tag1), allocator_type(tag1)),
|
||||
initial_y(y_values.begin(), y_values.end(), 0, hasher(tag2),
|
||||
key_equal(tag2), allocator_type(
|
||||
T::allocator_type::propagate_on_container_swap::value ?
|
||||
tag2 : tag1))
|
||||
{}
|
||||
|
||||
struct data_type {
|
||||
data_type(T const& x, T const& y)
|
||||
: x(x), y(y) {}
|
||||
|
||||
T x, y;
|
||||
};
|
||||
|
||||
data_type init() const { return data_type(initial_x, initial_y); }
|
||||
|
||||
void run(data_type& d) const {
|
||||
try {
|
||||
d.x.swap(d.y);
|
||||
} catch (std::runtime_error) {}
|
||||
}
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const {
|
||||
std::string scope(test::scope);
|
||||
|
||||
// TODO: In C++11 exceptions are only allowed in the swap function.
|
||||
BOOST_TEST(
|
||||
scope == "hash::hash(hash)" ||
|
||||
scope == "hash::operator=(hash)" ||
|
||||
scope == "equal_to::equal_to(equal_to)" ||
|
||||
scope == "equal_to::operator=(equal_to)");
|
||||
|
||||
test::check_equivalent_keys(d.x);
|
||||
test::check_equivalent_keys(d.y);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct swap_test3 : swap_base<T>
|
||||
template <class T>
|
||||
struct swap_test1 : swap_base<T>
|
||||
{
|
||||
swap_test3() : swap_base<T>(0, 60, 0, 0) {}
|
||||
swap_test1() : swap_base<T>(0, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
template <class T> struct swap_test4 : swap_base<T>
|
||||
template <class T>
|
||||
struct swap_test2 : swap_base<T>
|
||||
{
|
||||
swap_test4() : swap_base<T>(10, 10, 1, 2) {}
|
||||
swap_test2() : swap_base<T>(60, 0, 0, 0) {}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
EXCEPTION_TESTS(
|
||||
(self_swap_test1)(self_swap_test2)
|
||||
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
|
||||
CONTAINER_SEQ)
|
||||
// clang-format on
|
||||
template <class T>
|
||||
struct swap_test3 : swap_base<T>
|
||||
{
|
||||
swap_test3() : swap_base<T>(0, 60, 0, 0) {}
|
||||
};
|
||||
|
||||
RUN_TESTS()
|
||||
template <class T>
|
||||
struct swap_test4 : swap_base<T>
|
||||
{
|
||||
swap_test4() : swap_base<T>(10, 10, 1, 2) {}
|
||||
};
|
||||
|
||||
RUN_EXCEPTION_TESTS(
|
||||
(self_swap_test1)(self_swap_test2)
|
||||
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
|
||||
CONTAINER_SEQ)
|
||||
|
||||
@@ -7,27 +7,32 @@
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
|
||||
namespace test {
|
||||
template <class T1> struct check_return_type
|
||||
{
|
||||
template <class T2> static void equals(T2)
|
||||
namespace test
|
||||
{
|
||||
template <class T1>
|
||||
struct check_return_type
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
|
||||
}
|
||||
template <class T2>
|
||||
static void equals(T2)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
|
||||
}
|
||||
|
||||
template <class T2> static void equals_ref(T2&)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
|
||||
}
|
||||
template <class T2>
|
||||
static void equals_ref(T2&)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
|
||||
}
|
||||
|
||||
template <class T2> static void convertible(T2)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_convertible<T2, T1>::value));
|
||||
}
|
||||
};
|
||||
template <class T2>
|
||||
static void convertible(T2)
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_convertible<T2, T1>::value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+61
-71
@@ -6,84 +6,74 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace test {
|
||||
struct object_count
|
||||
{
|
||||
int instances;
|
||||
int constructions;
|
||||
struct object_count {
|
||||
int instances;
|
||||
int constructions;
|
||||
|
||||
object_count() : instances(0), constructions(0) {}
|
||||
void reset() { *this = object_count(); }
|
||||
object_count() : instances(0), constructions(0) {}
|
||||
void reset() { *this = object_count(); }
|
||||
|
||||
void construct()
|
||||
{
|
||||
++instances;
|
||||
++constructions;
|
||||
void construct() {
|
||||
++instances;
|
||||
++constructions;
|
||||
}
|
||||
|
||||
void destruct() {
|
||||
if(instances == 0) {
|
||||
BOOST_ERROR("Unbalanced constructions.");
|
||||
}
|
||||
else {
|
||||
--instances;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(object_count const& x) const {
|
||||
return instances == x.instances &&
|
||||
constructions == x.constructions;
|
||||
}
|
||||
|
||||
bool operator!=(object_count const& x) const {
|
||||
return !(*this == x);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out,
|
||||
object_count const& c)
|
||||
{
|
||||
out
|
||||
<< "[instances: "
|
||||
<< c.instances
|
||||
<< ", constructions: "
|
||||
<< c.constructions
|
||||
<< "]";
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually require by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
object_count global_object_count;
|
||||
}
|
||||
|
||||
void destruct()
|
||||
|
||||
struct counted_object
|
||||
{
|
||||
if (instances == 0) {
|
||||
BOOST_ERROR("Unbalanced constructions.");
|
||||
} else {
|
||||
--instances;
|
||||
}
|
||||
}
|
||||
counted_object() { global_object_count.construct(); }
|
||||
counted_object(counted_object const&) { global_object_count.construct(); }
|
||||
~counted_object() { global_object_count.destruct(); }
|
||||
};
|
||||
|
||||
bool operator==(object_count const& x) const
|
||||
{
|
||||
return instances == x.instances && constructions == x.constructions;
|
||||
}
|
||||
|
||||
bool operator!=(object_count const& x) const { return !(*this == x); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, object_count const& c)
|
||||
{
|
||||
out << "[instances: " << c.instances
|
||||
<< ", constructions: " << c.constructions << "]";
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually require by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
object_count global_object_count;
|
||||
}
|
||||
|
||||
struct counted_object
|
||||
{
|
||||
counted_object() { global_object_count.construct(); }
|
||||
counted_object(counted_object const&) { global_object_count.construct(); }
|
||||
~counted_object() { global_object_count.destruct(); }
|
||||
};
|
||||
|
||||
struct check_instances
|
||||
{
|
||||
int instances_;
|
||||
int constructions_;
|
||||
|
||||
check_instances()
|
||||
: instances_(global_object_count.instances),
|
||||
constructions_(global_object_count.constructions)
|
||||
{
|
||||
}
|
||||
~check_instances()
|
||||
{
|
||||
BOOST_TEST(global_object_count.instances == instances_);
|
||||
}
|
||||
|
||||
int instances() const { return global_object_count.instances - instances_; }
|
||||
int constructions() const
|
||||
{
|
||||
return global_object_count.constructions - constructions_;
|
||||
}
|
||||
};
|
||||
struct check_instances {
|
||||
int instances;
|
||||
|
||||
check_instances() : instances(global_object_count.instances) {}
|
||||
~check_instances() { BOOST_TEST(global_object_count.instances == instances); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+78
-76
@@ -6,91 +6,93 @@
|
||||
#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER)
|
||||
#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER
|
||||
|
||||
#include "./fwd.hpp"
|
||||
#include "./list.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <algorithm>
|
||||
#include "./metafunctions.hpp"
|
||||
#include "./fwd.hpp"
|
||||
#include "./list.hpp"
|
||||
|
||||
namespace test {
|
||||
template <class T1, class T2>
|
||||
bool equivalent_impl(T1 const& x, T2 const& y, base_type)
|
||||
{
|
||||
return x == y;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool equivalent_impl(
|
||||
boost::hash<T> const&, boost::hash<T> const&, derived_type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool equivalent_impl(
|
||||
std::equal_to<T> const&, std::equal_to<T> const&, derived_type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
bool equivalent_impl(
|
||||
std::pair<T1, T2> const& x1, std::pair<T3, T4> const& x2, derived_type)
|
||||
{
|
||||
return equivalent_impl(x1.first, x2.first, derived) &&
|
||||
equivalent_impl(x1.second, x2.second, derived);
|
||||
}
|
||||
|
||||
struct equivalent_type
|
||||
{
|
||||
equivalent_type() {}
|
||||
|
||||
namespace test
|
||||
{
|
||||
template <class T1, class T2>
|
||||
bool operator()(T1 const& x, T2 const& y) const
|
||||
{
|
||||
return equivalent_impl(x, y, derived);
|
||||
}
|
||||
};
|
||||
|
||||
const equivalent_type equivalent;
|
||||
|
||||
template <class Container> class unordered_equivalence_tester
|
||||
{
|
||||
typename Container::size_type size_;
|
||||
typename Container::hasher hasher_;
|
||||
typename Container::key_equal key_equal_;
|
||||
float max_load_factor_;
|
||||
|
||||
typedef test::list<typename Container::value_type> value_list;
|
||||
value_list values_;
|
||||
|
||||
public:
|
||||
unordered_equivalence_tester(Container const& x)
|
||||
: size_(x.size()), hasher_(x.hash_function()), key_equal_(x.key_eq()),
|
||||
max_load_factor_(x.max_load_factor()), values_(x.begin(), x.end())
|
||||
{
|
||||
values_.sort();
|
||||
bool equivalent_impl(T1 const& x, T2 const& y, base_type) {
|
||||
return x == y;
|
||||
}
|
||||
|
||||
bool operator()(Container const& x) const
|
||||
template <class T>
|
||||
bool equivalent_impl(boost::hash<T> const&, boost::hash<T> const&,
|
||||
derived_type)
|
||||
{
|
||||
if (!((size_ == x.size()) &&
|
||||
(test::equivalent(hasher_, x.hash_function())) &&
|
||||
(test::equivalent(key_equal_, x.key_eq())) &&
|
||||
(max_load_factor_ == x.max_load_factor()) &&
|
||||
(values_.size() == x.size())))
|
||||
return false;
|
||||
|
||||
value_list copy(x.begin(), x.end());
|
||||
copy.sort();
|
||||
return values_ == copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
unordered_equivalence_tester();
|
||||
};
|
||||
template <class T>
|
||||
bool equivalent_impl(std::equal_to<T> const&, std::equal_to<T> const&,
|
||||
derived_type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
bool equivalent_impl(std::pair<T1, T2> const& x1,
|
||||
std::pair<T3, T4> const& x2, derived_type) {
|
||||
return equivalent_impl(x1.first, x2.first, derived) &&
|
||||
equivalent_impl(x1.second, x2.second, derived);
|
||||
}
|
||||
|
||||
struct equivalent_type {
|
||||
template <class T1, class T2>
|
||||
bool operator()(T1 const& x, T2 const& y) {
|
||||
return equivalent_impl(x, y, derived);
|
||||
}
|
||||
};
|
||||
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually require by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
equivalent_type equivalent;
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
class unordered_equivalence_tester
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME Container::size_type size_;
|
||||
BOOST_DEDUCED_TYPENAME Container::hasher hasher_;
|
||||
BOOST_DEDUCED_TYPENAME Container::key_equal key_equal_;
|
||||
float max_load_factor_;
|
||||
|
||||
typedef test::list<BOOST_DEDUCED_TYPENAME Container::value_type>
|
||||
value_list;
|
||||
value_list values_;
|
||||
public:
|
||||
unordered_equivalence_tester(Container const &x)
|
||||
: size_(x.size()),
|
||||
hasher_(x.hash_function()), key_equal_(x.key_eq()),
|
||||
max_load_factor_(x.max_load_factor()),
|
||||
values_(x.begin(), x.end())
|
||||
{
|
||||
values_.sort();
|
||||
}
|
||||
|
||||
bool operator()(Container const& x) const
|
||||
{
|
||||
if(!((size_ == x.size()) &&
|
||||
(test::equivalent(hasher_, x.hash_function())) &&
|
||||
(test::equivalent(key_equal_, x.key_eq())) &&
|
||||
(max_load_factor_ == x.max_load_factor()) &&
|
||||
(values_.size() == x.size()))) return false;
|
||||
|
||||
value_list copy(x.begin(), x.end());
|
||||
copy.sort();
|
||||
return values_ == copy;
|
||||
}
|
||||
private:
|
||||
unordered_equivalence_tester();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+190
-302
@@ -6,343 +6,231 @@
|
||||
#if !defined(BOOST_UNORDERED_EXCEPTION_TEST_HEADER)
|
||||
#define BOOST_UNORDERED_EXCEPTION_TEST_HEADER
|
||||
|
||||
#include "./count.hpp"
|
||||
#include "./test.hpp"
|
||||
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/seq/elem.hpp>
|
||||
#include <boost/preprocessor/seq/for_each_product.hpp>
|
||||
#include <boost/preprocessor/seq/elem.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
|
||||
#define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type) \
|
||||
UNORDERED_AUTO_TEST (name) { \
|
||||
test_func<type> fixture; \
|
||||
::test::lightweight::exception_safety( \
|
||||
fixture, BOOST_STRINGIZE(test_func<type>)); \
|
||||
}
|
||||
# define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type) \
|
||||
UNORDERED_AUTO_TEST(name) \
|
||||
{ \
|
||||
test_func< type > fixture; \
|
||||
::test::lightweight::exception_safety( \
|
||||
fixture, BOOST_STRINGIZE(test_func<type>)); \
|
||||
} \
|
||||
|
||||
#define UNORDERED_EXCEPTION_TEST_CASE_REPEAT(name, test_func, n, type) \
|
||||
UNORDERED_AUTO_TEST (name) { \
|
||||
for (unsigned i = 0; i < n; ++i) { \
|
||||
test_func<type> fixture; \
|
||||
::test::lightweight::exception_safety( \
|
||||
fixture, BOOST_STRINGIZE(test_func<type>)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint
|
||||
# define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint
|
||||
|
||||
#define UNORDERED_EXCEPTION_TEST_POSTFIX RUN_TESTS()
|
||||
|
||||
#define EXCEPTION_TESTS(test_seq, param_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((1))(param_seq))
|
||||
#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, \
|
||||
(test_seq)(param_seq)) \
|
||||
RUN_TESTS() \
|
||||
|
||||
#define EXCEPTION_TESTS_REPEAT(n, test_seq, param_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((n))(param_seq))
|
||||
#define RUN_EXCEPTION_TESTS_OP(r, product) \
|
||||
UNORDERED_EXCEPTION_TEST_CASE( \
|
||||
BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \
|
||||
), \
|
||||
BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_SEQ_ELEM(1, product) \
|
||||
) \
|
||||
|
||||
#define EXCEPTION_TESTS_OP(r, product) \
|
||||
UNORDERED_EXCEPTION_TEST_CASE_REPEAT( \
|
||||
BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(2, product))), \
|
||||
BOOST_PP_SEQ_ELEM(0, product), BOOST_PP_SEQ_ELEM(1, product), \
|
||||
BOOST_PP_SEQ_ELEM(2, product))
|
||||
#define UNORDERED_SCOPE(scope_name) \
|
||||
for(::test::scope_guard unordered_test_guard( \
|
||||
BOOST_STRINGIZE(scope_name)); \
|
||||
!unordered_test_guard.dismissed(); \
|
||||
unordered_test_guard.dismiss()) \
|
||||
|
||||
#define UNORDERED_SCOPE(scope_name) \
|
||||
for (::test::scope_guard unordered_test_guard(BOOST_STRINGIZE(scope_name)); \
|
||||
!unordered_test_guard.dismissed(); unordered_test_guard.dismiss())
|
||||
#define UNORDERED_EPOINT(name) \
|
||||
if(::test::exceptions_enabled) { \
|
||||
UNORDERED_EPOINT_IMPL(name); \
|
||||
} \
|
||||
|
||||
#define UNORDERED_EPOINT(name) \
|
||||
if (::test::exceptions_enabled) { \
|
||||
UNORDERED_EPOINT_IMPL(name); \
|
||||
}
|
||||
#define ENABLE_EXCEPTIONS \
|
||||
::test::exceptions_enable BOOST_PP_CAT( \
|
||||
ENABLE_EXCEPTIONS_, __LINE__)(true) \
|
||||
|
||||
#define ENABLE_EXCEPTIONS \
|
||||
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true)
|
||||
|
||||
#define DISABLE_EXCEPTIONS \
|
||||
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false)
|
||||
#define DISABLE_EXCEPTIONS \
|
||||
::test::exceptions_enable BOOST_PP_CAT( \
|
||||
ENABLE_EXCEPTIONS_, __LINE__)(false) \
|
||||
|
||||
namespace test {
|
||||
static char const* scope = "";
|
||||
bool exceptions_enabled = false;
|
||||
static char const* scope = "";
|
||||
bool exceptions_enabled = false;
|
||||
|
||||
class scope_guard
|
||||
{
|
||||
scope_guard& operator=(scope_guard const&);
|
||||
scope_guard(scope_guard const&);
|
||||
class scope_guard {
|
||||
scope_guard& operator=(scope_guard const&);
|
||||
scope_guard(scope_guard const&);
|
||||
|
||||
char const* old_scope_;
|
||||
char const* scope_;
|
||||
bool dismissed_;
|
||||
|
||||
public:
|
||||
scope_guard(char const* name)
|
||||
: old_scope_(scope), scope_(name), dismissed_(false)
|
||||
{
|
||||
scope = scope_;
|
||||
}
|
||||
|
||||
~scope_guard()
|
||||
{
|
||||
if (dismissed_)
|
||||
scope = old_scope_;
|
||||
}
|
||||
|
||||
void dismiss() { dismissed_ = true; }
|
||||
|
||||
bool dismissed() const { return dismissed_; }
|
||||
};
|
||||
|
||||
class exceptions_enable
|
||||
{
|
||||
exceptions_enable& operator=(exceptions_enable const&);
|
||||
exceptions_enable(exceptions_enable const&);
|
||||
|
||||
bool old_value_;
|
||||
bool released_;
|
||||
|
||||
public:
|
||||
exceptions_enable(bool enable)
|
||||
: old_value_(exceptions_enabled), released_(false)
|
||||
{
|
||||
exceptions_enabled = enable;
|
||||
}
|
||||
|
||||
~exceptions_enable()
|
||||
{
|
||||
if (!released_) {
|
||||
exceptions_enabled = old_value_;
|
||||
released_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (!released_) {
|
||||
exceptions_enabled = old_value_;
|
||||
released_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct exception_base
|
||||
{
|
||||
struct data_type
|
||||
{
|
||||
};
|
||||
struct strong_type
|
||||
{
|
||||
template <class T> void store(T const&) {}
|
||||
template <class T> void test(T const&) const {}
|
||||
};
|
||||
data_type init() const { return data_type(); }
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {}
|
||||
};
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)() const, T2 const& obj, P1&, P2&)
|
||||
{
|
||||
(obj.*fn)();
|
||||
}
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)(P1&) const, T2 const& obj, P1& p1, P2&)
|
||||
{
|
||||
(obj.*fn)(p1);
|
||||
}
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)(P1&, P2&) const, T2 const& obj, P1& p1, P2& p2)
|
||||
{
|
||||
(obj.*fn)(p1, p2);
|
||||
}
|
||||
|
||||
template <class T> T const& constant(T const& x) { return x; }
|
||||
|
||||
template <class Test> class test_runner
|
||||
{
|
||||
Test const& test_;
|
||||
bool exception_in_check_;
|
||||
|
||||
test_runner(test_runner const&);
|
||||
test_runner& operator=(test_runner const&);
|
||||
|
||||
public:
|
||||
test_runner(Test const& t) : test_(t), exception_in_check_(false) {}
|
||||
void run()
|
||||
{
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::check_instances check;
|
||||
test::scope = "";
|
||||
typename Test::data_type x(test_.init());
|
||||
typename Test::strong_type strong;
|
||||
strong.store(x);
|
||||
try {
|
||||
ENABLE_EXCEPTIONS;
|
||||
call_ignore_extra_parameters<Test, typename Test::data_type,
|
||||
typename Test::strong_type>(&Test::run, test_, x, strong);
|
||||
} catch (...) {
|
||||
try {
|
||||
DISABLE_EXCEPTIONS;
|
||||
call_ignore_extra_parameters<Test, typename Test::data_type const,
|
||||
typename Test::strong_type const>(
|
||||
&Test::check, test_, constant(x), constant(strong));
|
||||
} catch (...) {
|
||||
exception_in_check_ = true;
|
||||
char const* old_scope_;
|
||||
char const* scope_;
|
||||
bool dismissed_;
|
||||
public:
|
||||
scope_guard(char const* name)
|
||||
: old_scope_(scope),
|
||||
scope_(name),
|
||||
dismissed_(false)
|
||||
{
|
||||
scope = scope_;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
void end()
|
||||
{
|
||||
if (exception_in_check_) {
|
||||
BOOST_ERROR("Unexcpected exception in test_runner check call.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Quick exception testing based on lightweight test
|
||||
~scope_guard() {
|
||||
if(dismissed_) scope = old_scope_;
|
||||
}
|
||||
|
||||
namespace lightweight {
|
||||
static int iteration;
|
||||
static int count;
|
||||
void dismiss() {
|
||||
dismissed_ = true;
|
||||
}
|
||||
|
||||
struct test_exception
|
||||
{
|
||||
char const* name;
|
||||
test_exception(char const* n) : name(n) {}
|
||||
bool dismissed() const {
|
||||
return dismissed_;
|
||||
}
|
||||
};
|
||||
|
||||
struct test_failure
|
||||
class exceptions_enable
|
||||
{
|
||||
exceptions_enable& operator=(exceptions_enable const&);
|
||||
exceptions_enable(exceptions_enable const&);
|
||||
|
||||
bool old_value_;
|
||||
public:
|
||||
exceptions_enable(bool enable)
|
||||
: old_value_(exceptions_enabled)
|
||||
{
|
||||
exceptions_enabled = enable;
|
||||
}
|
||||
|
||||
~exceptions_enable()
|
||||
{
|
||||
exceptions_enabled = old_value_;
|
||||
}
|
||||
};
|
||||
|
||||
void epoint(char const* name)
|
||||
struct exception_base {
|
||||
struct data_type {};
|
||||
struct strong_type {
|
||||
template <class T> void store(T const&) {}
|
||||
template <class T> void test(T const&) const {}
|
||||
};
|
||||
data_type init() const { return data_type(); }
|
||||
void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {}
|
||||
};
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)() const, T2 const& obj,
|
||||
P1&, P2&)
|
||||
{
|
||||
++count;
|
||||
if (count == iteration) {
|
||||
throw test_exception(name);
|
||||
}
|
||||
(obj.*fn)();
|
||||
}
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)(P1&) const, T2 const& obj,
|
||||
P1& p1, P2&)
|
||||
{
|
||||
(obj.*fn)(p1);
|
||||
}
|
||||
|
||||
template <class T, class P1, class P2, class T2>
|
||||
inline void call_ignore_extra_parameters(
|
||||
void (T::*fn)(P1&, P2&) const, T2 const& obj,
|
||||
P1& p1, P2& p2)
|
||||
{
|
||||
(obj.*fn)(p1, p2);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T const& constant(T const& x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
template <class Test>
|
||||
void exception_safety(Test const& f, char const* /*name*/)
|
||||
class test_runner
|
||||
{
|
||||
test_runner<Test> runner(f);
|
||||
Test const& test_;
|
||||
|
||||
iteration = 0;
|
||||
bool success = false;
|
||||
unsigned int failure_count = 0;
|
||||
char const* error_msg = 0;
|
||||
do {
|
||||
int error_count = boost::detail::test_errors();
|
||||
++iteration;
|
||||
count = 0;
|
||||
|
||||
try {
|
||||
runner.run();
|
||||
success = true;
|
||||
} catch (test_failure) {
|
||||
error_msg = "test_failure caught.";
|
||||
break;
|
||||
} catch (test_exception e) {
|
||||
if (error_count != boost::detail::test_errors()) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< "Iteration: " << iteration
|
||||
<< " Error found for epoint: " << e.name << std::endl;
|
||||
}
|
||||
} catch (...) {
|
||||
error_msg = "Unexpected exception.";
|
||||
break;
|
||||
test_runner(test_runner const&);
|
||||
test_runner& operator=(test_runner const&);
|
||||
public:
|
||||
test_runner(Test const& t) : test_(t) {}
|
||||
void operator()() const {
|
||||
DISABLE_EXCEPTIONS;
|
||||
test::scope = "";
|
||||
BOOST_DEDUCED_TYPENAME Test::data_type x(test_.init());
|
||||
BOOST_DEDUCED_TYPENAME Test::strong_type strong;
|
||||
strong.store(x);
|
||||
try {
|
||||
ENABLE_EXCEPTIONS;
|
||||
call_ignore_extra_parameters<
|
||||
Test,
|
||||
BOOST_DEDUCED_TYPENAME Test::data_type,
|
||||
BOOST_DEDUCED_TYPENAME Test::strong_type
|
||||
>(&Test::run, test_, x, strong);
|
||||
}
|
||||
catch(...) {
|
||||
call_ignore_extra_parameters<
|
||||
Test,
|
||||
BOOST_DEDUCED_TYPENAME Test::data_type const,
|
||||
BOOST_DEDUCED_TYPENAME Test::strong_type const
|
||||
>(&Test::check, test_, constant(x), constant(strong));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_count != boost::detail::test_errors()) {
|
||||
++failure_count;
|
||||
}
|
||||
} while (!success && failure_count < 5);
|
||||
|
||||
if (error_msg) {
|
||||
BOOST_ERROR(error_msg);
|
||||
}
|
||||
runner.end();
|
||||
}
|
||||
|
||||
//
|
||||
// An alternative way to run exception tests.
|
||||
// See merge_exception_tests.cpp for an example.
|
||||
|
||||
struct exception_looper
|
||||
{
|
||||
bool success;
|
||||
unsigned int failure_count;
|
||||
char const* error_msg;
|
||||
int error_count;
|
||||
|
||||
exception_looper() : success(false), failure_count(0), error_msg(0) {}
|
||||
|
||||
void start() { iteration = 0; }
|
||||
|
||||
bool loop_condition() const
|
||||
{
|
||||
return !error_msg && !success && failure_count < 5;
|
||||
}
|
||||
|
||||
void start_iteration()
|
||||
{
|
||||
error_count = boost::detail::test_errors();
|
||||
++iteration;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void successful_run() { success = true; }
|
||||
|
||||
void test_failure_caught(test_failure const&)
|
||||
{
|
||||
error_msg = "test_failure caught.";
|
||||
}
|
||||
|
||||
void test_exception_caught(test_exception const& e)
|
||||
{
|
||||
if (error_count != boost::detail::test_errors()) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< "Iteration: " << iteration
|
||||
<< " Error found for epoint: " << e.name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void unexpected_exception_caught()
|
||||
{
|
||||
error_msg = "Unexpected exception.";
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
if (error_msg) {
|
||||
BOOST_ERROR(error_msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define EXCEPTION_LOOP(op) \
|
||||
test::lightweight::exception_looper looper; \
|
||||
looper.start(); \
|
||||
while (looper.loop_condition()) { \
|
||||
looper.start_iteration(); \
|
||||
try { \
|
||||
op; \
|
||||
looper.successful_run(); \
|
||||
} catch (test::lightweight::test_failure e) { \
|
||||
looper.test_failure_caught(e); \
|
||||
} catch (test::lightweight::test_exception e) { \
|
||||
looper.test_exception_caught(e); \
|
||||
} catch (...) { \
|
||||
looper.unexpected_exception_caught(); \
|
||||
} \
|
||||
} \
|
||||
looper.end();
|
||||
}
|
||||
// Quick exception testing based on lightweight test
|
||||
|
||||
namespace lightweight {
|
||||
static int iteration;
|
||||
static int count;
|
||||
|
||||
struct test_exception {
|
||||
char const* name;
|
||||
test_exception(char const* n) : name(n) {}
|
||||
};
|
||||
|
||||
struct test_failure {
|
||||
};
|
||||
|
||||
void epoint(char const* name) {
|
||||
++count;
|
||||
if(count == iteration) {
|
||||
throw test_exception(name);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Test>
|
||||
void exception_safety(Test const& f, char const* /*name*/) {
|
||||
test_runner<Test> runner(f);
|
||||
|
||||
iteration = 0;
|
||||
bool success = false;
|
||||
do {
|
||||
++iteration;
|
||||
count = 0;
|
||||
|
||||
try {
|
||||
runner();
|
||||
success = true;
|
||||
}
|
||||
catch(test_failure) {
|
||||
BOOST_ERROR("test_failure caught.");
|
||||
break;
|
||||
}
|
||||
catch(test_exception) {
|
||||
continue;
|
||||
}
|
||||
catch(...) {
|
||||
BOOST_ERROR("Unexpected exception.");
|
||||
break;
|
||||
}
|
||||
} while(!success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+10
-18
@@ -8,25 +8,17 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace test {
|
||||
typedef enum {
|
||||
default_generator,
|
||||
generate_collisions,
|
||||
limited_range
|
||||
} random_generator;
|
||||
namespace test
|
||||
{
|
||||
int generate(int const*);
|
||||
char generate(char const*);
|
||||
signed char generate(signed char const*);
|
||||
std::string generate(std::string*);
|
||||
float generate(float const*);
|
||||
|
||||
int generate(int const*, random_generator);
|
||||
char generate(char const*, random_generator);
|
||||
signed char generate(signed char const*, random_generator);
|
||||
std::string generate(std::string const*, random_generator);
|
||||
float generate(float const*, random_generator);
|
||||
|
||||
struct base_type
|
||||
{
|
||||
} base;
|
||||
struct derived_type : base_type
|
||||
{
|
||||
} derived;
|
||||
struct base_type {} base;
|
||||
struct derived_type : base_type {} derived;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+47
-70
@@ -11,83 +11,60 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER
|
||||
|
||||
#include "./fwd.hpp"
|
||||
#include <boost/type_traits/add_const.hpp>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <boost/type_traits/add_const.hpp>
|
||||
#include "./fwd.hpp"
|
||||
|
||||
namespace test {
|
||||
struct seed_t
|
||||
{
|
||||
seed_t(unsigned int x)
|
||||
namespace test
|
||||
{
|
||||
struct seed_t {
|
||||
seed_t(unsigned int x) {
|
||||
using namespace std;
|
||||
srand(x);
|
||||
}
|
||||
};
|
||||
|
||||
inline int generate(int const*)
|
||||
{
|
||||
using namespace std;
|
||||
srand(x);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t random_value(std::size_t max)
|
||||
{
|
||||
using namespace std;
|
||||
return static_cast<std::size_t>(rand()) % max;
|
||||
}
|
||||
|
||||
inline int generate(int const*, random_generator g)
|
||||
{
|
||||
using namespace std;
|
||||
int value = rand();
|
||||
if (g == limited_range) {
|
||||
value = value % 100;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
inline char generate(char const*, random_generator)
|
||||
{
|
||||
using namespace std;
|
||||
return static_cast<char>((rand() >> 1) % (128 - 32) + 32);
|
||||
}
|
||||
|
||||
inline signed char generate(signed char const*, random_generator)
|
||||
{
|
||||
using namespace std;
|
||||
return static_cast<signed char>(rand());
|
||||
}
|
||||
|
||||
inline std::string generate(std::string const*, random_generator g)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
char* char_ptr = 0;
|
||||
|
||||
std::string result;
|
||||
|
||||
if (g == limited_range) {
|
||||
std::size_t length = test::random_value(2) + 2;
|
||||
|
||||
char const* strings[] = {"'vZh(3~ms", "%m", "_Y%U", "N'Y", "4,J_J"};
|
||||
for (std::size_t i = 0; i < length; ++i) {
|
||||
result += strings[random_value(sizeof(strings) / sizeof(strings[0]))];
|
||||
}
|
||||
} else {
|
||||
std::size_t length = test::random_value(10) + 1;
|
||||
for (std::size_t i = 0; i < length; ++i) {
|
||||
result += generate(char_ptr, g);
|
||||
}
|
||||
using namespace std;
|
||||
return rand();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
inline char generate(char const*)
|
||||
{
|
||||
using namespace std;
|
||||
return static_cast<char>((rand() >> 1) % (128-32) + 32);
|
||||
}
|
||||
|
||||
float generate(float const*, random_generator g)
|
||||
{
|
||||
using namespace std;
|
||||
int x = 0;
|
||||
int value = generate(&x, g);
|
||||
return (float)value / (float)RAND_MAX;
|
||||
}
|
||||
inline signed char generate(signed char const*)
|
||||
{
|
||||
using namespace std;
|
||||
return static_cast<signed char>(rand());
|
||||
}
|
||||
|
||||
inline std::string generate(std::string const*)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
char* char_ptr = 0;
|
||||
|
||||
std::string result;
|
||||
|
||||
int length = rand() % 10;
|
||||
for(int i = 0; i < length; ++i)
|
||||
result += generate(char_ptr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float generate(float const*)
|
||||
{
|
||||
using namespace std;
|
||||
return (float) rand() / (float) RAND_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+28
-41
@@ -6,51 +6,38 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_HEADER
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace test {
|
||||
template <class Container> struct get_key_impl
|
||||
{
|
||||
typedef typename Container::key_type key_type;
|
||||
|
||||
static key_type const& get_key(key_type const& x) { return x; }
|
||||
|
||||
template <class T>
|
||||
static key_type const& get_key(std::pair<key_type, T> const& x, char = 0)
|
||||
namespace test
|
||||
{
|
||||
template <class Container>
|
||||
struct get_key_impl
|
||||
{
|
||||
return x.first;
|
||||
}
|
||||
typedef BOOST_DEDUCED_TYPENAME Container::key_type key_type;
|
||||
|
||||
template <class T>
|
||||
static key_type const& get_key(
|
||||
std::pair<key_type const, T> const& x, unsigned char = 0)
|
||||
static key_type const& get_key(key_type const& x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static key_type const& get_key(
|
||||
std::pair<key_type, T> const& x, char = 0)
|
||||
{
|
||||
return x.first;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static key_type const& get_key(std::pair<key_type const, T> const& x,
|
||||
unsigned char = 0)
|
||||
{
|
||||
return x.first;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Container, class T>
|
||||
inline BOOST_DEDUCED_TYPENAME Container::key_type const& get_key(T const& x)
|
||||
{
|
||||
return x.first;
|
||||
return get_key_impl<Container>::get_key(x);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Container, class T>
|
||||
inline typename Container::key_type const& get_key(T const& x)
|
||||
{
|
||||
return get_key_impl<Container>::get_key(x);
|
||||
}
|
||||
|
||||
// test::next
|
||||
//
|
||||
// Increments an iterator by 1 or a given value.
|
||||
// Like boost::next, but simpler.
|
||||
// Mainly because boost::next uses an MPL file
|
||||
// which causes warnings.
|
||||
|
||||
template <typename Iterator> Iterator next(Iterator it) { return ++it; }
|
||||
|
||||
template <typename Iterator, typename IntType>
|
||||
Iterator next(Iterator it, IntType x)
|
||||
{
|
||||
std::advance(it,
|
||||
static_cast<typename std::iterator_traits<Iterator>::difference_type>(x));
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+143
-143
@@ -7,159 +7,159 @@
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <iterator>
|
||||
#include <boost/iterator.hpp>
|
||||
#include <boost/iterator/iterator_traits.hpp>
|
||||
|
||||
namespace test {
|
||||
template <class Iterator> struct proxy
|
||||
{
|
||||
typedef typename Iterator::value_type value_type;
|
||||
|
||||
explicit proxy(value_type const& v) : v_(v) {}
|
||||
proxy(proxy const& x) : v_(x.v_) {}
|
||||
operator value_type const&() const { return v_; }
|
||||
|
||||
value_type v_;
|
||||
|
||||
private:
|
||||
proxy& operator=(proxy const&);
|
||||
};
|
||||
|
||||
template <class Iterator> struct input_iterator_adaptor
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type value_type;
|
||||
typedef typename std::iterator_traits<Iterator>::pointer pointer;
|
||||
typedef proxy<Iterator> reference;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
|
||||
input_iterator_adaptor() : base_() {}
|
||||
explicit input_iterator_adaptor(Iterator& it) : base_(&it) {}
|
||||
proxy<Iterator> operator*() const { return proxy<Iterator>(**base_); }
|
||||
value_type* operator->() const { return &**base_; }
|
||||
input_iterator_adaptor& operator++()
|
||||
namespace test
|
||||
{
|
||||
template <class Iterator>
|
||||
struct proxy
|
||||
{
|
||||
++*base_;
|
||||
return *this;
|
||||
}
|
||||
// input_iterator_adaptor operator++(int) {
|
||||
//}
|
||||
bool operator==(input_iterator_adaptor const& x) const
|
||||
typedef BOOST_DEDUCED_TYPENAME Iterator::value_type value_type;
|
||||
|
||||
explicit proxy(value_type const& v) : v_(v) {}
|
||||
proxy(proxy const& x) : v_(x.v_) {}
|
||||
operator value_type const&() const { return v_; }
|
||||
|
||||
value_type v_;
|
||||
private:
|
||||
proxy& operator=(proxy const&);
|
||||
};
|
||||
|
||||
template <class Iterator>
|
||||
struct input_iterator_adaptor
|
||||
: public boost::iterator<
|
||||
std::input_iterator_tag,
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_value<Iterator>::type,
|
||||
std::ptrdiff_t,
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_pointer<Iterator>::type,
|
||||
proxy<Iterator>
|
||||
>
|
||||
{
|
||||
return *base_ == *x.base_;
|
||||
}
|
||||
bool operator!=(input_iterator_adaptor const& x) const
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::iterator_value<Iterator>::type
|
||||
value_type;
|
||||
|
||||
input_iterator_adaptor()
|
||||
: base_() {}
|
||||
explicit input_iterator_adaptor(Iterator& it)
|
||||
: base_(&it) {}
|
||||
proxy<Iterator> operator*() const {
|
||||
return proxy<Iterator>(**base_);
|
||||
}
|
||||
value_type* operator->() const {
|
||||
return &**base_;
|
||||
}
|
||||
input_iterator_adaptor& operator++() {
|
||||
++*base_; return *this;
|
||||
}
|
||||
//input_iterator_adaptor operator++(int) {
|
||||
//}
|
||||
bool operator==(input_iterator_adaptor const& x) const {
|
||||
return *base_ == *x.base_;
|
||||
}
|
||||
bool operator!=(input_iterator_adaptor const& x) const {
|
||||
return *base_ != *x.base_;
|
||||
}
|
||||
private:
|
||||
Iterator* base_;
|
||||
};
|
||||
|
||||
template <class Iterator>
|
||||
input_iterator_adaptor<Iterator> input_iterator(Iterator& it)
|
||||
{
|
||||
return *base_ != *x.base_;
|
||||
return input_iterator_adaptor<Iterator>(it);
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator* base_;
|
||||
};
|
||||
template <class Iterator>
|
||||
struct copy_iterator_adaptor
|
||||
: public boost::iterator<
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_category<Iterator>::type,
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_value<Iterator>::type,
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_difference<Iterator>::type,
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_pointer<Iterator>::type,
|
||||
proxy<Iterator>
|
||||
>
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::iterator_value<Iterator>::type
|
||||
value_type;
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::iterator_difference<Iterator>::type
|
||||
difference_type;
|
||||
|
||||
copy_iterator_adaptor()
|
||||
: base_() {}
|
||||
explicit copy_iterator_adaptor(Iterator const& it)
|
||||
: base_(it) {}
|
||||
value_type operator*() const {
|
||||
return *base_;
|
||||
}
|
||||
value_type* operator->() const {
|
||||
return &*base_;
|
||||
}
|
||||
value_type operator[](difference_type d) {
|
||||
return base_[d];
|
||||
}
|
||||
copy_iterator_adaptor& operator++() {
|
||||
++base_; return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator++(int) {
|
||||
copy_iterator_adaptor tmp(*this); ++base_; return tmp;
|
||||
}
|
||||
copy_iterator_adaptor& operator--() {
|
||||
--base_; return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator--(int) {
|
||||
copy_iterator_adaptor tmp(*this); --base_; return tmp;
|
||||
}
|
||||
copy_iterator_adaptor operator+=(difference_type x) {
|
||||
base_ += x;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator-=(difference_type x) {
|
||||
base_ -= x;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator+(difference_type n) {
|
||||
return copy_iterator_adaptor(base_+n);
|
||||
}
|
||||
copy_iterator_adaptor operator-(difference_type n) {
|
||||
return copy_iterator_adaptor(base_-n);
|
||||
}
|
||||
friend copy_iterator_adaptor operator+(
|
||||
difference_type n, copy_iterator_adaptor x) {
|
||||
return x+n;
|
||||
}
|
||||
difference_type operator-(copy_iterator_adaptor const& other) {
|
||||
return base_-other.base_;
|
||||
}
|
||||
bool operator==(copy_iterator_adaptor const& x) const {
|
||||
return base_ == x.base_;
|
||||
}
|
||||
bool operator!=(copy_iterator_adaptor const& x) const {
|
||||
return base_ != x.base_;
|
||||
}
|
||||
bool operator<(copy_iterator_adaptor const& x) const {
|
||||
return base_ < x.base_;
|
||||
}
|
||||
bool operator>(copy_iterator_adaptor const& x) const {
|
||||
return base_ > x.base_;
|
||||
}
|
||||
bool operator<=(copy_iterator_adaptor const& x) const {
|
||||
return base_ <= x.base_;
|
||||
}
|
||||
bool operator>=(copy_iterator_adaptor const& x) const {
|
||||
return base_ >= x.base_;
|
||||
}
|
||||
private:
|
||||
Iterator base_;
|
||||
};
|
||||
|
||||
template <class Iterator>
|
||||
input_iterator_adaptor<Iterator> input_iterator(Iterator& it)
|
||||
{
|
||||
return input_iterator_adaptor<Iterator>(it);
|
||||
}
|
||||
|
||||
template <class Iterator> struct copy_iterator_adaptor
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type value_type;
|
||||
typedef
|
||||
typename std::iterator_traits<Iterator>::difference_type difference_type;
|
||||
typedef typename std::iterator_traits<Iterator>::iterator_category
|
||||
iterator_category;
|
||||
typedef typename std::iterator_traits<Iterator>::pointer pointer;
|
||||
typedef proxy<Iterator> reference;
|
||||
|
||||
copy_iterator_adaptor() : base_() {}
|
||||
explicit copy_iterator_adaptor(Iterator const& it) : base_(it) {}
|
||||
value_type operator*() const { return *base_; }
|
||||
value_type* operator->() const { return &*base_; }
|
||||
value_type operator[](difference_type d) { return base_[d]; }
|
||||
copy_iterator_adaptor& operator++()
|
||||
template <class Iterator>
|
||||
copy_iterator_adaptor<Iterator> copy_iterator(Iterator const& it)
|
||||
{
|
||||
++base_;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator++(int)
|
||||
{
|
||||
copy_iterator_adaptor tmp(*this);
|
||||
++base_;
|
||||
return tmp;
|
||||
}
|
||||
copy_iterator_adaptor& operator--()
|
||||
{
|
||||
--base_;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator--(int)
|
||||
{
|
||||
copy_iterator_adaptor tmp(*this);
|
||||
--base_;
|
||||
return tmp;
|
||||
}
|
||||
copy_iterator_adaptor operator+=(difference_type x)
|
||||
{
|
||||
base_ += x;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator-=(difference_type x)
|
||||
{
|
||||
base_ -= x;
|
||||
return *this;
|
||||
}
|
||||
copy_iterator_adaptor operator+(difference_type n)
|
||||
{
|
||||
return copy_iterator_adaptor(base_ + n);
|
||||
}
|
||||
copy_iterator_adaptor operator-(difference_type n)
|
||||
{
|
||||
return copy_iterator_adaptor(base_ - n);
|
||||
}
|
||||
friend copy_iterator_adaptor operator+(
|
||||
difference_type n, copy_iterator_adaptor x)
|
||||
{
|
||||
return x + n;
|
||||
}
|
||||
difference_type operator-(copy_iterator_adaptor const& other)
|
||||
{
|
||||
return base_ - other.base_;
|
||||
}
|
||||
bool operator==(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ == x.base_;
|
||||
}
|
||||
bool operator!=(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ != x.base_;
|
||||
}
|
||||
bool operator<(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ < x.base_;
|
||||
}
|
||||
bool operator>(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ > x.base_;
|
||||
}
|
||||
bool operator<=(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ <= x.base_;
|
||||
}
|
||||
bool operator>=(copy_iterator_adaptor const& x) const
|
||||
{
|
||||
return base_ >= x.base_;
|
||||
return copy_iterator_adaptor<Iterator>(it);
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator base_;
|
||||
};
|
||||
|
||||
template <class Iterator>
|
||||
copy_iterator_adaptor<Iterator> copy_iterator(Iterator const& it)
|
||||
{
|
||||
return copy_iterator_adaptor<Iterator>(it);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+91
-95
@@ -9,118 +9,113 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER
|
||||
|
||||
#include "./helpers.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
#include <cmath>
|
||||
#include "./metafunctions.hpp"
|
||||
#include "./helpers.hpp"
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#pragma warning(disable : 4267) // conversion from 'size_t' to 'unsigned int',
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#pragma warning(disable:4267) // conversion from 'size_t' to 'unsigned int',
|
||||
// possible loss of data
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
template <class X> void check_equivalent_keys(X const& x1)
|
||||
{
|
||||
typename X::key_equal eq = x1.key_eq();
|
||||
typedef typename X::key_type key_type;
|
||||
std::set<key_type, std::less<key_type> > found_;
|
||||
namespace test
|
||||
{
|
||||
template <class X>
|
||||
void check_equivalent_keys(X const& x1)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME X::key_equal eq = x1.key_eq();
|
||||
typedef BOOST_DEDUCED_TYPENAME X::key_type key_type;
|
||||
std::set<key_type, std::less<key_type> > found_;
|
||||
|
||||
typename X::const_iterator it = x1.begin(), end = x1.end();
|
||||
typename X::size_type size = 0;
|
||||
while (it != end) {
|
||||
// First test that the current key has not occurred before, required
|
||||
// to test either that keys are unique or that equivalent keys are
|
||||
// adjacent. (6.3.1/6)
|
||||
key_type key = get_key<X>(*it);
|
||||
if (!found_.insert(key).second)
|
||||
BOOST_ERROR("Elements with equivalent keys aren't adjacent.");
|
||||
BOOST_DEDUCED_TYPENAME X::const_iterator
|
||||
it = x1.begin(), end = x1.end();
|
||||
BOOST_DEDUCED_TYPENAME X::size_type size = 0;
|
||||
while(it != end) {
|
||||
// First test that the current key has not occurred before, required
|
||||
// to test either that keys are unique or that equivalent keys are
|
||||
// adjacent. (6.3.1/6)
|
||||
key_type key = get_key<X>(*it);
|
||||
if(!found_.insert(key).second)
|
||||
BOOST_ERROR("Elements with equivalent keys aren't adjacent.");
|
||||
|
||||
// Iterate over equivalent keys, counting them.
|
||||
unsigned int count = 0;
|
||||
do {
|
||||
++it;
|
||||
++count;
|
||||
++size;
|
||||
} while (it != end && eq(get_key<X>(*it), key));
|
||||
// Iterate over equivalent keys, counting them.
|
||||
unsigned int count = 0;
|
||||
do {
|
||||
++it;
|
||||
++count;
|
||||
++size;
|
||||
} while(it != end && eq(get_key<X>(*it), key));
|
||||
|
||||
// If the container has unique keys, test that there's only one.
|
||||
// Since the previous test makes sure that all equivalent keys are
|
||||
// adjacent, this is all the equivalent keys - so the test is
|
||||
// sufficient. (6.3.1/6 again).
|
||||
if (test::has_unique_keys<X>::value && count != 1)
|
||||
BOOST_ERROR("Non-unique key.");
|
||||
// If the container has unique keys, test that there's only one.
|
||||
// Since the previous test makes sure that all equivalent keys are
|
||||
// adjacent, this is all the equivalent keys - so the test is
|
||||
// sufficient. (6.3.1/6 again).
|
||||
if(test::has_unique_keys<X>::value && count != 1)
|
||||
BOOST_ERROR("Non-unique key.");
|
||||
|
||||
if (x1.count(key) != count) {
|
||||
BOOST_ERROR("Incorrect output of count.");
|
||||
std::cerr << x1.count(key) << "," << count << "\n";
|
||||
}
|
||||
if(x1.count(key) != count) {
|
||||
BOOST_ERROR("Incorrect output of count.");
|
||||
std::cerr<<x1.count(key)<<","<<count<<"\n";
|
||||
}
|
||||
|
||||
// Check that the keys are in the correct bucket and are
|
||||
// adjacent in the bucket.
|
||||
typename X::size_type bucket = x1.bucket(key);
|
||||
typename X::const_local_iterator lit = x1.begin(bucket),
|
||||
lend = x1.end(bucket);
|
||||
|
||||
unsigned int count_checked = 0;
|
||||
for (; lit != lend && !eq(get_key<X>(*lit), key); ++lit) {
|
||||
++count_checked;
|
||||
}
|
||||
|
||||
if (lit == lend) {
|
||||
BOOST_ERROR("Unable to find element with a local_iterator");
|
||||
std::cerr << "Checked: " << count_checked << " elements" << std::endl;
|
||||
} else {
|
||||
unsigned int count2 = 0;
|
||||
for (; lit != lend && eq(get_key<X>(*lit), key); ++lit)
|
||||
++count2;
|
||||
if (count != count2)
|
||||
BOOST_ERROR("Element count doesn't match local_iterator.");
|
||||
for (; lit != lend; ++lit) {
|
||||
if (eq(get_key<X>(*lit), key)) {
|
||||
BOOST_ERROR("Non-adjacent element with equivalent key "
|
||||
// Check that the keys are in the correct bucket and are
|
||||
// adjacent in the bucket.
|
||||
BOOST_DEDUCED_TYPENAME X::size_type bucket = x1.bucket(key);
|
||||
BOOST_DEDUCED_TYPENAME X::const_local_iterator
|
||||
lit = x1.begin(bucket), lend = x1.end(bucket);
|
||||
for(; lit != lend && !eq(get_key<X>(*lit), key); ++lit) continue;
|
||||
if(lit == lend)
|
||||
BOOST_ERROR("Unable to find element with a local_iterator");
|
||||
unsigned int count2 = 0;
|
||||
for(; lit != lend && eq(get_key<X>(*lit), key); ++lit) ++count2;
|
||||
if(count != count2)
|
||||
BOOST_ERROR("Element count doesn't match local_iterator.");
|
||||
for(; lit != lend; ++lit) {
|
||||
if(eq(get_key<X>(*lit), key)) {
|
||||
BOOST_ERROR("Non-adjacent element with equivalent key "
|
||||
"in bucket.");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check that size matches up.
|
||||
|
||||
if(x1.size() != size) {
|
||||
BOOST_ERROR("x1.size() doesn't match actual size.");
|
||||
std::cout<<x1.size()<<"/"<<size<<std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check that size matches up.
|
||||
// Check the load factor.
|
||||
|
||||
if (x1.size() != size) {
|
||||
BOOST_ERROR("x1.size() doesn't match actual size.");
|
||||
std::cout << x1.size() << "/" << size << std::endl;
|
||||
float load_factor =
|
||||
static_cast<float>(size) / static_cast<float>(x1.bucket_count());
|
||||
using namespace std;
|
||||
if(fabs(x1.load_factor() - load_factor) > x1.load_factor() / 64)
|
||||
BOOST_ERROR("x1.load_factor() doesn't match actual load_factor.");
|
||||
|
||||
// Check that size in the buckets matches up.
|
||||
|
||||
BOOST_DEDUCED_TYPENAME X::size_type bucket_size = 0;
|
||||
|
||||
for (BOOST_DEDUCED_TYPENAME X::size_type
|
||||
i = 0; i < x1.bucket_count(); ++i)
|
||||
{
|
||||
for (BOOST_DEDUCED_TYPENAME X::const_local_iterator
|
||||
begin = x1.begin(i), end = x1.end(i); begin != end; ++begin)
|
||||
{
|
||||
++bucket_size;
|
||||
}
|
||||
}
|
||||
|
||||
if(x1.size() != bucket_size) {
|
||||
BOOST_ERROR("x1.size() doesn't match bucket size.");
|
||||
std::cout<<x1.size()<<"/"<<bucket_size<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the load factor.
|
||||
|
||||
float load_factor = size == 0 ? 0
|
||||
: static_cast<float>(size) /
|
||||
static_cast<float>(x1.bucket_count());
|
||||
using namespace std;
|
||||
if (fabs(x1.load_factor() - load_factor) > x1.load_factor() / 64)
|
||||
BOOST_ERROR("x1.load_factor() doesn't match actual load_factor.");
|
||||
|
||||
// Check that size in the buckets matches up.
|
||||
|
||||
typename X::size_type bucket_size = 0;
|
||||
|
||||
for (typename X::size_type i = 0; i < x1.bucket_count(); ++i) {
|
||||
for (typename X::const_local_iterator begin2 = x1.begin(i),
|
||||
end2 = x1.end(i);
|
||||
begin2 != end2; ++begin2) {
|
||||
++bucket_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (x1.size() != bucket_size) {
|
||||
BOOST_ERROR("x1.size() doesn't match bucket size.");
|
||||
std::cout << x1.size() << "/" << bucket_size << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
@@ -128,3 +123,4 @@ namespace test {
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+285
-298
@@ -11,321 +11,308 @@
|
||||
#if !defined(UNORDERED_TEST_LIST_HEADER)
|
||||
#define UNORDERED_TEST_LIST_HEADER
|
||||
|
||||
#include <boost/iterator.hpp>
|
||||
#include <boost/limits.hpp>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
|
||||
namespace test {
|
||||
template <typename It1, typename It2>
|
||||
bool equal(It1 begin, It1 end, It2 compare)
|
||||
{
|
||||
for (; begin != end; ++begin, ++compare)
|
||||
if (*begin != *compare)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename It1, typename It2, typename Pred>
|
||||
bool equal(It1 begin, It1 end, It2 compare, Pred predicate)
|
||||
{
|
||||
for (; begin != end; ++begin, ++compare)
|
||||
if (!predicate(*begin, *compare))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> class list;
|
||||
|
||||
namespace test_detail {
|
||||
template <typename T> class list_node;
|
||||
template <typename T> class list_data;
|
||||
template <typename T> class list_iterator;
|
||||
template <typename T> class list_const_iterator;
|
||||
|
||||
template <typename T> class list_node
|
||||
namespace test
|
||||
{
|
||||
template <typename It1, typename It2>
|
||||
bool equal(It1 begin, It1 end, It2 compare)
|
||||
{
|
||||
list_node(list_node const&);
|
||||
list_node& operator=(list_node const&);
|
||||
for(;begin != end; ++begin, ++compare)
|
||||
if(*begin != *compare) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
T value_;
|
||||
list_node* next_;
|
||||
|
||||
list_node(T const& v) : value_(v), next_(0) {}
|
||||
list_node(T const& v, list_node* n) : value_(v), next_(n) {}
|
||||
};
|
||||
|
||||
template <typename T> class list_data
|
||||
template <typename It1, typename It2, typename Pred>
|
||||
bool equal(It1 begin, It1 end, It2 compare, Pred predicate)
|
||||
{
|
||||
for(;begin != end; ++begin, ++compare)
|
||||
if(!predicate(*begin, *compare)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename T> class list;
|
||||
|
||||
namespace test_detail
|
||||
{
|
||||
template <typename T> class list_node;
|
||||
template <typename T> class list_data;
|
||||
template <typename T> class list_iterator;
|
||||
template <typename T> class list_const_iterator;
|
||||
|
||||
template <typename T>
|
||||
class list_node
|
||||
{
|
||||
list_node(list_node const&);
|
||||
list_node& operator=(list_node const&);
|
||||
public:
|
||||
T value_;
|
||||
list_node* next_;
|
||||
|
||||
list_node(T const& v) : value_(v), next_(0) {}
|
||||
list_node(T const& v, list_node* n) : value_(v), next_(n) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class list_data
|
||||
{
|
||||
public:
|
||||
typedef list_node<T> node;
|
||||
typedef unsigned int size_type;
|
||||
|
||||
node* first_;
|
||||
node** last_ptr_;
|
||||
size_type size_;
|
||||
|
||||
list_data() : first_(0), last_ptr_(&first_), size_(0) {}
|
||||
|
||||
~list_data() {
|
||||
while(first_) {
|
||||
node* tmp = first_;
|
||||
first_ = first_->next_;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
private:
|
||||
list_data(list_data const&);
|
||||
list_data& operator=(list_data const&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class list_iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, T,
|
||||
int, T*, T&>
|
||||
{
|
||||
friend class list_const_iterator<T>;
|
||||
friend class test::list<T>;
|
||||
typedef list_node<T> node;
|
||||
typedef list_const_iterator<T> const_iterator;
|
||||
|
||||
node* ptr_;
|
||||
public:
|
||||
list_iterator() : ptr_(0) {}
|
||||
explicit list_iterator(node* x) : ptr_(x) {}
|
||||
|
||||
T& operator*() const { return ptr_->value_; }
|
||||
T* operator->() const { return &ptr_->value_; }
|
||||
list_iterator& operator++() {
|
||||
ptr_ = ptr_->next_; return *this; }
|
||||
list_iterator operator++(int) {
|
||||
list_iterator tmp = *this; ptr_ = ptr_->next_; return tmp; }
|
||||
bool operator==(const_iterator y) const { return ptr_ == y.ptr_; }
|
||||
bool operator!=(const_iterator y) const { return ptr_ != y.ptr_; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class list_const_iterator
|
||||
: public boost::iterator<
|
||||
std::forward_iterator_tag, T,
|
||||
int, T const*, T const&>
|
||||
{
|
||||
friend class list_iterator<T>;
|
||||
friend class test::list<T>;
|
||||
typedef list_node<T> node;
|
||||
typedef list_iterator<T> iterator;
|
||||
typedef list_const_iterator<T> const_iterator;
|
||||
|
||||
node* ptr_;
|
||||
public:
|
||||
list_const_iterator() : ptr_(0) {}
|
||||
list_const_iterator(list_iterator<T> const& x) : ptr_(x.ptr_) {}
|
||||
|
||||
T const& operator*() const { return ptr_->value_; }
|
||||
T const* operator->() const { return &ptr_->value_; }
|
||||
|
||||
list_const_iterator& operator++()
|
||||
{
|
||||
ptr_ = ptr_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
list_const_iterator operator++(int)
|
||||
{
|
||||
list_const_iterator tmp = *this;
|
||||
ptr_ = ptr_->next_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const_iterator y) const
|
||||
{
|
||||
return ptr_ == y.ptr_;
|
||||
}
|
||||
|
||||
bool operator!=(const_iterator y) const
|
||||
{
|
||||
return ptr_ != y.ptr_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class list
|
||||
{
|
||||
typedef test::test_detail::list_data<T> data;
|
||||
typedef test::test_detail::list_node<T> node;
|
||||
data data_;
|
||||
public:
|
||||
typedef list_node<T> node;
|
||||
typedef unsigned int size_type;
|
||||
typedef T value_type;
|
||||
typedef value_type& reference;
|
||||
typedef value_type const& const_reference;
|
||||
typedef unsigned int size_type;
|
||||
|
||||
node* first_;
|
||||
node** last_ptr_;
|
||||
size_type size_;
|
||||
typedef test::test_detail::list_iterator<T> iterator;
|
||||
typedef test::test_detail::list_const_iterator<T> const_iterator;
|
||||
|
||||
list_data() : first_(0), last_ptr_(&first_), size_(0) {}
|
||||
list() : data_() {}
|
||||
|
||||
~list_data()
|
||||
{
|
||||
while (first_) {
|
||||
node* tmp = first_;
|
||||
first_ = first_->next_;
|
||||
delete tmp;
|
||||
list(list const& other) : data_() {
|
||||
insert(other.begin(), other.end());
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
list(InputIterator i, InputIterator j) : data_() {
|
||||
insert(i, j);
|
||||
}
|
||||
|
||||
list& operator=(list const& other) {
|
||||
clear();
|
||||
insert(other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(data_.first_); }
|
||||
iterator end() { return iterator(); }
|
||||
const_iterator begin() const { return iterator(data_.first_); }
|
||||
const_iterator end() const { return iterator(); }
|
||||
const_iterator cbegin() const { return iterator(data_.first_); }
|
||||
const_iterator cend() const { return iterator(); }
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator i, InputIterator j) {
|
||||
for(; i != j; ++i)
|
||||
push_back(*i);
|
||||
}
|
||||
|
||||
void push_front(value_type const& v) {
|
||||
data_.first_ = new node(v, data_.first_);
|
||||
if(!data_.size_) data_.last_ptr_ = &(*data_.last_ptr_)->next_;
|
||||
++data_.size_;
|
||||
}
|
||||
|
||||
void push_back(value_type const& v) {
|
||||
*data_.last_ptr_ = new node(v);
|
||||
data_.last_ptr_ = &(*data_.last_ptr_)->next_;
|
||||
++data_.size_;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
while(data_.first_) {
|
||||
node* tmp = data_.first_;
|
||||
data_.first_ = data_.first_->next_;
|
||||
--data_.size_;
|
||||
delete tmp;
|
||||
}
|
||||
data_.last_ptr_ = &data_.first_;
|
||||
}
|
||||
|
||||
void erase(const_iterator i, const_iterator j) {
|
||||
node** ptr = &data_.first_;
|
||||
|
||||
while(*ptr != i.ptr_) {
|
||||
ptr = &(*ptr)->next_;
|
||||
}
|
||||
|
||||
while(*ptr != j.ptr_) {
|
||||
node* to_delete = *ptr;
|
||||
*ptr = (*ptr)->next_;
|
||||
--data_.size_;
|
||||
delete to_delete;
|
||||
}
|
||||
|
||||
if(!*ptr) data_.last_ptr_ = ptr;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return !data_.size_;
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return data_.size_;
|
||||
}
|
||||
|
||||
void sort() {
|
||||
sort(std::less<T>());
|
||||
}
|
||||
|
||||
template <typename Less>
|
||||
void sort(Less less = Less()) {
|
||||
if(!empty()) merge_sort(&data_.first_,
|
||||
(std::numeric_limits<size_type>::max)(), less);
|
||||
}
|
||||
|
||||
bool operator==(list const& y) const {
|
||||
return size() == y.size() &&
|
||||
test::equal(begin(), end(), y.begin());
|
||||
}
|
||||
|
||||
bool operator!=(list const& y) const {
|
||||
return !(*this == y);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
list_data(list_data const&);
|
||||
list_data& operator=(list_data const&);
|
||||
};
|
||||
|
||||
template <typename T> class list_iterator
|
||||
{
|
||||
friend class list_const_iterator<T>;
|
||||
friend class test::list<T>;
|
||||
typedef list_node<T> node;
|
||||
typedef list_const_iterator<T> const_iterator;
|
||||
|
||||
node* ptr_;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
typedef int difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
list_iterator() : ptr_(0) {}
|
||||
explicit list_iterator(node* x) : ptr_(x) {}
|
||||
|
||||
T& operator*() const { return ptr_->value_; }
|
||||
T* operator->() const { return &ptr_->value_; }
|
||||
list_iterator& operator++()
|
||||
{
|
||||
ptr_ = ptr_->next_;
|
||||
return *this;
|
||||
}
|
||||
list_iterator operator++(int)
|
||||
{
|
||||
list_iterator tmp = *this;
|
||||
ptr_ = ptr_->next_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(list_iterator y) const { return ptr_ == y.ptr_; }
|
||||
bool operator!=(list_iterator y) const { return ptr_ != y.ptr_; }
|
||||
|
||||
bool operator==(const_iterator y) const { return ptr_ == y.ptr_; }
|
||||
bool operator!=(const_iterator y) const { return ptr_ != y.ptr_; }
|
||||
};
|
||||
|
||||
template <typename T> class list_const_iterator
|
||||
{
|
||||
friend class list_iterator<T>;
|
||||
friend class test::list<T>;
|
||||
typedef list_node<T> node;
|
||||
typedef list_iterator<T> iterator;
|
||||
typedef list_const_iterator<T> const_iterator;
|
||||
|
||||
node* ptr_;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T const* pointer;
|
||||
typedef T const& reference;
|
||||
typedef int difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
list_const_iterator() : ptr_(0) {}
|
||||
list_const_iterator(list_iterator<T> const& x) : ptr_(x.ptr_) {}
|
||||
|
||||
T const& operator*() const { return ptr_->value_; }
|
||||
T const* operator->() const { return &ptr_->value_; }
|
||||
|
||||
list_const_iterator& operator++()
|
||||
{
|
||||
ptr_ = ptr_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
list_const_iterator operator++(int)
|
||||
{
|
||||
list_const_iterator tmp = *this;
|
||||
ptr_ = ptr_->next_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const_iterator y) const { return ptr_ == y.ptr_; }
|
||||
|
||||
bool operator!=(const_iterator y) const { return ptr_ != y.ptr_; }
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T> class list
|
||||
{
|
||||
typedef test::test_detail::list_data<T> data;
|
||||
typedef test::test_detail::list_node<T> node;
|
||||
data data_;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef value_type& reference;
|
||||
typedef value_type const& const_reference;
|
||||
typedef unsigned int size_type;
|
||||
|
||||
typedef test::test_detail::list_iterator<T> iterator;
|
||||
typedef test::test_detail::list_const_iterator<T> const_iterator;
|
||||
|
||||
list() : data_() {}
|
||||
|
||||
list(list const& other) : data_() { insert(other.begin(), other.end()); }
|
||||
|
||||
template <class InputIterator>
|
||||
list(InputIterator i, InputIterator j) : data_()
|
||||
{
|
||||
insert(i, j);
|
||||
}
|
||||
|
||||
list& operator=(list const& other)
|
||||
{
|
||||
clear();
|
||||
insert(other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(data_.first_); }
|
||||
iterator end() { return iterator(); }
|
||||
const_iterator begin() const { return iterator(data_.first_); }
|
||||
const_iterator end() const { return iterator(); }
|
||||
const_iterator cbegin() const { return iterator(data_.first_); }
|
||||
const_iterator cend() const { return iterator(); }
|
||||
|
||||
template <class InputIterator> void insert(InputIterator i, InputIterator j)
|
||||
{
|
||||
for (; i != j; ++i)
|
||||
push_back(*i);
|
||||
}
|
||||
|
||||
void push_front(value_type const& v)
|
||||
{
|
||||
data_.first_ = new node(v, data_.first_);
|
||||
if (!data_.size_)
|
||||
data_.last_ptr_ = &(*data_.last_ptr_)->next_;
|
||||
++data_.size_;
|
||||
}
|
||||
|
||||
void push_back(value_type const& v)
|
||||
{
|
||||
*data_.last_ptr_ = new node(v);
|
||||
data_.last_ptr_ = &(*data_.last_ptr_)->next_;
|
||||
++data_.size_;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
while (data_.first_) {
|
||||
node* tmp = data_.first_;
|
||||
data_.first_ = data_.first_->next_;
|
||||
--data_.size_;
|
||||
delete tmp;
|
||||
}
|
||||
data_.last_ptr_ = &data_.first_;
|
||||
}
|
||||
|
||||
void erase(const_iterator i, const_iterator j)
|
||||
{
|
||||
node** ptr = &data_.first_;
|
||||
|
||||
while (*ptr != i.ptr_) {
|
||||
ptr = &(*ptr)->next_;
|
||||
}
|
||||
|
||||
while (*ptr != j.ptr_) {
|
||||
node* to_delete = *ptr;
|
||||
*ptr = (*ptr)->next_;
|
||||
--data_.size_;
|
||||
delete to_delete;
|
||||
}
|
||||
|
||||
if (!*ptr)
|
||||
data_.last_ptr_ = ptr;
|
||||
}
|
||||
|
||||
bool empty() const { return !data_.size_; }
|
||||
|
||||
size_type size() const { return data_.size_; }
|
||||
|
||||
void sort() { sort(std::less<T>()); }
|
||||
|
||||
template <typename Less> void sort(Less less = Less())
|
||||
{
|
||||
if (!empty())
|
||||
merge_sort(
|
||||
&data_.first_, (std::numeric_limits<size_type>::max)(), less);
|
||||
}
|
||||
|
||||
bool operator==(list const& y) const
|
||||
{
|
||||
return size() == y.size() && test::equal(begin(), end(), y.begin());
|
||||
}
|
||||
|
||||
bool operator!=(list const& y) const { return !(*this == y); }
|
||||
|
||||
private:
|
||||
template <typename Less>
|
||||
node** merge_sort(node** l, size_type recurse_limit, Less less)
|
||||
{
|
||||
node** ptr = &(*l)->next_;
|
||||
for (size_type count = 0; count < recurse_limit && *ptr; ++count) {
|
||||
ptr = merge_adjacent_ranges(l, ptr, merge_sort(ptr, count, less), less);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename Less>
|
||||
node** merge_adjacent_ranges(
|
||||
node** first, node** second, node** third, Less less)
|
||||
{
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
if (first == second)
|
||||
return third;
|
||||
if (less((*second)->value_, (*first)->value_))
|
||||
break;
|
||||
first = &(*first)->next_;
|
||||
template <typename Less>
|
||||
node** merge_sort(node** l, size_type recurse_limit, Less less)
|
||||
{
|
||||
node** ptr = &(*l)->next_;
|
||||
for(size_type count = 0; count < recurse_limit && *ptr; ++count)
|
||||
{
|
||||
ptr = merge_adjacent_ranges(l, ptr,
|
||||
merge_sort(ptr, count, less), less);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename Less>
|
||||
node** merge_adjacent_ranges(node** first, node** second,
|
||||
node** third, Less less)
|
||||
{
|
||||
for(;;) {
|
||||
for(;;) {
|
||||
if(first == second) return third;
|
||||
if(less((*second)->value_, (*first)->value_)) break;
|
||||
first = &(*first)->next_;
|
||||
}
|
||||
|
||||
swap_adjacent_ranges(first, second, third);
|
||||
first = &(*first)->next_;
|
||||
swap_adjacent_ranges(first, second, third);
|
||||
first = &(*first)->next_;
|
||||
|
||||
// Since the two ranges we just swapped, the order is now:
|
||||
// first...third...second
|
||||
|
||||
for(;;) {
|
||||
if(first == third) return second;
|
||||
if(!less((*first)->value_, (*third)->value_)) break;
|
||||
first = &(*first)->next_;
|
||||
}
|
||||
|
||||
// Since the two ranges we just swapped, the order is now:
|
||||
// first...third...second
|
||||
|
||||
for (;;) {
|
||||
if (first == third)
|
||||
return second;
|
||||
if (!less((*first)->value_, (*third)->value_))
|
||||
break;
|
||||
first = &(*first)->next_;
|
||||
swap_adjacent_ranges(first, third, second);
|
||||
first = &(*first)->next_;
|
||||
}
|
||||
}
|
||||
|
||||
swap_adjacent_ranges(first, third, second);
|
||||
first = &(*first)->next_;
|
||||
}
|
||||
}
|
||||
|
||||
void swap_adjacent_ranges(node** first, node** second, node** third)
|
||||
{
|
||||
node* tmp = *first;
|
||||
*first = *second;
|
||||
*second = *third;
|
||||
*third = tmp;
|
||||
if (!*second)
|
||||
data_.last_ptr_ = second;
|
||||
}
|
||||
};
|
||||
|
||||
void swap_adjacent_ranges(node** first, node** second, node** third)
|
||||
{
|
||||
node* tmp = *first;
|
||||
*first = *second;
|
||||
*second = *third;
|
||||
*third = tmp;
|
||||
if(!*second) data_.last_ptr_ = second;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+130
-156
@@ -6,184 +6,158 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_MEMORY_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_MEMORY_HEADER
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered/detail/allocate.hpp>
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
namespace test {
|
||||
namespace detail {
|
||||
struct memory_area
|
||||
namespace test
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
void const* start;
|
||||
void const* end;
|
||||
struct memory_area {
|
||||
void const* start;
|
||||
void const* end;
|
||||
|
||||
memory_area(void const* s, void const* e) : start(s), end(e)
|
||||
{
|
||||
BOOST_ASSERT(start != end);
|
||||
}
|
||||
};
|
||||
memory_area(void const* s, void const* e)
|
||||
: start(s), end(e)
|
||||
{
|
||||
BOOST_ASSERT(start != end);
|
||||
}
|
||||
};
|
||||
|
||||
struct memory_track
|
||||
{
|
||||
explicit memory_track(int tag = -1) : constructed_(0), tag_(tag) {}
|
||||
struct memory_track {
|
||||
explicit memory_track(int tag = -1) :
|
||||
constructed_(0),
|
||||
tag_(tag) {}
|
||||
|
||||
int constructed_;
|
||||
int tag_;
|
||||
};
|
||||
int constructed_;
|
||||
int tag_;
|
||||
};
|
||||
|
||||
// This is a bit dodgy as it defines overlapping
|
||||
// areas as 'equal', so this isn't a total ordering.
|
||||
// But it is for non-overlapping memory regions - which
|
||||
// is what'll be stored.
|
||||
//
|
||||
// All searches will be for areas entirely contained by
|
||||
// a member of the set - so it should find the area that contains
|
||||
// the region that is searched for.
|
||||
// This is a bit dodgy as it defines overlapping
|
||||
// areas as 'equal', so this isn't a total ordering.
|
||||
// But it is for non-overlapping memory regions - which
|
||||
// is what'll be stored.
|
||||
//
|
||||
// All searches will be for areas entirely contained by
|
||||
// a member of the set - so it should find the area that contains
|
||||
// the region that is searched for.
|
||||
|
||||
struct memory_area_compare
|
||||
{
|
||||
bool operator()(memory_area const& x, memory_area const& y) const
|
||||
{
|
||||
return x.end <= y.start;
|
||||
}
|
||||
};
|
||||
struct memory_area_compare {
|
||||
bool operator()(memory_area const& x, memory_area const& y) const {
|
||||
return x.end <= y.start;
|
||||
}
|
||||
};
|
||||
|
||||
struct memory_tracker
|
||||
{
|
||||
typedef std::map<memory_area, memory_track, memory_area_compare,
|
||||
std::allocator<std::pair<memory_area const, memory_track> > >
|
||||
allocated_memory_type;
|
||||
struct memory_tracker {
|
||||
typedef std::map<memory_area, memory_track, memory_area_compare,
|
||||
std::allocator<std::pair<memory_area const, memory_track> >
|
||||
> allocated_memory_type;
|
||||
|
||||
allocated_memory_type allocated_memory;
|
||||
unsigned int count_allocators;
|
||||
unsigned int count_allocations;
|
||||
unsigned int count_constructions;
|
||||
bool tracking_constructions;
|
||||
allocated_memory_type allocated_memory;
|
||||
unsigned int count_allocators;
|
||||
unsigned int count_allocations;
|
||||
unsigned int count_constructions;
|
||||
|
||||
memory_tracker()
|
||||
: count_allocators(0), count_allocations(0), count_constructions(0),
|
||||
tracking_constructions(true)
|
||||
{
|
||||
}
|
||||
memory_tracker() :
|
||||
count_allocators(0), count_allocations(0),
|
||||
count_constructions(0)
|
||||
{}
|
||||
|
||||
~memory_tracker() { BOOST_ASSERT(count_allocators == 0); }
|
||||
void allocator_ref()
|
||||
{
|
||||
if(count_allocators == 0) {
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
}
|
||||
++count_allocators;
|
||||
}
|
||||
|
||||
void allocator_ref()
|
||||
{
|
||||
if (count_allocators == 0) {
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
}
|
||||
++count_allocators;
|
||||
}
|
||||
void allocator_unref()
|
||||
{
|
||||
BOOST_TEST(count_allocators > 0);
|
||||
if(count_allocators > 0) {
|
||||
--count_allocators;
|
||||
if(count_allocators == 0) {
|
||||
bool no_allocations_left = (count_allocations == 0);
|
||||
bool no_constructions_left = (count_constructions == 0);
|
||||
bool allocated_memory_empty = allocated_memory.empty();
|
||||
|
||||
void allocator_unref()
|
||||
{
|
||||
BOOST_TEST(count_allocators > 0);
|
||||
if (count_allocators > 0) {
|
||||
--count_allocators;
|
||||
if (count_allocators == 0) {
|
||||
bool no_allocations_left = (count_allocations == 0);
|
||||
bool no_constructions_left = (count_constructions == 0);
|
||||
bool allocated_memory_empty = allocated_memory.empty();
|
||||
// Clearing the data before the checks terminate the
|
||||
// tests.
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
|
||||
// Clearing the data before the checks terminate the
|
||||
// tests.
|
||||
count_allocations = 0;
|
||||
count_constructions = 0;
|
||||
allocated_memory.clear();
|
||||
BOOST_TEST(no_allocations_left);
|
||||
BOOST_TEST(no_constructions_left);
|
||||
BOOST_TEST(allocated_memory_empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST(no_allocations_left);
|
||||
BOOST_TEST(no_constructions_left);
|
||||
BOOST_TEST(allocated_memory_empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
void track_allocate(void *ptr, std::size_t n, std::size_t size,
|
||||
int tag)
|
||||
{
|
||||
if(n == 0) {
|
||||
BOOST_ERROR("Allocating 0 length array.");
|
||||
}
|
||||
else {
|
||||
++count_allocations;
|
||||
allocated_memory.insert(
|
||||
std::pair<memory_area const, memory_track>(
|
||||
memory_area(ptr, (char*) ptr + n * size),
|
||||
memory_track(tag)));
|
||||
}
|
||||
}
|
||||
|
||||
void track_allocate(void* ptr, std::size_t n, std::size_t size, int tag)
|
||||
{
|
||||
if (n == 0) {
|
||||
BOOST_ERROR("Allocating 0 length array.");
|
||||
} else {
|
||||
++count_allocations;
|
||||
allocated_memory.insert(std::pair<memory_area const, memory_track>(
|
||||
memory_area(ptr, (char*)ptr + n * size), memory_track(tag)));
|
||||
}
|
||||
}
|
||||
void track_deallocate(void* ptr, std::size_t n, std::size_t size,
|
||||
int tag, bool check_tag_ = true)
|
||||
{
|
||||
allocated_memory_type::iterator pos =
|
||||
allocated_memory.find(
|
||||
memory_area(ptr, (char*) ptr + n * size));
|
||||
if(pos == allocated_memory.end()) {
|
||||
BOOST_ERROR("Deallocating unknown pointer.");
|
||||
} else {
|
||||
BOOST_TEST(pos->first.start == ptr);
|
||||
BOOST_TEST(pos->first.end == (char*) ptr + n * size);
|
||||
if (check_tag_) BOOST_TEST(pos->second.tag_ == tag);
|
||||
allocated_memory.erase(pos);
|
||||
}
|
||||
BOOST_TEST(count_allocations > 0);
|
||||
if(count_allocations > 0) --count_allocations;
|
||||
}
|
||||
|
||||
void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag,
|
||||
bool check_tag_ = true)
|
||||
{
|
||||
allocated_memory_type::iterator pos =
|
||||
allocated_memory.find(memory_area(ptr, (char*)ptr + n * size));
|
||||
if (pos == allocated_memory.end()) {
|
||||
BOOST_ERROR("Deallocating unknown pointer.");
|
||||
} else {
|
||||
BOOST_TEST(pos->first.start == ptr);
|
||||
BOOST_TEST(pos->first.end == (char*)ptr + n * size);
|
||||
if (check_tag_)
|
||||
BOOST_TEST(pos->second.tag_ == tag);
|
||||
allocated_memory.erase(pos);
|
||||
}
|
||||
BOOST_TEST(count_allocations > 0);
|
||||
if (count_allocations > 0)
|
||||
--count_allocations;
|
||||
}
|
||||
void track_construct(void* /*ptr*/, std::size_t /*size*/,
|
||||
int /*tag*/)
|
||||
{
|
||||
++count_constructions;
|
||||
}
|
||||
|
||||
void track_construct(void* /*ptr*/, std::size_t /*size*/, int /*tag*/)
|
||||
{
|
||||
if (tracking_constructions) {
|
||||
++count_constructions;
|
||||
}
|
||||
}
|
||||
|
||||
void track_destroy(void* /*ptr*/, std::size_t /*size*/, int /*tag*/)
|
||||
{
|
||||
if (tracking_constructions) {
|
||||
BOOST_TEST(count_constructions > 0);
|
||||
if (count_constructions > 0)
|
||||
--count_constructions;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually required by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
test::detail::memory_tracker tracker;
|
||||
void track_destroy(void* /*ptr*/, std::size_t /*size*/,
|
||||
int /*tag*/)
|
||||
{
|
||||
BOOST_TEST(count_constructions > 0);
|
||||
if(count_constructions > 0) --count_constructions;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct disable_construction_tracking
|
||||
namespace detail
|
||||
{
|
||||
bool old_value;
|
||||
|
||||
disable_construction_tracking()
|
||||
: old_value(detail::tracker.tracking_constructions)
|
||||
{
|
||||
test::detail::tracker.tracking_constructions = false;
|
||||
}
|
||||
|
||||
~disable_construction_tracking()
|
||||
{
|
||||
test::detail::tracker.tracking_constructions = old_value;
|
||||
}
|
||||
|
||||
private:
|
||||
disable_construction_tracking(disable_construction_tracking const&);
|
||||
disable_construction_tracking& operator=(
|
||||
disable_construction_tracking const&);
|
||||
};
|
||||
}
|
||||
// This won't be a problem as I'm only using a single compile unit
|
||||
// in each test (this is actually required by the minimal test
|
||||
// framework).
|
||||
//
|
||||
// boostinspect:nounnamed
|
||||
namespace {
|
||||
test::detail::memory_tracker tracker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,26 +8,35 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/type_traits/declval.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
namespace test {
|
||||
template <class Container>
|
||||
struct is_set : public boost::is_same<typename Container::key_type,
|
||||
typename Container::value_type>
|
||||
{
|
||||
};
|
||||
namespace test
|
||||
{
|
||||
template <class Container>
|
||||
struct is_set
|
||||
: public boost::is_same<
|
||||
BOOST_DEDUCED_TYPENAME Container::key_type,
|
||||
BOOST_DEDUCED_TYPENAME Container::value_type> {};
|
||||
|
||||
template <class Container> struct has_unique_keys
|
||||
{
|
||||
static char flip(typename Container::iterator const&);
|
||||
static long flip(std::pair<typename Container::iterator, bool> const&);
|
||||
BOOST_STATIC_CONSTANT(bool,
|
||||
value = sizeof(long) ==
|
||||
sizeof(flip(
|
||||
(boost::declval<Container*>())
|
||||
->insert(
|
||||
boost::declval<typename Container::value_type const&>()))));
|
||||
};
|
||||
template <class Container>
|
||||
struct has_unique_keys
|
||||
{
|
||||
BOOST_STATIC_CONSTANT(bool, value = false);
|
||||
};
|
||||
|
||||
template <class V, class H, class P, class A>
|
||||
struct has_unique_keys<boost::unordered_set<V, H, P, A> >
|
||||
{
|
||||
BOOST_STATIC_CONSTANT(bool, value = true);
|
||||
};
|
||||
|
||||
template <class K, class M, class H, class P, class A>
|
||||
struct has_unique_keys<boost::unordered_map<K, M, H, P, A> >
|
||||
{
|
||||
BOOST_STATIC_CONSTANT(bool, value = true);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
|
||||
// Include this after the boost headers, but before other test headers.
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
#if defined(_WIN32_WCE)
|
||||
// The standard windows mobile headers trigger this warning so I disable it
|
||||
// before doing anything else.
|
||||
#pragma warning(disable : 4201) // nonstandard extension used :
|
||||
#pragma warning(disable:4201) // nonstandard extension used :
|
||||
// nameless struct/union
|
||||
#endif
|
||||
|
||||
@@ -6,104 +6,110 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER
|
||||
|
||||
#include "./generators.hpp"
|
||||
#include "./list.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/detail/select_type.hpp>
|
||||
#include "./generators.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
|
||||
namespace test {
|
||||
template <class X> struct unordered_generator_set
|
||||
{
|
||||
typedef typename X::value_type value_type;
|
||||
namespace test
|
||||
{
|
||||
typedef enum {
|
||||
default_generator,
|
||||
generate_collisions
|
||||
} random_generator;
|
||||
|
||||
random_generator type_;
|
||||
|
||||
unordered_generator_set(random_generator type) : type_(type) {}
|
||||
|
||||
template <class T> void fill(T& x, std::size_t len)
|
||||
template <class X>
|
||||
struct unordered_generator_set
|
||||
{
|
||||
value_type* value_ptr = 0;
|
||||
len += x.size();
|
||||
typedef BOOST_DEDUCED_TYPENAME X::value_type value_type;
|
||||
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
value_type value = generate(value_ptr, type_);
|
||||
random_generator type_;
|
||||
|
||||
std::size_t count =
|
||||
type_ == generate_collisions ? random_value(5) + 1 : 1;
|
||||
unordered_generator_set(random_generator type)
|
||||
: type_(type) {}
|
||||
|
||||
for (std::size_t j = 0; j < count; ++j) {
|
||||
x.push_back(value);
|
||||
template <class T>
|
||||
void fill(T& x, std::size_t len) {
|
||||
value_type* value_ptr = 0;
|
||||
int* int_ptr = 0;
|
||||
len += x.size();
|
||||
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
value_type value = generate(value_ptr);
|
||||
|
||||
int count = type_ == generate_collisions ?
|
||||
1 + (generate(int_ptr) % 5) : 1;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
x.push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <class X> struct unordered_generator_map
|
||||
{
|
||||
typedef typename X::key_type key_type;
|
||||
typedef typename X::mapped_type mapped_type;
|
||||
|
||||
random_generator type_;
|
||||
|
||||
unordered_generator_map(random_generator type) : type_(type) {}
|
||||
|
||||
template <class T> void fill(T& x, std::size_t len)
|
||||
template <class X>
|
||||
struct unordered_generator_map
|
||||
{
|
||||
key_type* key_ptr = 0;
|
||||
mapped_type* mapped_ptr = 0;
|
||||
typedef BOOST_DEDUCED_TYPENAME X::key_type key_type;
|
||||
typedef BOOST_DEDUCED_TYPENAME X::mapped_type mapped_type;
|
||||
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
key_type key = generate(key_ptr, type_);
|
||||
random_generator type_;
|
||||
|
||||
std::size_t count =
|
||||
type_ == generate_collisions ? random_value(5) + 1 : 1;
|
||||
unordered_generator_map(random_generator type)
|
||||
: type_(type) {}
|
||||
|
||||
for (std::size_t j = 0; j < count; ++j) {
|
||||
x.push_back(std::pair<key_type const, mapped_type>(
|
||||
key, generate(mapped_ptr, type_)));
|
||||
template <class T>
|
||||
void fill(T& x, std::size_t len) {
|
||||
key_type* key_ptr = 0;
|
||||
mapped_type* mapped_ptr = 0;
|
||||
int* int_ptr = 0;
|
||||
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
key_type key = generate(key_ptr);
|
||||
|
||||
int count = type_ == generate_collisions ?
|
||||
1 + (generate(int_ptr) % 5) : 1;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
x.push_back(std::pair<key_type const, mapped_type>(
|
||||
key, generate(mapped_ptr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <class X>
|
||||
struct unordered_generator_base
|
||||
: public boost::detail::if_true<test::is_set<X>::value>::
|
||||
BOOST_NESTED_TEMPLATE then<test::unordered_generator_set<X>,
|
||||
test::unordered_generator_map<X> >
|
||||
{
|
||||
};
|
||||
|
||||
template <class X>
|
||||
struct unordered_generator : public unordered_generator_base<X>::type
|
||||
{
|
||||
typedef typename unordered_generator_base<X>::type base;
|
||||
|
||||
unordered_generator(random_generator const& type = default_generator)
|
||||
: base(type)
|
||||
template <class X>
|
||||
struct unordered_generator_base
|
||||
: public boost::detail::if_true<
|
||||
test::is_set<X>::value
|
||||
>::BOOST_NESTED_TEMPLATE then<
|
||||
test::unordered_generator_set<X>,
|
||||
test::unordered_generator_map<X>
|
||||
>
|
||||
{
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <class X>
|
||||
struct random_values : public test::list<typename X::value_type>
|
||||
{
|
||||
random_values() {}
|
||||
|
||||
explicit random_values(std::size_t count,
|
||||
test::random_generator const& generator = test::default_generator)
|
||||
template <class X>
|
||||
struct unordered_generator : public unordered_generator_base<X>::type
|
||||
{
|
||||
fill(count, generator);
|
||||
}
|
||||
typedef BOOST_DEDUCED_TYPENAME unordered_generator_base<X>::type base;
|
||||
|
||||
void fill(std::size_t count,
|
||||
test::random_generator const& generator = test::default_generator)
|
||||
unordered_generator(random_generator const& type = default_generator)
|
||||
: base(type) {}
|
||||
};
|
||||
|
||||
template <class X>
|
||||
struct random_values
|
||||
: public test::list<BOOST_DEDUCED_TYPENAME X::value_type>
|
||||
{
|
||||
test::unordered_generator<X> gen(generator);
|
||||
gen.fill(*this, count);
|
||||
}
|
||||
};
|
||||
random_values(int count, test::random_generator const& generator =
|
||||
test::default_generator)
|
||||
{
|
||||
test::unordered_generator<X> gen(generator);
|
||||
gen.fill(*this, count);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+26
-26
@@ -6,37 +6,37 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER
|
||||
|
||||
#include "./equivalent.hpp"
|
||||
#include "./exception_test.hpp"
|
||||
#include "./list.hpp"
|
||||
#include <boost/config.hpp>
|
||||
#include <iterator>
|
||||
#include "./equivalent.hpp"
|
||||
#include "./list.hpp"
|
||||
#include "./exception_test.hpp"
|
||||
|
||||
namespace test {
|
||||
template <class X> class strong
|
||||
{
|
||||
typedef test::list<typename X::value_type> values_type;
|
||||
values_type values_;
|
||||
unsigned int allocations_;
|
||||
|
||||
public:
|
||||
void store(X const& x, unsigned int allocations = 0)
|
||||
namespace test
|
||||
{
|
||||
template <class X>
|
||||
class strong
|
||||
{
|
||||
DISABLE_EXCEPTIONS;
|
||||
values_.clear();
|
||||
values_.insert(x.cbegin(), x.cend());
|
||||
allocations_ = allocations;
|
||||
}
|
||||
typedef test::list<BOOST_DEDUCED_TYPENAME X::value_type> values_type;
|
||||
values_type values_;
|
||||
unsigned int allocations_;
|
||||
public:
|
||||
void store(X const& x, unsigned int allocations = 0) {
|
||||
DISABLE_EXCEPTIONS;
|
||||
values_.clear();
|
||||
values_.insert(x.cbegin(), x.cend());
|
||||
allocations_ = allocations;
|
||||
}
|
||||
|
||||
void test(X const& x, unsigned int allocations = 0) const
|
||||
{
|
||||
if (!(x.size() == values_.size() && test::equal(x.cbegin(), x.cend(),
|
||||
values_.begin(), test::equivalent)))
|
||||
BOOST_ERROR("Strong exception safety failure.");
|
||||
if (allocations != allocations_)
|
||||
BOOST_ERROR("Strong exception failure: extra allocations.");
|
||||
}
|
||||
};
|
||||
void test(X const& x, unsigned int allocations = 0) const {
|
||||
if(!(x.size() == values_.size() &&
|
||||
test::equal(x.cbegin(), x.cend(), values_.begin(),
|
||||
test::equivalent)))
|
||||
BOOST_ERROR("Strong exception safety failure.");
|
||||
if(allocations != allocations_)
|
||||
BOOST_ERROR("Strong exception failure: extra allocations.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+71
-166
@@ -6,194 +6,99 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_TEST_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_TEST_HEADER
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#define UNORDERED_AUTO_TEST(x) \
|
||||
struct BOOST_PP_CAT(x, _type) : public ::test::registered_test_base \
|
||||
{ \
|
||||
BOOST_PP_CAT(x, _type) \
|
||||
() : ::test::registered_test_base(BOOST_PP_STRINGIZE(x)) \
|
||||
{ \
|
||||
::test::get_state().add_test(this); \
|
||||
} \
|
||||
void run(); \
|
||||
}; \
|
||||
BOOST_PP_CAT(x, _type) x; \
|
||||
void BOOST_PP_CAT(x, _type)::run()
|
||||
#define UNORDERED_AUTO_TEST(x) \
|
||||
struct BOOST_PP_CAT(x, _type) : public ::test::registered_test_base { \
|
||||
BOOST_PP_CAT(x, _type)() \
|
||||
: ::test::registered_test_base(BOOST_PP_STRINGIZE(x)) \
|
||||
{ \
|
||||
::test::test_list::add_test(this); \
|
||||
} \
|
||||
void run(); \
|
||||
}; \
|
||||
BOOST_PP_CAT(x, _type) x; \
|
||||
void BOOST_PP_CAT(x, _type)::run() \
|
||||
|
||||
#define RUN_TESTS() \
|
||||
int main(int, char**) \
|
||||
{ \
|
||||
BOOST_UNORDERED_TEST_COMPILER_INFO() \
|
||||
::test::get_state().run_tests(); \
|
||||
return boost::report_errors(); \
|
||||
}
|
||||
|
||||
#define RUN_TESTS_QUIET() \
|
||||
int main(int, char**) \
|
||||
{ \
|
||||
BOOST_UNORDERED_TEST_COMPILER_INFO() \
|
||||
::test::get_state().run_tests(true); \
|
||||
return boost::report_errors(); \
|
||||
}
|
||||
|
||||
#define UNORDERED_SUB_TEST(x) \
|
||||
for (int UNORDERED_SUB_TEST_VALUE = ::test::get_state().start_sub_test(x); \
|
||||
UNORDERED_SUB_TEST_VALUE; \
|
||||
UNORDERED_SUB_TEST_VALUE = \
|
||||
::test::get_state().end_sub_test(x, UNORDERED_SUB_TEST_VALUE))
|
||||
#define RUN_TESTS() int main(int, char**) \
|
||||
{ ::test::test_list::run_tests(); return boost::report_errors(); } \
|
||||
|
||||
namespace test {
|
||||
struct registered_test_base {
|
||||
registered_test_base* next;
|
||||
char const* name;
|
||||
explicit registered_test_base(char const* n) : name(n) {}
|
||||
virtual void run() = 0;
|
||||
virtual ~registered_test_base() {}
|
||||
};
|
||||
|
||||
struct registered_test_base
|
||||
{
|
||||
registered_test_base* next;
|
||||
char const* name;
|
||||
explicit registered_test_base(char const* n) : name(n) {}
|
||||
virtual void run() = 0;
|
||||
virtual ~registered_test_base() {}
|
||||
};
|
||||
|
||||
struct state
|
||||
{
|
||||
bool is_quiet;
|
||||
registered_test_base* first_test;
|
||||
registered_test_base* last_test;
|
||||
|
||||
state() : is_quiet(false), first_test(0), last_test(0) {}
|
||||
|
||||
void add_test(registered_test_base* test)
|
||||
{
|
||||
if (last_test) {
|
||||
last_test->next = test;
|
||||
} else {
|
||||
first_test = test;
|
||||
}
|
||||
last_test = test;
|
||||
}
|
||||
|
||||
void run_tests(bool quiet = false)
|
||||
{
|
||||
is_quiet = quiet;
|
||||
|
||||
for (registered_test_base* i = first_test; i; i = i->next) {
|
||||
int error_count = boost::detail::test_errors();
|
||||
if (!quiet) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Running " << i->name << "\n"
|
||||
<< std::flush;
|
||||
namespace test_list {
|
||||
static inline registered_test_base*& first() {
|
||||
static registered_test_base* ptr = 0;
|
||||
return ptr;
|
||||
}
|
||||
i->run();
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << std::flush;
|
||||
if (quiet && error_count != boost::detail::test_errors()) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Error in: " << i->name << "\n"
|
||||
<< std::flush;
|
||||
|
||||
static inline registered_test_base*& last() {
|
||||
static registered_test_base* ptr = 0;
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int start_sub_test(char const* name)
|
||||
{
|
||||
if (!is_quiet) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Sub-test: " << name << "\n"
|
||||
<< std::flush;
|
||||
}
|
||||
// Add one because it's used as a loop condition.
|
||||
return boost::detail::test_errors() + 1;
|
||||
}
|
||||
static inline void add_test(registered_test_base* test) {
|
||||
if(last()) {
|
||||
last()->next = test;
|
||||
}
|
||||
else {
|
||||
first() = test;
|
||||
}
|
||||
|
||||
int end_sub_test(char const* name, int value)
|
||||
{
|
||||
if (is_quiet && value != boost::detail::test_errors() + 1) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Error in sub-test: " << name << "\n"
|
||||
<< std::flush;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
last() = test;
|
||||
}
|
||||
|
||||
// Get the currnet translation unit's test state.
|
||||
static inline state& get_state()
|
||||
{
|
||||
static state instance;
|
||||
return instance;
|
||||
}
|
||||
static inline void run_tests() {
|
||||
for(registered_test_base* i = first(); i; i = i->next) {
|
||||
std::cout<<"Running "<<i->name<<"\n"<<std::flush;
|
||||
i->run();
|
||||
std::cerr<<std::flush;
|
||||
std::cout<<std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define BOOST_UNORDERED_CPLUSPLUS __cplusplus
|
||||
#else
|
||||
#define BOOST_UNORDERED_CPLUSPLUS "(not defined)"
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_TEST_COMPILER_INFO() \
|
||||
{ \
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM \
|
||||
<< "Compiler: " << BOOST_COMPILER << "\n" \
|
||||
<< "Library: " << BOOST_STDLIB << "\n" \
|
||||
<< "__cplusplus: " << BOOST_UNORDERED_CPLUSPLUS << "\n\n" \
|
||||
<< "BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT: " \
|
||||
<< BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT << "\n" \
|
||||
<< "BOOST_UNORDERED_EMPLACE_LIMIT: " << BOOST_UNORDERED_EMPLACE_LIMIT \
|
||||
<< "\n" \
|
||||
<< "BOOST_UNORDERED_CXX11_CONSTRUCTION: " \
|
||||
<< BOOST_UNORDERED_CXX11_CONSTRUCTION << "\n\n" \
|
||||
<< std::flush; \
|
||||
}
|
||||
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/seq/fold_left.hpp>
|
||||
#include <boost/preprocessor/seq/for_each_product.hpp>
|
||||
#include <boost/preprocessor/seq/seq.hpp>
|
||||
#include <boost/preprocessor/seq/fold_left.hpp>
|
||||
#include <boost/preprocessor/seq/to_tuple.hpp>
|
||||
#include <boost/preprocessor/seq/seq.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
|
||||
// Run test with every combination of the parameters (a sequence of sequences)
|
||||
#define UNORDERED_TEST(name, parameters) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(UNORDERED_TEST_OP, ((name))((1))parameters)
|
||||
#define UNORDERED_TEST(name, parameters) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(UNORDERED_TEST_OP, \
|
||||
((name))((1)) parameters) \
|
||||
|
||||
#define UNORDERED_TEST_REPEAT(name, n, parameters) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(UNORDERED_TEST_OP, ((name))((n))parameters)
|
||||
#define UNORDERED_TEST_REPEAT(name, n, parameters) \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT(UNORDERED_TEST_OP, \
|
||||
((name))((n)) parameters) \
|
||||
|
||||
#define UNORDERED_TEST_OP(r, product) \
|
||||
UNORDERED_TEST_OP2(BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_SEQ_ELEM(1, product), \
|
||||
BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_TAIL(product)))
|
||||
#define UNORDERED_TEST_OP(r, product) \
|
||||
UNORDERED_TEST_OP2( \
|
||||
BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_SEQ_ELEM(1, product), \
|
||||
BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_TAIL(product))) \
|
||||
|
||||
#define UNORDERED_TEST_OP2(name, n, params) \
|
||||
UNORDERED_AUTO_TEST ( \
|
||||
BOOST_PP_SEQ_FOLD_LEFT(UNORDERED_TEST_OP_JOIN, name, params)) { \
|
||||
for (int i = 0; i < n; ++i) \
|
||||
name BOOST_PP_SEQ_TO_TUPLE(params); \
|
||||
}
|
||||
#define UNORDERED_TEST_OP2(name, n, params) \
|
||||
UNORDERED_AUTO_TEST( \
|
||||
BOOST_PP_SEQ_FOLD_LEFT(UNORDERED_TEST_OP_JOIN, name, params)) \
|
||||
{ \
|
||||
for (int i = 0; i < n; ++i) \
|
||||
name BOOST_PP_SEQ_TO_TUPLE(params); \
|
||||
} \
|
||||
|
||||
#define UNORDERED_TEST_OP_JOIN(s, state, elem) \
|
||||
BOOST_PP_CAT(state, BOOST_PP_CAT(_, elem))
|
||||
#define UNORDERED_TEST_OP_JOIN(s, state, elem) \
|
||||
BOOST_PP_CAT(state, BOOST_PP_CAT(_, elem)) \
|
||||
|
||||
#define UNORDERED_MULTI_TEST(name, impl, parameters) \
|
||||
UNORDERED_MULTI_TEST_REPEAT(name, impl, 1, parameters)
|
||||
|
||||
#define UNORDERED_MULTI_TEST_REPEAT(name, impl, n, parameters) \
|
||||
UNORDERED_AUTO_TEST (name) { \
|
||||
BOOST_PP_SEQ_FOR_EACH_PRODUCT( \
|
||||
UNORDERED_MULTI_TEST_OP, ((impl))((n))parameters) \
|
||||
}
|
||||
|
||||
#define UNORDERED_MULTI_TEST_OP(r, product) \
|
||||
UNORDERED_MULTI_TEST_OP2(BOOST_PP_SEQ_ELEM(0, product), \
|
||||
BOOST_PP_SEQ_ELEM(1, product), \
|
||||
BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_TAIL(product)))
|
||||
|
||||
// Need to wrap UNORDERED_SUB_TEST in a block to avoid an msvc bug.
|
||||
// https://support.microsoft.com/en-gb/help/315481/bug-too-many-unnested-loops-incorrectly-causes-a-c1061-compiler-error-in-visual-c
|
||||
#define UNORDERED_MULTI_TEST_OP2(name, n, params) \
|
||||
{ \
|
||||
UNORDERED_SUB_TEST(BOOST_PP_STRINGIZE( \
|
||||
BOOST_PP_SEQ_FOLD_LEFT(UNORDERED_TEST_OP_JOIN, name, params))) \
|
||||
{ \
|
||||
for (int i = 0; i < n; ++i) \
|
||||
name BOOST_PP_SEQ_TO_TUPLE(params); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+136
-119
@@ -9,135 +9,152 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER
|
||||
|
||||
#include "../objects/fwd.hpp"
|
||||
#include "./equivalent.hpp"
|
||||
#include "./helpers.hpp"
|
||||
#include "./list.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include "../objects/fwd.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include "./helpers.hpp"
|
||||
#include "./equivalent.hpp"
|
||||
#include "./list.hpp"
|
||||
|
||||
namespace test {
|
||||
template <typename X> struct equals_to_compare
|
||||
{
|
||||
typedef std::less<typename X::first_argument_type> type;
|
||||
};
|
||||
|
||||
template <> struct equals_to_compare<test::equal_to>
|
||||
{
|
||||
typedef test::less type;
|
||||
};
|
||||
|
||||
template<class T> struct equals_to_compare< std::equal_to<T> >
|
||||
{
|
||||
typedef std::less<T> type;
|
||||
};
|
||||
|
||||
template <class X1, class X2> void compare_range(X1 const& x1, X2 const& x2)
|
||||
{
|
||||
typedef test::list<typename X1::value_type> value_list;
|
||||
value_list values1(x1.begin(), x1.end());
|
||||
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));
|
||||
}
|
||||
|
||||
template <class X1, class X2, class T>
|
||||
void compare_pairs(X1 const& x1, X2 const& x2, T*)
|
||||
{
|
||||
test::list<T> values1(x1.first, x1.second);
|
||||
test::list<T> values2(x2.first, x2.second);
|
||||
values1.sort();
|
||||
values2.sort();
|
||||
BOOST_TEST(values1.size() == values2.size() &&
|
||||
test::equal(values1.begin(), values1.end(), values2.begin(),
|
||||
test::equivalent));
|
||||
}
|
||||
|
||||
template <typename X, bool is_set = test::is_set<X>::value,
|
||||
bool has_unique_keys = test::has_unique_keys<X>::value>
|
||||
struct ordered_base;
|
||||
|
||||
template <typename X> struct ordered_base<X, true, true>
|
||||
{
|
||||
typedef std::set<typename X::value_type,
|
||||
typename equals_to_compare<typename X::key_equal>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <typename X> struct ordered_base<X, true, false>
|
||||
{
|
||||
typedef std::multiset<typename X::value_type,
|
||||
typename equals_to_compare<typename X::key_equal>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <typename X> struct ordered_base<X, false, true>
|
||||
{
|
||||
typedef std::map<typename X::key_type, typename X::mapped_type,
|
||||
typename equals_to_compare<typename X::key_equal>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <typename X> struct ordered_base<X, false, false>
|
||||
{
|
||||
typedef std::multimap<typename X::key_type, typename X::mapped_type,
|
||||
typename equals_to_compare<typename X::key_equal>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <class X> class ordered : public ordered_base<X>::type
|
||||
{
|
||||
typedef typename ordered_base<X>::type base;
|
||||
|
||||
public:
|
||||
typedef typename base::key_compare key_compare;
|
||||
|
||||
ordered() : base() {}
|
||||
|
||||
explicit ordered(key_compare const& kc) : base(kc) {}
|
||||
|
||||
void compare(X const& x) { compare_range(x, *this); }
|
||||
|
||||
void compare_key(X const& x, typename X::value_type const& val)
|
||||
namespace test
|
||||
{
|
||||
template <typename X>
|
||||
struct equals_to_compare
|
||||
{
|
||||
compare_pairs(x.equal_range(get_key<X>(val)),
|
||||
this->equal_range(get_key<X>(val)), (typename X::value_type*)0);
|
||||
typedef std::less<BOOST_DEDUCED_TYPENAME X::first_argument_type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct equals_to_compare<test::equal_to>
|
||||
{
|
||||
typedef test::less type;
|
||||
};
|
||||
|
||||
template <class X1, class X2>
|
||||
void compare_range(X1 const& x1, X2 const& x2)
|
||||
{
|
||||
typedef test::list<BOOST_DEDUCED_TYPENAME X1::value_type> value_list;
|
||||
value_list values1(x1.begin(), x1.end());
|
||||
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));
|
||||
}
|
||||
|
||||
template <class It> void insert_range(It b, It e)
|
||||
template <class X1, class X2, class T>
|
||||
void compare_pairs(X1 const& x1, X2 const& x2, T*)
|
||||
{
|
||||
while (b != e) {
|
||||
this->insert(*b);
|
||||
++b;
|
||||
}
|
||||
test::list<T> values1(x1.first, x1.second);
|
||||
test::list<T> values2(x2.first, x2.second);
|
||||
values1.sort();
|
||||
values2.sort();
|
||||
BOOST_TEST(values1.size() == values2.size() &&
|
||||
test::equal(values1.begin(), values1.end(),
|
||||
values2.begin(), test::equivalent));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Equals>
|
||||
typename equals_to_compare<Equals>::type create_compare(Equals const&)
|
||||
{
|
||||
typename equals_to_compare<Equals>::type x;
|
||||
return x;
|
||||
}
|
||||
template <typename X>
|
||||
struct ordered_base;
|
||||
|
||||
template <class X> ordered<X> create_ordered(X const& container)
|
||||
{
|
||||
return ordered<X>(create_compare(container.key_eq()));
|
||||
}
|
||||
template <class V, class H, class P, class A>
|
||||
struct ordered_base<boost::unordered_set<V, H, P, A> >
|
||||
{
|
||||
typedef std::set<V,
|
||||
BOOST_DEDUCED_TYPENAME equals_to_compare<P>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <class X1, class X2>
|
||||
void check_container(X1 const& container, X2 const& values)
|
||||
{
|
||||
ordered<X1> tracker = create_ordered(container);
|
||||
tracker.insert_range(values.begin(), values.end());
|
||||
tracker.compare(container);
|
||||
}
|
||||
template <class V, class H, class P, class A>
|
||||
struct ordered_base<boost::unordered_multiset<V, H, P, A> >
|
||||
{
|
||||
typedef std::multiset<V,
|
||||
BOOST_DEDUCED_TYPENAME equals_to_compare<P>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <class K, class M, class H, class P, class A>
|
||||
struct ordered_base<boost::unordered_map<K, M, H, P, A> >
|
||||
{
|
||||
typedef std::map<K, M,
|
||||
BOOST_DEDUCED_TYPENAME equals_to_compare<P>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <class K, class M, class H, class P, class A>
|
||||
struct ordered_base<boost::unordered_multimap<K, M, H, P, A> >
|
||||
{
|
||||
typedef std::multimap<K, M,
|
||||
BOOST_DEDUCED_TYPENAME equals_to_compare<P>::type>
|
||||
type;
|
||||
};
|
||||
|
||||
template <class X>
|
||||
class ordered : public ordered_base<X>::type
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME ordered_base<X>::type base;
|
||||
public:
|
||||
typedef BOOST_DEDUCED_TYPENAME base::key_compare key_compare;
|
||||
|
||||
ordered()
|
||||
: base()
|
||||
{}
|
||||
|
||||
explicit ordered(key_compare const& kc)
|
||||
: base(kc)
|
||||
{}
|
||||
|
||||
void compare(X const& x)
|
||||
{
|
||||
compare_range(x, *this);
|
||||
}
|
||||
|
||||
void compare_key(X const& x,
|
||||
BOOST_DEDUCED_TYPENAME X::value_type const& val)
|
||||
{
|
||||
compare_pairs(
|
||||
x.equal_range(get_key<X>(val)),
|
||||
this->equal_range(get_key<X>(val)),
|
||||
(BOOST_DEDUCED_TYPENAME X::value_type*) 0);
|
||||
}
|
||||
|
||||
template <class It>
|
||||
void insert_range(It b, It e) {
|
||||
while(b != e) {
|
||||
this->insert(*b);
|
||||
++b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Equals>
|
||||
BOOST_DEDUCED_TYPENAME
|
||||
equals_to_compare<Equals>::type create_compare(Equals const&)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME equals_to_compare<Equals>::type x;
|
||||
return x;
|
||||
}
|
||||
|
||||
template <class X>
|
||||
ordered<X> create_ordered(X const& container)
|
||||
{
|
||||
return ordered<X>(create_compare(container.key_eq()));
|
||||
}
|
||||
|
||||
template <class X1, class X2>
|
||||
void check_container(X1 const& container, X2 const& values)
|
||||
{
|
||||
ordered<X1> tracker = create_ordered(container);
|
||||
tracker.insert_range(values.begin(), values.end());
|
||||
tracker.compare(container);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+253
-293
@@ -15,343 +15,303 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
struct allocator_false
|
||||
{
|
||||
enum
|
||||
struct allocator_false
|
||||
{
|
||||
is_select_on_copy = 0,
|
||||
is_propagate_on_swap = 0,
|
||||
is_propagate_on_assign = 0,
|
||||
is_propagate_on_move = 0,
|
||||
cxx11_construct = 0
|
||||
enum {
|
||||
is_select_on_copy = 0,
|
||||
is_propagate_on_swap = 0,
|
||||
is_propagate_on_assign = 0,
|
||||
is_propagate_on_move = 0,
|
||||
cxx11_construct = 0
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct allocator_flags_all
|
||||
{
|
||||
enum
|
||||
struct allocator_flags_all
|
||||
{
|
||||
is_select_on_copy = 1,
|
||||
is_propagate_on_swap = 1,
|
||||
is_propagate_on_assign = 1,
|
||||
is_propagate_on_move = 1,
|
||||
cxx11_construct = 1
|
||||
enum {
|
||||
is_select_on_copy = 1,
|
||||
is_propagate_on_swap = 1,
|
||||
is_propagate_on_assign = 1,
|
||||
is_propagate_on_move = 1,
|
||||
cxx11_construct = 1
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct select_copy : allocator_false
|
||||
{ enum { is_select_on_copy = 1 }; };
|
||||
struct propagate_swap : allocator_false
|
||||
{ enum { is_propagate_on_swap = 1 }; };
|
||||
struct propagate_assign : allocator_false
|
||||
{ enum { is_propagate_on_assign = 1 }; };
|
||||
struct propagate_move : allocator_false
|
||||
{ enum { is_propagate_on_move = 1 }; };
|
||||
|
||||
struct select_copy : allocator_false
|
||||
{
|
||||
enum
|
||||
struct no_select_copy : allocator_flags_all
|
||||
{ enum { is_select_on_copy = 0 }; };
|
||||
struct no_propagate_swap : allocator_flags_all
|
||||
{ enum { is_propagate_on_swap = 0 }; };
|
||||
struct no_propagate_assign : allocator_flags_all
|
||||
{ enum { is_propagate_on_assign = 0 }; };
|
||||
struct no_propagate_move : allocator_flags_all
|
||||
{ enum { is_propagate_on_move = 0 }; };
|
||||
|
||||
template <typename Flag>
|
||||
struct swap_allocator_base
|
||||
{
|
||||
is_select_on_copy = 1
|
||||
struct propagate_on_container_swap {
|
||||
enum { value = Flag::is_propagate_on_swap }; };
|
||||
};
|
||||
};
|
||||
struct propagate_swap : allocator_false
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_swap = 1
|
||||
};
|
||||
};
|
||||
struct propagate_assign : allocator_false
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_assign = 1
|
||||
};
|
||||
};
|
||||
struct propagate_move : allocator_false
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_move = 1
|
||||
};
|
||||
};
|
||||
|
||||
struct no_select_copy : allocator_flags_all
|
||||
{
|
||||
enum
|
||||
template <typename Flag>
|
||||
struct assign_allocator_base
|
||||
{
|
||||
is_select_on_copy = 0
|
||||
struct propagate_on_container_copy_assignment {
|
||||
enum { value = Flag::is_propagate_on_assign }; };
|
||||
};
|
||||
};
|
||||
struct no_propagate_swap : allocator_flags_all
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_swap = 0
|
||||
};
|
||||
};
|
||||
struct no_propagate_assign : allocator_flags_all
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_assign = 0
|
||||
};
|
||||
};
|
||||
struct no_propagate_move : allocator_flags_all
|
||||
{
|
||||
enum
|
||||
{
|
||||
is_propagate_on_move = 0
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Flag> struct swap_allocator_base
|
||||
{
|
||||
struct propagate_on_container_swap
|
||||
template <typename Flag>
|
||||
struct move_allocator_base
|
||||
{
|
||||
enum
|
||||
{
|
||||
value = Flag::is_propagate_on_swap
|
||||
};
|
||||
struct propagate_on_container_move_assignment {
|
||||
enum { value = Flag::is_propagate_on_move }; };
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Flag> struct assign_allocator_base
|
||||
{
|
||||
struct propagate_on_container_copy_assignment
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
value = Flag::is_propagate_on_assign
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Flag> struct move_allocator_base
|
||||
{
|
||||
struct propagate_on_container_move_assignment
|
||||
{
|
||||
enum
|
||||
{
|
||||
value = Flag::is_propagate_on_move
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace {
|
||||
// boostinspect:nounnamed
|
||||
bool force_equal_allocator_value = false;
|
||||
}
|
||||
|
||||
struct force_equal_allocator
|
||||
{
|
||||
bool old_value_;
|
||||
|
||||
explicit force_equal_allocator(bool value)
|
||||
: old_value_(force_equal_allocator_value)
|
||||
{
|
||||
force_equal_allocator_value = value;
|
||||
// boostinspect:nounnamed
|
||||
bool force_equal_allocator_value = false;
|
||||
}
|
||||
|
||||
~force_equal_allocator() { force_equal_allocator_value = old_value_; }
|
||||
};
|
||||
|
||||
template <typename T> struct cxx11_allocator_base
|
||||
{
|
||||
int tag_;
|
||||
int selected_;
|
||||
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T const* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
explicit cxx11_allocator_base(int t) : tag_(t), selected_(0)
|
||||
struct force_equal_allocator
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
bool old_value_;
|
||||
|
||||
explicit force_equal_allocator(bool value)
|
||||
: old_value_(force_equal_allocator_value)
|
||||
{ force_equal_allocator_value = value; }
|
||||
|
||||
~force_equal_allocator()
|
||||
{ force_equal_allocator_value = old_value_; }
|
||||
};
|
||||
|
||||
template <typename Y>
|
||||
cxx11_allocator_base(cxx11_allocator_base<Y> const& x)
|
||||
: tag_(x.tag_), selected_(x.selected_)
|
||||
template <typename T>
|
||||
struct cxx11_allocator_base
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
int tag_;
|
||||
int selected_;
|
||||
|
||||
cxx11_allocator_base(cxx11_allocator_base const& x)
|
||||
: tag_(x.tag_), selected_(x.selected_)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T const* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
~cxx11_allocator_base() { detail::tracker.allocator_unref(); }
|
||||
explicit cxx11_allocator_base(int t)
|
||||
: tag_(t), selected_(0)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
template <typename Y> cxx11_allocator_base(
|
||||
cxx11_allocator_base<Y> const& x)
|
||||
: tag_(x.tag_), selected_(x.selected_)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator_base& operator=(cxx11_allocator_base const& x) = default;
|
||||
cxx11_allocator_base(cxx11_allocator_base const& x)
|
||||
: tag_(x.tag_), selected_(x.selected_)
|
||||
{
|
||||
detail::tracker.allocator_ref();
|
||||
}
|
||||
|
||||
~cxx11_allocator_base()
|
||||
{
|
||||
detail::tracker.allocator_unref();
|
||||
}
|
||||
|
||||
pointer address(reference r)
|
||||
{
|
||||
return pointer(&r);
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r)
|
||||
{
|
||||
return const_pointer(&r);
|
||||
}
|
||||
|
||||
pointer allocate(size_type n) {
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, void const* u)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
// Only checking tags when propagating swap.
|
||||
// Note that tags will be tested
|
||||
// properly in the normal allocator.
|
||||
detail::tracker.track_deallocate((void*) p, n, sizeof(T), tag_,
|
||||
!force_equal_allocator_value);
|
||||
::operator delete((void*) p);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t) {
|
||||
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
|
||||
new(p) T(t);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template<typename... Args> void construct(T* p, BOOST_FWD_REF(Args)... args) {
|
||||
detail::tracker.track_construct((void*) p, sizeof(T), tag_);
|
||||
new(p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
void destroy(T* p) {
|
||||
detail::tracker.track_destroy((void*) p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*)ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, void const*)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
detail::tracker.track_allocate((void*)ptr, n, sizeof(T), tag_);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
// Only checking tags when propagating swap.
|
||||
// Note that tags will be tested
|
||||
// properly in the normal allocator.
|
||||
detail::tracker.track_deallocate(
|
||||
(void*)p, n, sizeof(T), tag_, !force_equal_allocator_value);
|
||||
::operator delete((void*)p);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(t);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <typename... Args>
|
||||
void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p)
|
||||
{
|
||||
detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
{
|
||||
return (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Flags = propagate_swap, typename Enable = void>
|
||||
struct cxx11_allocator;
|
||||
|
||||
template <typename T, typename Flags>
|
||||
struct cxx11_allocator<T, Flags,
|
||||
typename boost::disable_if_c<Flags::is_select_on_copy>::type>
|
||||
: public cxx11_allocator_base<T>,
|
||||
public swap_allocator_base<Flags>,
|
||||
public assign_allocator_base<Flags>,
|
||||
public move_allocator_base<Flags>,
|
||||
Flags
|
||||
{
|
||||
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, < 402000)
|
||||
template <typename U> struct rebind
|
||||
{
|
||||
typedef cxx11_allocator<U, Flags> other;
|
||||
size_type max_size() const {
|
||||
return (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
explicit cxx11_allocator(int t = 0) : cxx11_allocator_base<T>(t) {}
|
||||
template <typename T, typename Flags = propagate_swap,
|
||||
typename Enable = void>
|
||||
struct cxx11_allocator;
|
||||
|
||||
template <typename Y>
|
||||
cxx11_allocator(cxx11_allocator<Y, Flags> const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
template <typename T, typename Flags>
|
||||
struct cxx11_allocator<
|
||||
T, Flags,
|
||||
typename boost::disable_if_c<Flags::is_select_on_copy>::type
|
||||
> : public cxx11_allocator_base<T>,
|
||||
public swap_allocator_base<Flags>,
|
||||
public assign_allocator_base<Flags>,
|
||||
public move_allocator_base<Flags>,
|
||||
Flags
|
||||
{
|
||||
}
|
||||
template <typename U> struct rebind {
|
||||
typedef cxx11_allocator<U, Flags> other;
|
||||
};
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x) : cxx11_allocator_base<T>(x) {}
|
||||
explicit cxx11_allocator(int t = 0)
|
||||
: cxx11_allocator_base<T>(t)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Y> cxx11_allocator(
|
||||
cxx11_allocator<Y, Flags> const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
{
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator& operator=(cxx11_allocator const& x) = default;
|
||||
#endif
|
||||
cxx11_allocator(cxx11_allocator const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
{
|
||||
}
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
{
|
||||
return force_equal_allocator_value || (this->tag_ == x.tag_);
|
||||
}
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
{
|
||||
return force_equal_allocator_value || (this->tag_ == x.tag_);
|
||||
}
|
||||
|
||||
bool operator!=(cxx11_allocator const& x) const { return !(*this == x); }
|
||||
};
|
||||
|
||||
template <typename T, typename Flags>
|
||||
struct cxx11_allocator<T, Flags,
|
||||
typename boost::enable_if_c<Flags::is_select_on_copy>::type>
|
||||
: public cxx11_allocator_base<T>,
|
||||
public swap_allocator_base<Flags>,
|
||||
public assign_allocator_base<Flags>,
|
||||
public move_allocator_base<Flags>,
|
||||
Flags
|
||||
{
|
||||
cxx11_allocator select_on_container_copy_construction() const
|
||||
{
|
||||
cxx11_allocator tmp(*this);
|
||||
++tmp.selected_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, < 402000)
|
||||
template <typename U> struct rebind
|
||||
{
|
||||
typedef cxx11_allocator<U, Flags> other;
|
||||
bool operator!=(cxx11_allocator const& x) const
|
||||
{
|
||||
return !(*this == x);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
explicit cxx11_allocator(int t = 0) : cxx11_allocator_base<T>(t) {}
|
||||
|
||||
template <typename Y>
|
||||
cxx11_allocator(cxx11_allocator<Y, Flags> const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
template <typename T, typename Flags>
|
||||
struct cxx11_allocator<
|
||||
T, Flags,
|
||||
typename boost::enable_if_c<Flags::is_select_on_copy>::type
|
||||
> : public cxx11_allocator_base<T>,
|
||||
public swap_allocator_base<Flags>,
|
||||
public assign_allocator_base<Flags>,
|
||||
public move_allocator_base<Flags>,
|
||||
Flags
|
||||
{
|
||||
cxx11_allocator select_on_container_copy_construction() const
|
||||
{
|
||||
cxx11_allocator tmp(*this);
|
||||
++tmp.selected_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <typename U> struct rebind {
|
||||
typedef cxx11_allocator<U, Flags> other;
|
||||
};
|
||||
|
||||
explicit cxx11_allocator(int t = 0)
|
||||
: cxx11_allocator_base<T>(t)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Y> cxx11_allocator(
|
||||
cxx11_allocator<Y, Flags> const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
{
|
||||
}
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x)
|
||||
: cxx11_allocator_base<T>(x)
|
||||
{
|
||||
}
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
{
|
||||
return force_equal_allocator_value || (this->tag_ == x.tag_);
|
||||
}
|
||||
|
||||
bool operator!=(cxx11_allocator const& x) const
|
||||
{
|
||||
return !(*this == x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Flags>
|
||||
bool equivalent_impl(
|
||||
cxx11_allocator<T, Flags> const& x,
|
||||
cxx11_allocator<T, Flags> const& y,
|
||||
test::derived_type)
|
||||
{
|
||||
return x.tag_ == y.tag_;
|
||||
}
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x) : cxx11_allocator_base<T>(x) {}
|
||||
// Function to check how many times an allocator has been selected,
|
||||
// return 0 for other allocators.
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator& operator=(cxx11_allocator const& x) = default;
|
||||
#endif
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
struct convert_from_anything
|
||||
{
|
||||
return force_equal_allocator_value || (this->tag_ == x.tag_);
|
||||
template <typename T>
|
||||
convert_from_anything(T const&) {}
|
||||
};
|
||||
|
||||
inline int selected_count(convert_from_anything)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator!=(cxx11_allocator const& x) const { return !(*this == x); }
|
||||
};
|
||||
|
||||
template <typename T, typename Flags>
|
||||
bool equivalent_impl(cxx11_allocator<T, Flags> const& x,
|
||||
cxx11_allocator<T, Flags> const& y, test::derived_type)
|
||||
{
|
||||
return x.tag_ == y.tag_;
|
||||
}
|
||||
|
||||
// Function to check how many times an allocator has been selected,
|
||||
// return 0 for other allocators.
|
||||
|
||||
struct convert_from_anything
|
||||
{
|
||||
template <typename T> convert_from_anything(T const&) {}
|
||||
};
|
||||
|
||||
inline int selected_count(convert_from_anything) { return 0; }
|
||||
|
||||
template <typename T, typename Flags>
|
||||
int selected_count(cxx11_allocator<T, Flags> const& x)
|
||||
{
|
||||
return x.selected_;
|
||||
}
|
||||
template <typename T, typename Flags>
|
||||
int selected_count(cxx11_allocator<T, Flags> const& x)
|
||||
{
|
||||
return x.selected_;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+429
-584
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,13 @@
|
||||
#if !defined(BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER
|
||||
|
||||
namespace test {
|
||||
class object;
|
||||
class hash;
|
||||
class less;
|
||||
class equal_to;
|
||||
template <class T> class allocator;
|
||||
namespace test
|
||||
{
|
||||
class object;
|
||||
class hash;
|
||||
class less;
|
||||
class equal_to;
|
||||
template <class T> class allocator;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+272
-401
@@ -10,33 +10,26 @@
|
||||
#if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER)
|
||||
#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER
|
||||
|
||||
#include <boost/move/move.hpp>
|
||||
#include <cstddef>
|
||||
#include <boost/move/move.hpp>
|
||||
#include <utility>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable:4100) // unreferenced formal parameter
|
||||
#endif
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, == 1500)
|
||||
#define BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED 1
|
||||
#else
|
||||
#define BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED 0
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
namespace minimal {
|
||||
namespace test
|
||||
{
|
||||
namespace minimal
|
||||
{
|
||||
class destructible;
|
||||
class copy_constructible;
|
||||
class copy_constructible_equality_comparable;
|
||||
class default_assignable;
|
||||
class assignable;
|
||||
|
||||
struct ampersand_operator_used
|
||||
{
|
||||
ampersand_operator_used() { BOOST_TEST(false); }
|
||||
};
|
||||
struct ampersand_operator_used {};
|
||||
|
||||
template <class T> class hash;
|
||||
template <class T> class equal_to;
|
||||
@@ -47,186 +40,167 @@ namespace test {
|
||||
|
||||
struct constructor_param
|
||||
{
|
||||
operator int() const { return 0; }
|
||||
operator int() const { return 0; }
|
||||
};
|
||||
|
||||
class destructible
|
||||
{
|
||||
public:
|
||||
destructible(constructor_param const&) {}
|
||||
~destructible() {}
|
||||
void dummy_member() const {}
|
||||
|
||||
destructible(constructor_param const&) {}
|
||||
~destructible() {}
|
||||
private:
|
||||
destructible(destructible const&);
|
||||
destructible& operator=(destructible const&);
|
||||
destructible(destructible const&);
|
||||
destructible& operator=(destructible const&);
|
||||
};
|
||||
|
||||
class copy_constructible
|
||||
{
|
||||
public:
|
||||
copy_constructible(constructor_param const&) {}
|
||||
copy_constructible(copy_constructible const&) {}
|
||||
~copy_constructible() {}
|
||||
void dummy_member() const {}
|
||||
|
||||
copy_constructible(constructor_param const&) {}
|
||||
copy_constructible(copy_constructible const&) {}
|
||||
~copy_constructible() {}
|
||||
private:
|
||||
copy_constructible& operator=(copy_constructible const&);
|
||||
copy_constructible() {}
|
||||
copy_constructible& operator=(copy_constructible const&);
|
||||
copy_constructible() {}
|
||||
};
|
||||
|
||||
class copy_constructible_equality_comparable
|
||||
{
|
||||
public:
|
||||
copy_constructible_equality_comparable(constructor_param const&) {}
|
||||
copy_constructible_equality_comparable(constructor_param const&) {}
|
||||
|
||||
copy_constructible_equality_comparable(
|
||||
copy_constructible_equality_comparable const&)
|
||||
{
|
||||
}
|
||||
copy_constructible_equality_comparable(
|
||||
copy_constructible_equality_comparable const&)
|
||||
{
|
||||
}
|
||||
|
||||
~copy_constructible_equality_comparable() {}
|
||||
|
||||
void dummy_member() const {}
|
||||
~copy_constructible_equality_comparable()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
copy_constructible_equality_comparable& operator=(
|
||||
copy_constructible_equality_comparable const&);
|
||||
copy_constructible_equality_comparable() {}
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
copy_constructible_equality_comparable& operator=(
|
||||
copy_constructible_equality_comparable const&);
|
||||
copy_constructible_equality_comparable() {}
|
||||
ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
bool operator==(copy_constructible_equality_comparable,
|
||||
copy_constructible_equality_comparable)
|
||||
bool operator==(
|
||||
copy_constructible_equality_comparable,
|
||||
copy_constructible_equality_comparable)
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(copy_constructible_equality_comparable,
|
||||
copy_constructible_equality_comparable)
|
||||
bool operator!=(
|
||||
copy_constructible_equality_comparable,
|
||||
copy_constructible_equality_comparable)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
class default_assignable
|
||||
{
|
||||
public:
|
||||
default_assignable(constructor_param const&) {}
|
||||
default_assignable(constructor_param const&) {}
|
||||
|
||||
default_assignable() {}
|
||||
default_assignable()
|
||||
{
|
||||
}
|
||||
|
||||
default_assignable(default_assignable const&) {}
|
||||
default_assignable(default_assignable const&)
|
||||
{
|
||||
}
|
||||
|
||||
default_assignable& operator=(default_assignable const&) { return *this; }
|
||||
default_assignable& operator=(default_assignable const&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
~default_assignable() {}
|
||||
~default_assignable()
|
||||
{
|
||||
}
|
||||
|
||||
void dummy_member() const {}
|
||||
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
ampersand_operator_used operator&() const {
|
||||
return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
class assignable
|
||||
{
|
||||
public:
|
||||
assignable(constructor_param const&) {}
|
||||
assignable(assignable const&) {}
|
||||
assignable& operator=(assignable const&) { return *this; }
|
||||
~assignable() {}
|
||||
void dummy_member() const {}
|
||||
assignable(constructor_param const&) {}
|
||||
assignable(assignable const&) {}
|
||||
assignable& operator=(assignable const&) { return *this; }
|
||||
~assignable() {}
|
||||
|
||||
private:
|
||||
assignable() {}
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
assignable() {}
|
||||
// TODO: This messes up a concept check in the tests.
|
||||
//ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
struct movable_init
|
||||
{
|
||||
};
|
||||
struct movable_init {};
|
||||
|
||||
class movable1
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(movable1)
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(movable1)
|
||||
|
||||
public:
|
||||
movable1(constructor_param const&) {}
|
||||
movable1() {}
|
||||
explicit movable1(movable_init) {}
|
||||
movable1(BOOST_RV_REF(movable1)) {}
|
||||
movable1& operator=(BOOST_RV_REF(movable1)) { return *this; }
|
||||
~movable1() {}
|
||||
void dummy_member() const {}
|
||||
movable1(constructor_param const&) {}
|
||||
movable1() {}
|
||||
explicit movable1(movable_init) {}
|
||||
movable1(BOOST_RV_REF(movable1)) {}
|
||||
movable1& operator=(BOOST_RV_REF(movable1)) { return *this; }
|
||||
~movable1() {}
|
||||
};
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
#if !defined(BOOST_NO_RVALUE_REFERENCES)
|
||||
class movable2
|
||||
{
|
||||
public:
|
||||
movable2(constructor_param const&) {}
|
||||
explicit movable2(movable_init) {}
|
||||
movable2(movable2&&) {}
|
||||
~movable2() {}
|
||||
movable2& operator=(movable2&&) { return *this; }
|
||||
void dummy_member() const {}
|
||||
|
||||
movable2(constructor_param const&) {}
|
||||
explicit movable2(movable_init) {}
|
||||
movable2(movable2&&) {}
|
||||
~movable2() {}
|
||||
movable2& operator=(movable2&&) { return *this; }
|
||||
private:
|
||||
movable2() {}
|
||||
movable2(movable2 const&);
|
||||
movable2& operator=(movable2 const&);
|
||||
movable2() {}
|
||||
movable2(movable2 const&);
|
||||
movable2& operator=(movable2 const&);
|
||||
};
|
||||
#else
|
||||
typedef movable1 movable2;
|
||||
#endif
|
||||
|
||||
template <class T> class hash
|
||||
template <class T>
|
||||
class hash
|
||||
{
|
||||
public:
|
||||
hash(constructor_param const&) {}
|
||||
hash() {}
|
||||
hash(hash const&) {}
|
||||
hash& operator=(hash const&) { return *this; }
|
||||
~hash() {}
|
||||
hash(constructor_param const&) {}
|
||||
hash() {}
|
||||
hash(hash const&) {}
|
||||
hash& operator=(hash const&) { return *this; }
|
||||
~hash() {}
|
||||
|
||||
std::size_t operator()(T const&) const { return 0; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
std::size_t operator()(T const&) const { return 0; }
|
||||
private:
|
||||
ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
template <class T> class equal_to
|
||||
template <class T>
|
||||
class equal_to
|
||||
{
|
||||
public:
|
||||
equal_to(constructor_param const&) {}
|
||||
equal_to() {}
|
||||
equal_to(equal_to const&) {}
|
||||
equal_to& operator=(equal_to const&) { return *this; }
|
||||
~equal_to() {}
|
||||
equal_to(constructor_param const&) {}
|
||||
equal_to() {}
|
||||
equal_to(equal_to const&) {}
|
||||
equal_to& operator=(equal_to const&) { return *this; }
|
||||
~equal_to() {}
|
||||
|
||||
bool operator()(T const&, T const&) const { return true; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
bool operator()(T const&, T const&) const { return true; }
|
||||
private:
|
||||
ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
template <class T> class ptr;
|
||||
@@ -235,391 +209,288 @@ namespace test {
|
||||
struct void_ptr
|
||||
{
|
||||
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
|
||||
template <typename T> friend class ptr;
|
||||
|
||||
template <typename T>
|
||||
friend class ptr;
|
||||
private:
|
||||
#endif
|
||||
|
||||
void* ptr_;
|
||||
void* ptr_;
|
||||
|
||||
public:
|
||||
void_ptr() : ptr_(0) {}
|
||||
void_ptr() : ptr_(0) {}
|
||||
|
||||
template <typename T> explicit void_ptr(ptr<T> const& x) : ptr_(x.ptr_) {}
|
||||
template <typename T>
|
||||
explicit void_ptr(ptr<T> const& x) : ptr_(x.ptr_) {}
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
bool operator==(void_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(void_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator==(void_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(void_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
};
|
||||
|
||||
class void_const_ptr
|
||||
{
|
||||
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
|
||||
template <typename T> friend class const_ptr;
|
||||
|
||||
template <typename T>
|
||||
friend class const_ptr;
|
||||
private:
|
||||
#endif
|
||||
|
||||
void* ptr_;
|
||||
void* ptr_;
|
||||
|
||||
public:
|
||||
void_const_ptr() : ptr_(0) {}
|
||||
void_const_ptr() : ptr_(0) {}
|
||||
|
||||
template <typename T>
|
||||
explicit void_const_ptr(const_ptr<T> const& x) : ptr_(x.ptr_)
|
||||
{
|
||||
}
|
||||
template <typename T>
|
||||
explicit void_const_ptr(const_ptr<T> const& x) : ptr_(x.ptr_) {}
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
bool operator==(void_const_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(void_const_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator==(void_const_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(void_const_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
};
|
||||
|
||||
template <class T> class ptr
|
||||
template <class T>
|
||||
class ptr
|
||||
{
|
||||
friend class allocator<T>;
|
||||
friend class const_ptr<T>;
|
||||
friend struct void_ptr;
|
||||
friend class allocator<T>;
|
||||
friend class const_ptr<T>;
|
||||
friend struct void_ptr;
|
||||
|
||||
T* ptr_;
|
||||
|
||||
ptr(T* x) : ptr_(x) {}
|
||||
T* ptr_;
|
||||
|
||||
ptr(T* x) : ptr_(x) {}
|
||||
public:
|
||||
ptr() : ptr_(0) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
|
||||
ptr() : ptr_(0) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*) x.ptr_) {}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
ptr& operator++()
|
||||
{
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
ptr operator++(int)
|
||||
{
|
||||
ptr tmp(*this);
|
||||
++ptr_;
|
||||
return tmp;
|
||||
}
|
||||
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p)
|
||||
{
|
||||
return ptr<T>(s + p.ptr_);
|
||||
}
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
ptr& operator++() { ++ptr_; return *this; }
|
||||
ptr operator++(int) { ptr tmp(*this); ++ptr_; return tmp; }
|
||||
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p)
|
||||
{ return ptr<T>(s + p.ptr_); }
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
bool operator==(ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator<(ptr const& x) const { return ptr_ < x.ptr_; }
|
||||
bool operator>(ptr const& x) const { return ptr_ > x.ptr_; }
|
||||
bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; }
|
||||
bool operator>=(ptr const& x) const { return ptr_ >= x.ptr_; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
bool operator==(ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator<(ptr const& x) const { return ptr_ < x.ptr_; }
|
||||
bool operator>(ptr const& x) const { return ptr_ > x.ptr_; }
|
||||
bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; }
|
||||
bool operator>=(ptr const& x) const { return ptr_ >= x.ptr_; }
|
||||
private:
|
||||
// TODO:
|
||||
//ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
template <class T> class const_ptr
|
||||
template <class T>
|
||||
class const_ptr
|
||||
{
|
||||
friend class allocator<T>;
|
||||
friend struct const_void_ptr;
|
||||
friend class allocator<T>;
|
||||
friend struct const_void_ptr;
|
||||
|
||||
T const* ptr_;
|
||||
|
||||
const_ptr(T const* ptr) : ptr_(ptr) {}
|
||||
T const* ptr_;
|
||||
|
||||
const_ptr(T const* ptr) : ptr_(ptr) {}
|
||||
public:
|
||||
const_ptr() : ptr_(0) {}
|
||||
const_ptr(ptr<T> const& x) : ptr_(x.ptr_) {}
|
||||
explicit const_ptr(void_const_ptr const& x) : ptr_((T const*)x.ptr_) {}
|
||||
const_ptr() : ptr_(0) {}
|
||||
const_ptr(ptr<T> const& x) : ptr_(x.ptr_) {}
|
||||
explicit const_ptr(void_const_ptr const& x) : ptr_((T const*) x.ptr_) {}
|
||||
|
||||
T const& operator*() const { return *ptr_; }
|
||||
T const* operator->() const { return ptr_; }
|
||||
const_ptr& operator++()
|
||||
{
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
const_ptr operator++(int)
|
||||
{
|
||||
const_ptr tmp(*this);
|
||||
++ptr_;
|
||||
return tmp;
|
||||
}
|
||||
const_ptr operator+(std::ptrdiff_t s) const
|
||||
{
|
||||
return const_ptr(ptr_ + s);
|
||||
}
|
||||
friend const_ptr operator+(std::ptrdiff_t s, const_ptr p)
|
||||
{
|
||||
return ptr<T>(s + p.ptr_);
|
||||
}
|
||||
T const& operator[](int s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
operator bool() const { return !!ptr_; }
|
||||
T const& operator*() const { return *ptr_; }
|
||||
T const* operator->() const { return ptr_; }
|
||||
const_ptr& operator++() { ++ptr_; return *this; }
|
||||
const_ptr operator++(int) { const_ptr tmp(*this); ++ptr_; return tmp; }
|
||||
const_ptr operator+(std::ptrdiff_t s) const
|
||||
{ return const_ptr(ptr_ + s); }
|
||||
friend const_ptr operator+(std::ptrdiff_t s, const_ptr p)
|
||||
{ return ptr<T>(s + p.ptr_); }
|
||||
T const& operator[](int s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
bool operator==(const_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(const_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator<(const_ptr const& x) const { return ptr_ < x.ptr_; }
|
||||
bool operator>(const_ptr const& x) const { return ptr_ > x.ptr_; }
|
||||
bool operator<=(const_ptr const& x) const { return ptr_ <= x.ptr_; }
|
||||
bool operator>=(const_ptr const& x) const { return ptr_ >= x.ptr_; }
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
bool operator==(const_ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(const_ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator<(const_ptr const& x) const { return ptr_ < x.ptr_; }
|
||||
bool operator>(const_ptr const& x) const { return ptr_ > x.ptr_; }
|
||||
bool operator<=(const_ptr const& x) const { return ptr_ <= x.ptr_; }
|
||||
bool operator>=(const_ptr const& x) const { return ptr_ >= x.ptr_; }
|
||||
private:
|
||||
// TODO:
|
||||
//ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
template <class T> class allocator
|
||||
template <class T>
|
||||
class allocator
|
||||
{
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef void_ptr void_pointer;
|
||||
typedef void_const_ptr const_void_pointer;
|
||||
typedef ptr<T> pointer;
|
||||
typedef const_ptr<T> const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef void_ptr void_pointer;
|
||||
typedef void_const_ptr const_void_pointer;
|
||||
typedef ptr<T> pointer;
|
||||
typedef const_ptr<T> const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
template <class U> struct rebind
|
||||
{
|
||||
typedef allocator<U> other;
|
||||
};
|
||||
template <class U> struct rebind { typedef allocator<U> other; };
|
||||
|
||||
allocator() {}
|
||||
template <class Y> allocator(allocator<Y> const&) {}
|
||||
allocator(allocator const&) {}
|
||||
~allocator() {}
|
||||
allocator() {}
|
||||
template <class Y> allocator(allocator<Y> const&) {}
|
||||
allocator(allocator const&) {}
|
||||
~allocator() {}
|
||||
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
pointer allocate(size_type n) {
|
||||
return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
|
||||
template <class Y> pointer allocate(size_type n, const_ptr<Y>)
|
||||
{
|
||||
return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
template <class Y>
|
||||
pointer allocate(size_type n, const_ptr<Y> u)
|
||||
{
|
||||
return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type)
|
||||
{
|
||||
::operator delete((void*)p.ptr_);
|
||||
}
|
||||
void deallocate(pointer p, size_type)
|
||||
{
|
||||
::operator delete((void*) p.ptr_);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t) { new ((void*)p) T(t); }
|
||||
void construct(T* p, T const& t) { new((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template<class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args) {
|
||||
new((void*)p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p) { p->~T(); }
|
||||
void destroy(T* p) { p->~T(); }
|
||||
|
||||
size_type max_size() const { return 1000; }
|
||||
size_type max_size() const { return 1000; }
|
||||
|
||||
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) || \
|
||||
BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
|
||||
public:
|
||||
allocator& operator=(allocator const&) { return *this; }
|
||||
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) || \
|
||||
BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
|
||||
public: allocator& operator=(allocator const&) { return *this;}
|
||||
#else
|
||||
private: allocator& operator=(allocator const&);
|
||||
#endif
|
||||
private:
|
||||
allocator& operator=(allocator const&);
|
||||
#endif
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T> class allocator<T const>
|
||||
{
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef void_ptr void_pointer;
|
||||
typedef void_const_ptr const_void_pointer;
|
||||
// Maybe these two should be const_ptr<T>
|
||||
typedef ptr<T const> pointer;
|
||||
typedef const_ptr<T const> const_pointer;
|
||||
typedef T const& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T const value_type;
|
||||
|
||||
template <class U> struct rebind
|
||||
{
|
||||
typedef allocator<U> other;
|
||||
};
|
||||
|
||||
allocator() {}
|
||||
template <class Y> allocator(allocator<Y> const&) {}
|
||||
allocator(allocator const&) {}
|
||||
~allocator() {}
|
||||
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
return pointer(static_cast<T const*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
|
||||
template <class Y> pointer allocate(size_type n, const_ptr<Y>)
|
||||
{
|
||||
return pointer(static_cast<T const*>(::operator new(n * sizeof(T))));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type)
|
||||
{
|
||||
::operator delete((void*)p.ptr_);
|
||||
}
|
||||
|
||||
void construct(T const* p, T const& t) { new ((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args>
|
||||
void construct(T const* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T const* p) { p->~T(); }
|
||||
|
||||
size_type max_size() const { return 1000; }
|
||||
|
||||
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) || \
|
||||
BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
|
||||
public:
|
||||
allocator& operator=(allocator const&) { return *this; }
|
||||
#else
|
||||
private:
|
||||
allocator& operator=(allocator const&);
|
||||
#endif
|
||||
#if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED
|
||||
ampersand_operator_used operator&() const
|
||||
{
|
||||
return ampersand_operator_used();
|
||||
}
|
||||
#endif
|
||||
ampersand_operator_used operator&() const { return ampersand_operator_used(); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline bool operator==(allocator<T> const&, allocator<T> const&)
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool operator!=(allocator<T> const&, allocator<T> const&)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T> void swap(allocator<T>&, allocator<T>&) {}
|
||||
template <class T>
|
||||
void swap(allocator<T>&, allocator<T>&)
|
||||
{
|
||||
}
|
||||
|
||||
// C++11 allocator
|
||||
//
|
||||
// Not a fully minimal C++11 allocator, just what I support. Hopefully will
|
||||
// cut down further in the future.
|
||||
|
||||
template <class T> class cxx11_allocator
|
||||
template <class T>
|
||||
class cxx11_allocator
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
// template <class U> struct rebind { typedef cxx11_allocator<U> other; };
|
||||
typedef T value_type;
|
||||
template <class U> struct rebind { typedef cxx11_allocator<U> other; };
|
||||
|
||||
cxx11_allocator() {}
|
||||
template <class Y> cxx11_allocator(cxx11_allocator<Y> const&) {}
|
||||
cxx11_allocator(cxx11_allocator const&) {}
|
||||
~cxx11_allocator() {}
|
||||
cxx11_allocator() {}
|
||||
template <class Y> cxx11_allocator(cxx11_allocator<Y> const&) {}
|
||||
cxx11_allocator(cxx11_allocator const&) {}
|
||||
~cxx11_allocator() {}
|
||||
|
||||
T* address(T& r) { return &r; }
|
||||
T const* address(T const& r) { return &r; }
|
||||
T* address(T& r) { return &r; }
|
||||
T const* address(T const& r) { return &r; }
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
T* allocate(std::size_t n) {
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
template <class Y> T* allocate(std::size_t n, const_ptr<Y>)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
template <class Y>
|
||||
T* allocate(std::size_t n, const_ptr<Y> u) {
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete((void*)p); }
|
||||
void deallocate(T* p, std::size_t) {
|
||||
::operator delete((void*) p);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t) { new ((void*)p) T(t); }
|
||||
void construct(T* p, T const& t) { new((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#if !defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||
template<class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args) {
|
||||
new((void*)p) T(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p) { p->~T(); }
|
||||
void destroy(T* p) { p->~T(); }
|
||||
|
||||
std::size_t max_size() const { return 1000u; }
|
||||
std::size_t max_size() const { return 1000u; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline bool operator==(cxx11_allocator<T> const&, cxx11_allocator<T> const&)
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool operator!=(cxx11_allocator<T> const&, cxx11_allocator<T> const&)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T> void swap(cxx11_allocator<T>&, cxx11_allocator<T>&) {}
|
||||
}
|
||||
template <class T>
|
||||
void swap(cxx11_allocator<T>&, cxx11_allocator<T>&)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
|
||||
namespace boost {
|
||||
#else
|
||||
namespace test {
|
||||
namespace minimal {
|
||||
namespace minimal {
|
||||
#endif
|
||||
std::size_t hash_value(test::minimal::copy_constructible_equality_comparable)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
std::size_t hash_value(
|
||||
test::minimal::copy_constructible_equality_comparable)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
|
||||
}
|
||||
}
|
||||
}}
|
||||
#else
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
+567
-626
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user