Compare commits

...

36 Commits

Author SHA1 Message Date
Peter Dimov
f2b2ef4ebe Avoid warning 4100 under early MSVC 2022-11-28 01:59:32 +02:00
Peter Dimov
fe28cdbd1f Add boost::hash_value for std::nullptr_t. Refs #29. 2022-11-27 20:13:52 +02:00
Peter Dimov
f98b943585 Add benchmark/.gitignore 2022-11-27 19:39:38 +02:00
Peter Dimov
4aeb7b2285 Update benchmark/unordered_flat.cpp 2022-11-27 06:47:54 +02:00
Peter Dimov
e310932a9c Add mulxp3_hash32 to benchmarks 2022-11-27 02:43:20 +02:00
Peter Dimov
558d0ead17 Suppress std::complex<int> warning under clang-cl 2022-11-25 21:13:57 +02:00
Peter Dimov
d6905ab159 Add benchmark/word_count.cpp 2022-11-25 20:52:56 +02:00
Peter Dimov
251894540d Split mul31_unrolled_hash into mul31_x4_hash and mul31_x8_hash in benchmarks 2022-11-25 20:52:30 +02:00
Peter Dimov
ffadafa0c1 Remove old_boost_hash from benchmarks 2022-11-25 20:27:20 +02:00
Peter Dimov
ce166d1030 Add mulxp_hash to benchmark/unordered.cpp 2022-11-25 20:24:59 +02:00
Peter Dimov
8ab83c1cd4 Add mulxp_hash to benchmark/unordered_flat.cpp 2022-11-25 20:24:38 +02:00
Peter Dimov
16546190f6 Add benchmark/unordered_flat.cpp 2022-11-19 03:19:34 +02:00
Peter Dimov
5e26a3b807 Add absl::Hash to benchmark/unordered.cpp 2022-11-18 17:37:09 +02:00
Peter Dimov
bd5b7a359c Update ci.yml 2022-11-03 14:19:56 +02:00
Peter Dimov
f5f5476dcc <boost/unordered/hash_traits.hpp> no longer requires C++11 2022-11-01 02:35:51 +02:00
Peter Dimov
228f7db5d3 Add examples/point2.cpp 2022-10-30 17:51:31 +02:00
Peter Dimov
f50b914456 Update documentation 2022-10-30 17:23:39 +02:00
Peter Dimov
c134ca39e9 Update test/CMakeLists.txt 2022-10-30 03:05:20 +02:00
Peter Dimov
f51f68fe93 Do not use the u8string and u8string_view typedefs, because char8_t availability does not guarantee their presence 2022-10-30 03:01:05 +02:00
Peter Dimov
0865c1d230 Disable hash_is_avalanching tests when BOOST_NO_CXX11_TEMPLATE_ALIASES is defined, because hash_traits.hpp uses void_t 2022-10-30 03:54:24 +03:00
Peter Dimov
0d2266decb Specialize boost::unordered::hash_is_avalanching for hash<string> and hash<string_view> 2022-10-30 02:47:10 +03:00
Peter Dimov
3c9ba89cf2 Update .drone.jsonnet 2022-10-28 21:21:44 +03:00
Peter Dimov
20ee3b60fd Update CMakeLists.txt 2022-10-28 21:18:10 +03:00
Peter Dimov
bdd3dcf944 Disable -Wunused-private-field in described_class_test.cpp 2022-10-28 20:04:13 +03:00
Peter Dimov
018fc4e1fa Add support for described classes to hash.hpp 2022-10-28 19:35:10 +03:00
Peter Dimov
7568176bfa is_described_class should be false for unions 2022-10-28 18:42:07 +03:00
Peter Dimov
08864cd1e9 Update cmake_subdir_test/CMakeLists.txt 2022-10-28 14:56:57 +03:00
Peter Dimov
5a616b9b54 Update CMakeLists.txt 2022-10-28 07:07:48 +03:00
Peter Dimov
16fb4cc749 Add is_described_class to hash_fwd.hpp; include it in hash.hpp 2022-10-28 03:32:18 +03:00
Peter Dimov
0171246a61 Add is_described_class<T> 2022-10-28 01:47:27 +03:00
Peter Dimov
29c85559e4 Update .drone.jsonnet 2022-10-27 02:44:38 +03:00
Peter Dimov
412bd51449 Avoid -Wunused-but-set-variable under Clang 15 2022-10-27 02:12:43 +03:00
Peter Dimov
87196a503e Update .drone.jsonnet 2022-10-27 01:16:34 +03:00
Peter Dimov
df1671d593 msvc-14.0 doesn't quite have expression SFINAE. Fixes #28. 2022-10-26 19:38:53 +03:00
Peter Dimov
1996cf36c1 Update ci.yml 2022-10-26 19:32:22 +03:00
Peter Dimov
a4b2048a29 Add is_contiguous_range_test3 (refs #28) 2022-10-26 19:13:27 +03:00
34 changed files with 2028 additions and 63 deletions

View File

@@ -135,47 +135,205 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 20.04 GCC 9 ARM64",
"Linux 16.04 GCC 5* 32/64",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 6 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '03,11,14', ADDRMD: '32,64' },
"g++-6-multilib",
),
linux_pipeline(
"Linux 18.04 GCC 7* 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 8 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
"g++-8-multilib",
),
linux_pipeline(
"Linux 20.04 GCC 9* 32/64",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 20.04 GCC 9* ARM64",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
arch="arm64",
),
linux_pipeline(
"Linux 20.04 GCC 9 S390x",
"Linux 20.04 GCC 9* S390x",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
arch="s390x",
),
linux_pipeline(
"Linux 20.04 GCC 10 32/64",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '03,11,14,17,20', ADDRMD: '32,64' },
"g++-10-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 11* 32/64",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 22.04 GCC 12 32 ASAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20', ADDRMD: '32' } + asan,
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20,2b', ADDRMD: '32' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 12 64 ASAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20', ADDRMD: '64' } + asan,
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20,2b', ADDRMD: '64' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 16.04 Clang 3.5",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '03,11' },
"clang-3.5",
),
linux_pipeline(
"Linux 16.04 Clang 3.6",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '03,11,14' },
"clang-3.6",
),
linux_pipeline(
"Linux 16.04 Clang 3.7",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '03,11,14' },
"clang-3.7",
),
linux_pipeline(
"Linux 16.04 Clang 3.8",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '03,11,14' },
"clang-3.8",
),
linux_pipeline(
"Linux 18.04 Clang 3.9",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '03,11,14' },
"clang-3.9",
),
linux_pipeline(
"Linux 18.04 Clang 4.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '03,11,14' },
"clang-4.0",
),
linux_pipeline(
"Linux 18.04 Clang 5.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '03,11,14,1z' },
"clang-5.0",
),
linux_pipeline(
"Linux 18.04 Clang 6.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '03,11,14,17' },
"clang-6.0",
),
linux_pipeline(
"Linux 20.04 Clang 7",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '03,11,14,17' },
"clang-7",
),
linux_pipeline(
"Linux 20.04 Clang 8",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '03,11,14,17' },
"clang-8",
),
linux_pipeline(
"Linux 20.04 Clang 9",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '03,11,14,17,2a' },
"clang-9",
),
linux_pipeline(
"Linux 20.04 Clang 10",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '03,11,14,17,2a' },
"clang-10",
),
linux_pipeline(
"Linux 20.04 Clang 11",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '03,11,14,17,2a' },
"clang-11",
),
linux_pipeline(
"Linux 20.04 Clang 12",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '03,11,14,17,2a' },
"clang-12",
),
linux_pipeline(
"Linux 22.04 Clang 13",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '03,11,14,17,20' },
"clang-13",
),
linux_pipeline(
"Linux 22.04 Clang 14 UBSAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + ubsan,
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20,2b' } + ubsan,
"clang-14",
),
linux_pipeline(
"Linux 22.04 Clang 14 ASAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + asan,
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20,2b' } + asan,
"clang-14",
),
linux_pipeline(
"Linux 22.04 Clang 15",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '03,11,14,17,20,2b' },
"clang-15",
["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"],
),
macos_pipeline(
"MacOS 10.15 Xcode 12.2 UBSAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + ubsan,
@@ -186,9 +344,27 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
),
windows_pipeline(
"Windows VS2015 msvc-14.0",
"cppalliance/dronevs2015",
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' },
),
windows_pipeline(
"Windows VS2017 msvc-14.1",
"cppalliance/dronevs2017",
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
),
windows_pipeline(
"Windows VS2019 msvc-14.2",
"cppalliance/dronevs2019",
{ TOOLSET: 'msvc-14.2', CXXSTD: '14,17,20,latest' },
),
windows_pipeline(
"Windows VS2022 msvc-14.3",
"cppalliance/dronevs2022:1",
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' },
),
]

View File

@@ -58,7 +58,7 @@ jobs:
install: g++-11-multilib
address-model: 32,64
- toolset: gcc-12
cxxstd: "03,11,14,17,20"
cxxstd: "03,11,14,17,20,2b"
os: ubuntu-22.04
install: g++-12-multilib
address-model: 32,64
@@ -111,12 +111,12 @@ jobs:
os: ubuntu-20.04
- toolset: clang
compiler: clang++-13
cxxstd: "03,11,14,17,20"
cxxstd: "03,11,14,17,20,2b"
os: ubuntu-22.04
install: clang-13
- toolset: clang
compiler: clang++-14
cxxstd: "03,11,14,17,20"
cxxstd: "03,11,14,17,20,2b"
os: ubuntu-22.04
install: clang-14
- toolset: clang
@@ -126,7 +126,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install packages
if: matrix.install
@@ -195,7 +195,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Boost
shell: cmd
@@ -239,7 +239,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install packages
if: matrix.install
@@ -286,7 +286,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install packages
if: matrix.install
@@ -343,7 +343,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install packages
if: matrix.install

View File

@@ -15,6 +15,8 @@ target_include_directories(boost_container_hash INTERFACE include)
target_link_libraries(boost_container_hash
INTERFACE
Boost::config
Boost::describe
Boost::mp11
Boost::type_traits
)

3
benchmark/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
enwik8
enwik9
*.exe

View File

@@ -3,12 +3,19 @@
// https://www.boost.org/LICENSE_1_0.txt
#define _CRT_SECURE_NO_WARNINGS
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/container_hash/hash.hpp>
#include <boost/unordered_set.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/core/type_name.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/hash/hash.h"
#endif
#ifdef HAVE_MULXP_HASH
# include "mulxp_hash.hpp"
#endif
#include <cstddef>
#include <cstdio>
#include <cstdint>
@@ -19,10 +26,8 @@
// mul31_hash
class mul31_hash
struct mul31_hash
{
public:
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
@@ -43,18 +48,20 @@ public:
}
};
// mul31_unrolled_hash
// mul31_x4_hash
template<int Bits> struct mul31_unrolled_hash_impl;
template<> struct mul31_unrolled_hash_impl<32>
struct mul31_x4_hash
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
std::size_t h = 0xCBF29CE484222325ull;
#else
std::size_t h = 0x811C9DC5u;
#endif
while( n >= 4 )
{
@@ -80,14 +87,20 @@ template<> struct mul31_unrolled_hash_impl<32>
}
};
template<> struct mul31_unrolled_hash_impl<64>
// mul31_x8_hash
struct mul31_x8_hash
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
std::size_t h = 0xCBF29CE484222325ull;
#if SIZE_MAX > UINT32_MAX
boost::uint64_t h = 0xCBF29CE484222325ull;
#else
boost::uint64_t h = 0x811C9DC5u;
#endif
while( n >= 8 )
{
@@ -113,12 +126,10 @@ template<> struct mul31_unrolled_hash_impl<64>
--n;
}
return h;
return static_cast<std::size_t>( h );
}
};
struct mul31_unrolled_hash: mul31_unrolled_hash_impl< std::numeric_limits<std::size_t>::digits > {};
// fnv1a_hash
template<int Bits> struct fnv1a_hash_impl;
@@ -163,28 +174,52 @@ template<> struct fnv1a_hash_impl<64>
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
// old_boost_hash
// mulxp_hash
class old_boost_hash
#ifdef HAVE_MULXP_HASH
struct mulxp0_hash_
{
public:
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
std::size_t h = 0;
for( std::size_t i = 0; i < n; ++i )
{
h ^= static_cast<unsigned char>( p[i] ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
}
return h;
return mulxp0_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp1_hash_
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp2_hash_
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp2_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash_
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash32_
{
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
}
};
#endif
// test_hash_speed
template<class H, class V> void test_hash_speed( int N, V const& v )
@@ -210,11 +245,11 @@ template<class H, class V> void test_hash_speed( int N, V const& v )
#if defined( _MSC_VER )
std::printf( "%25s : q=%20Iu, %lld ms\n", hash.c_str(), q, ms1 );
std::printf( "%53s : q=%20Iu, %lld ms\n", hash.c_str(), q, ms1 );
#else
std::printf( "%25s : q=%20zu, %lld ms\n", hash.c_str(), q, ms1 );
std::printf( "%53s : q=%20zu, %lld ms\n", hash.c_str(), q, ms1 );
#endif
}
@@ -235,11 +270,11 @@ template<class H, class V> void test_hash_collision( int N, V const& v, std::siz
#if defined( _MSC_VER )
std::printf( "%25s : c=%Iu\n", hash.c_str(), n - s.size() );
std::printf( "%53s : c=%Iu\n", hash.c_str(), n - s.size() );
#else
std::printf( "%25s : c=%zu\n", hash.c_str(), n - s.size() );
std::printf( "%53s : c=%zu\n", hash.c_str(), n - s.size() );
#endif
}
@@ -292,11 +327,11 @@ template<class V, class S> void test4( int N, V const& v, char const * hash, S s
#if defined( _MSC_VER )
std::printf( "%25s : n=%Iu, m=%Iu, c=%Iu, q=%Iu, %lld + %lld ms\n", hash, n, m, c, q, ms1, ms2 );
std::printf( "%53s : n=%Iu, m=%Iu, c=%Iu, q=%Iu, %4lld + %4lld = %4lld ms\n", hash, n, m, c, q, ms1, ms2, ms1 + ms2 );
#else
std::printf( "%25s : n=%zu, m=%zu, c=%zu, q=%zu, %lld + %lld ms\n", hash, n, m, c, q, ms1, ms2 );
std::printf( "%53s : n=%zu, m=%zu, c=%zu, q=%zu, %4lld + %4lld = %4lld ms\n", hash, n, m, c, q, ms1, ms2, ms1 + ms2 );
#endif
}
@@ -340,11 +375,21 @@ int main()
std::puts( "Hash speed test:\n" );
test_hash_speed<mul31_hash>( N * 16, v );
test_hash_speed<mul31_unrolled_hash>( N * 16, v );
test_hash_speed<mul31_x4_hash>( N * 16, v );
test_hash_speed<mul31_x8_hash>( N * 16, v );
test_hash_speed<fnv1a_hash>( N * 16, v );
test_hash_speed<old_boost_hash>( N * 16, v );
test_hash_speed<boost::hash<std::string> >( N * 16, v );
test_hash_speed<std::hash<std::string> >( N * 16, v );
#ifdef HAVE_ABSEIL
test_hash_speed<absl::Hash<std::string> >( N * 16, v );
#endif
#ifdef HAVE_MULXP_HASH
test_hash_speed<mulxp0_hash_>( N * 16, v );
test_hash_speed<mulxp1_hash_>( N * 16, v );
test_hash_speed<mulxp2_hash_>( N * 16, v );
test_hash_speed<mulxp3_hash_>( N * 16, v );
test_hash_speed<mulxp3_hash32_>( N * 16, v );
#endif
std::puts( "" );
@@ -365,11 +410,21 @@ int main()
}
test_hash_collision<mul31_hash>( N * 16, v, n );
test_hash_collision<mul31_unrolled_hash>( N * 16, v, n );
test_hash_collision<mul31_x4_hash>( N * 16, v, n );
test_hash_collision<mul31_x8_hash>( N * 16, v, n );
test_hash_collision<fnv1a_hash>( N * 16, v, n );
test_hash_collision<old_boost_hash>( N * 16, v, n );
test_hash_collision<boost::hash<std::string> >( N * 16, v, n );
test_hash_collision<std::hash<std::string> >( N * 16, v, n );
#ifdef HAVE_ABSEIL
test_hash_collision<absl::Hash<std::string> >( N * 16, v, n );
#endif
#ifdef HAVE_MULXP_HASH
test_hash_collision<mulxp0_hash_>( N * 16, v, n );
test_hash_collision<mulxp1_hash_>( N * 16, v, n );
test_hash_collision<mulxp2_hash_>( N * 16, v, n );
test_hash_collision<mulxp3_hash_>( N * 16, v, n );
test_hash_collision<mulxp3_hash32_>( N * 16, v, n );
#endif
}
std::puts( "" );
@@ -379,11 +434,27 @@ int main()
std::puts( "Container speed test:\n" );
test_container_speed<K, mul31_hash>( N, v );
test_container_speed<K, mul31_unrolled_hash>( N, v );
test_container_speed<K, mul31_x4_hash>( N, v );
test_container_speed<K, mul31_x8_hash>( N, v );
test_container_speed<K, fnv1a_hash>( N, v );
test_container_speed<K, old_boost_hash>( N, v );
test_container_speed<K, boost::hash<std::string> >( N, v );
test_container_speed<K, std::hash<std::string> >( N, v );
#ifdef HAVE_ABSEIL
test_container_speed<K, absl::Hash<std::string> >( N, v );
#endif
#ifdef HAVE_MULXP_HASH
test_container_speed<K, mulxp0_hash_>( N, v );
test_container_speed<K, mulxp1_hash_>( N, v );
test_container_speed<K, mulxp2_hash_>( N, v );
test_container_speed<K, mulxp3_hash_>( N, v );
test_container_speed<K, mulxp3_hash32_>( N, v );
#endif
std::puts( "" );
}
#ifdef HAVE_ABSEIL
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif

View File

@@ -0,0 +1,495 @@
// 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
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/hash/hash.h"
#endif
#ifdef HAVE_MULXP_HASH
# include "mulxp_hash.hpp"
#endif
#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 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = 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() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
std::cout << std::endl;
}
//
struct record
{
std::string label_;
long long time_;
};
static std::vector<record> times;
template<class Hash> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
boost::unordered_flat_map<std::string, std::uint32_t, Hash> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
record rec = { label, 0 };
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 );
}
// mul31_hash
struct mul31_hash
{
// not avalanching
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
std::size_t h = 0xCBF29CE484222325ull;
#else
std::size_t h = 0x811C9DC5u;
#endif
for( std::size_t i = 0; i < n; ++i )
{
h = h * 31 + static_cast<unsigned char>( p[i] );
}
return h;
}
};
// mul31_x4_hash
struct mul31_x4_hash
{
// not avalanching
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
std::size_t h = 0xCBF29CE484222325ull;
#else
std::size_t h = 0x811C9DC5u;
#endif
while( n >= 4 )
{
h = h * (31u * 31u * 31u * 31u)
+ static_cast<unsigned char>( p[0] ) * (31u * 31u * 31u)
+ static_cast<unsigned char>( p[1] ) * (31u * 31u)
+ static_cast<unsigned char>( p[2] ) * 31u
+ static_cast<unsigned char>( p[3] );
p += 4;
n -= 4;
}
while( n > 0 )
{
h = h * 31u + static_cast<unsigned char>( *p );
++p;
--n;
}
return h;
}
};
// mul31_x8_hash
struct mul31_x8_hash
{
// not avalanching
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
boost::uint64_t h = 0xCBF29CE484222325ull;
#else
boost::uint64_t h = 0x811C9DC5u;
#endif
while( n >= 8 )
{
h = h * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[0] ) * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[1] ) * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[2] ) * (31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[3] ) * (31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[4] ) * (31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[5] ) * (31ull * 31ull)
+ static_cast<unsigned char>( p[6] ) * 31ull
+ static_cast<unsigned char>( p[7] );
p += 8;
n -= 8;
}
while( n > 0 )
{
h = h * 31u + static_cast<unsigned char>( *p );
++p;
--n;
}
return static_cast<std::size_t>( h );
}
};
// 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 >
{
using is_avalanching = void;
};
// std_hash
struct std_hash: std::hash<std::string>
{
using is_avalanching = void;
};
// absl_hash
#ifdef HAVE_ABSEIL
struct absl_hash: absl::Hash<std::string>
{
using is_avalanching = void;
};
#endif
// mulxp_hash
#ifdef HAVE_MULXP_HASH
struct mulxp0_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp0_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp1_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp2_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp2_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash32_
{
using is_avalanching = void;
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
{
std::size_t r = mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
#if SIZE_MAX > UINT32_MAX
r |= r << 32;
#endif
return r;
}
};
#endif
//
int main()
{
init_indices();
test< boost::hash<std::string> >( "boost::hash" );
test< std_hash >( "std::hash" );
test< mul31_hash >( "mul31_hash" );
test< mul31_x4_hash >( "mul31_x4_hash" );
test< mul31_x8_hash >( "mul31_x8_hash" );
test< fnv1a_hash >( "fnv1a_hash" );
#ifdef HAVE_ABSEIL
test< absl_hash >( "absl::Hash" );
#endif
#ifdef HAVE_MULXP_HASH
test< mulxp0_hash_ >( "mulxp0_hash" );
test< mulxp1_hash_ >( "mulxp1_hash" );
test< mulxp2_hash_ >( "mulxp2_hash" );
test< mulxp3_hash_ >( "mulxp3_hash" );
test< mulxp3_hash32_ >( "mulxp3_hash32" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 22 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif

420
benchmark/word_count.cpp Normal file
View File

@@ -0,0 +1,420 @@
// Copyright 2022 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
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered/unordered_flat_map.hpp>
#ifdef HAVE_ABSEIL
# include "absl/hash/hash.h"
#endif
#ifdef HAVE_MULXP_HASH
# include "mulxp_hash.hpp"
#endif
#include <boost/regex.hpp>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <fstream>
#include <string_view>
#include <string>
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;
}
static std::vector<std::string> words;
static void init_words()
{
char const* fn = "enwik9"; // http://mattmahoney.net/dc/textdata
auto t1 = std::chrono::steady_clock::now();
std::ifstream is( fn );
std::string in( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} );
boost::regex re( "[a-zA-Z]+");
boost::sregex_token_iterator it( in.begin(), in.end(), re, 0 ), end;
words.assign( it, end );
auto t2 = std::chrono::steady_clock::now();
std::cout << fn << ": " << words.size() << " words, " << ( t2 - t1 ) / 1ms << " ms\n\n";
}
template<class Map> BOOST_NOINLINE void test_word_count( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
++map[ word ];
++s;
}
print_time( t1, "Word count", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_contains( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
std::string_view w2( word );
w2.remove_prefix( 1 );
s += map.contains( w2 );
}
print_time( t1, "Contains", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_count( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
std::string_view w2( word );
w2.remove_prefix( 1 );
s += map.count( w2 );
}
print_time( t1, "Count", s, map.size() );
std::cout << std::endl;
}
//
struct record
{
std::string label_;
long long time_;
};
static std::vector<record> times;
template<class Hash> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
boost::unordered_flat_map<std::string_view, std::size_t, Hash> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_word_count( map, t1 );
record rec = { label, 0 };
test_contains( map, t1 );
test_count( 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 );
}
// mul31_hash
struct mul31_hash
{
// not avalanching
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
std::size_t h = 0xCBF29CE484222325ull;
#else
std::size_t h = 0x811C9DC5u;
#endif
for( std::size_t i = 0; i < n; ++i )
{
h = h * 31 + static_cast<unsigned char>( p[i] );
}
return h;
}
};
// mul31_x4_hash
struct mul31_x4_hash
{
// not avalanching
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
std::size_t h = 0xCBF29CE484222325ull;
#else
std::size_t h = 0x811C9DC5u;
#endif
while( n >= 4 )
{
h = h * (31u * 31u * 31u * 31u)
+ static_cast<unsigned char>( p[0] ) * (31u * 31u * 31u)
+ static_cast<unsigned char>( p[1] ) * (31u * 31u)
+ static_cast<unsigned char>( p[2] ) * 31u
+ static_cast<unsigned char>( p[3] );
p += 4;
n -= 4;
}
while( n > 0 )
{
h = h * 31u + static_cast<unsigned char>( *p );
++p;
--n;
}
return h;
}
};
// mul31_x8_hash
struct mul31_x8_hash
{
// not avalanching
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
char const * p = st.data();
std::size_t n = st.size();
#if SIZE_MAX > UINT32_MAX
boost::uint64_t h = 0xCBF29CE484222325ull;
#else
boost::uint64_t h = 0x811C9DC5u;
#endif
while( n >= 8 )
{
h = h * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[0] ) * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[1] ) * (31ull * 31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[2] ) * (31ull * 31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[3] ) * (31ull * 31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[4] ) * (31ull * 31ull * 31ull)
+ static_cast<unsigned char>( p[5] ) * (31ull * 31ull)
+ static_cast<unsigned char>( p[6] ) * 31ull
+ static_cast<unsigned char>( p[7] );
p += 8;
n -= 8;
}
while( n > 0 )
{
h = h * 31u + static_cast<unsigned char>( *p );
++p;
--n;
}
return static_cast<std::size_t>( h );
}
};
// 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 >
{
using is_avalanching = void;
};
// std_hash
struct std_hash: std::hash<std::string_view>
{
using is_avalanching = void;
};
// absl_hash
#ifdef HAVE_ABSEIL
struct absl_hash: absl::Hash<std::string_view>
{
using is_avalanching = void;
};
#endif
#ifdef HAVE_MULXP_HASH
struct mulxp0_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
return mulxp0_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp1_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp2_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
return mulxp2_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash_
{
using is_avalanching = void;
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
}
};
struct mulxp3_hash32_
{
using is_avalanching = void;
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
{
std::size_t r = mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
#if SIZE_MAX > UINT32_MAX
r |= r << 32;
#endif
return r;
}
};
#endif
//
int main()
{
init_words();
test< boost::hash<std::string_view> >( "boost::hash" );
test< std_hash >( "std::hash" );
test< mul31_hash >( "mul31_hash" );
test< mul31_x4_hash >( "mul31_x4_hash" );
test< mul31_x8_hash >( "mul31_x8_hash" );
test< fnv1a_hash >( "fnv1a_hash" );
#ifdef HAVE_ABSEIL
test< absl_hash >( "absl::Hash" );
#endif
#ifdef HAVE_MULXP_HASH
test< mulxp0_hash_ >( "mulxp0_hash" );
test< mulxp1_hash_ >( "mulxp1_hash" );
test< mulxp2_hash_ >( "mulxp2_hash" );
test< mulxp3_hash_ >( "mulxp3_hash" );
test< mulxp3_hash32_ >( "mulxp3_hash32" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 22 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif

View File

@@ -22,6 +22,7 @@ include::hash/recent.adoc[]
include::hash/tutorial.adoc[]
include::hash/user.adoc[]
include::hash/combine.adoc[]
include::hash/describe.adoc[]
include::hash/reference.adoc[]
include::hash/notes.adoc[]
include::hash/links.adoc[]

61
doc/hash/describe.adoc Normal file
View File

@@ -0,0 +1,61 @@
////
Copyright 2022 Peter Dimov
Distributed under the Boost Software License, Version 1.0.
https://www.boost.org/LICENSE_1_0.txt
////
[#describe]
= Hashing User Types with Boost.Describe
:idprefix: describe_
Let's look at our `point` class again:
[source]
----
class point
{
int x;
int y;
public:
point() : x(0), y(0) {}
point(int x, int y) : x(x), y(y) {}
};
----
If you're using {cpp}14 or above, a much easier way to add
support for `boost::hash` to `point` is by using
link:../../../describe/index.html[Boost.Describe] (and
get an automatic definition of `operator==` for free):
[source]
----
#include <boost/describe/class.hpp>
#include <boost/describe/operators.hpp>
class point
{
int x;
int y;
BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y))
public:
point() : x(0), y(0) {}
point(int x, int y) : x(x), y(y) {}
};
using boost::describe::operators::operator==;
using boost::describe::operators::operator!=;
----
(Full code for this example is at
link:../../examples/point2.cpp[examples/point2.cpp].)
Since the `point` class has been annotated with `BOOST_DESCRIBE_CLASS`,
the library can enumerate its members (and base classes) and automatically
synthesize the appropriate `hash_value` overload for it, without us needing
to do so.

View File

@@ -32,6 +32,9 @@ Out of the box, `boost::hash` supports
* unordered sequences, standard or user-defined (sequences for which the hash
value does not depend on the element order, such as `std::unordered_set` and
`std::unordered_map`);
* described structs and classes -- ones that have been annotated with the
`BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS` macros from
link:../../../describe/index.html[Boost.Describe];
* `std::unique_ptr`, `std::shared_ptr`;
* `std::type_index`;
* `std::error_code`, `std::error_condition`;

View File

@@ -21,6 +21,9 @@ Major update.
* User-defined containers (types that have `begin()` and `end()`
member functions that return iterators) are now supported out
of the box.
* Described structs and classes (those annotated with
`BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS`) are now
supported out of the box.
* `hash_combine` has been improved.
* The performance (and quality, as a result of the above change)
of string hashing has been improved. `boost::hash` for strings

View File

@@ -29,6 +29,7 @@ namespace container_hash
template<class T> struct is_range;
template<class T> struct is_contiguous_range;
template<class T> struct is_unordered_range;
template<class T> struct is_described_class;
} // namespace container_hash
@@ -103,6 +104,10 @@ template<class T>
template<class T>
std::size_t hash_value( T const& v );
// Enabled only when container_hash::is_described_class<T>::value is true
template<class T>
std::size_t hash_value( T const& v );
template<class T>
std::size_t hash_value( std::shared_ptr<T> const& v );
@@ -446,6 +451,34 @@ Remarks: ::
This overload handles the standard unordered containers, such as
`std::unordered_set` and `std::unordered_map`.
[source]
----
// Enabled only when container_hash::is_described_class<T>::value is true
template<class T>
std::size_t hash_value( T const& v );
----
Effects: ::
+
[source]
----
std::size_t seed = 0;
boost::hash_combine( seed, b1 );
boost::hash_combine( seed, b2 );
// ...
boost::hash_combine( seed, bM );
boost::hash_combine( seed, m1 );
boost::hash_combine( seed, m2 );
// ...
boost::hash_combine( seed, mN );
return seed;
----
+
where `bi` are the bases of `v` and `mi` are its members.
[source]
----
template<class T>
@@ -633,3 +666,40 @@ template<class T> struct is_unordered_range
Users are allowed to specialize `is_unordered_range` for their types
if the default behavior does not deduce the correct value.
== <boost/container_hash/{zwsp}is_described_class.hpp>
Defines the trait `boost::container_hash::is_described_class`.
[source]
----
namespace boost
{
namespace container_hash
{
template<class T> struct is_described_class;
} // namespace container_hash
} // namespace boost
----
=== is_described_class<T>
[source]
----
template<class T> struct is_described_class
{
static constexpr bool value = /* see below */;
};
----
`is_described_class<T>::value` is `true` when
`boost::describe::has_describe_bases<T>::value` is `true`,
`boost::describe::has_describe_members<T>::value` is `true`, and
`T` is not a union.
Users are allowed to specialize `is_described_class` for their types
if the default behavior does not deduce the correct value.

View File

@@ -74,7 +74,7 @@ assert(books.find(knife) != books.end());
assert(books.find(dandelion) == books.end());
----
The full example can be found in:
The full example can be found in
link:../../examples/books.hpp[examples/books.hpp] and
link:../../examples/books.cpp[examples/books.cpp].

View File

@@ -7,3 +7,4 @@ run books.cpp ;
run point.cpp ;
run portable.cpp ;
run template.cpp : : : <toolset>msvc-8.0:<build>no ;
run point2.cpp ;

65
examples/point2.cpp Normal file
View File

@@ -0,0 +1,65 @@
// Copyright 2005 Daniel James.
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// Force use of assert.
#if defined(NDEBUG)
#undef NDEBUG
#endif
#include <boost/container_hash/hash.hpp>
#include <boost/describe/class.hpp>
#include <boost/describe/operators.hpp>
#include <cassert>
// This example illustrates how to use Boost.Describe to obtain
// automatic boost::hash support. For full details see the hash
// tutorial.
#if defined(BOOST_DESCRIBE_CXX14)
class point
{
int x;
int y;
BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y))
public:
point() : x(0), y(0) {}
point(int x, int y) : x(x), y(y) {}
};
using boost::describe::operators::operator==;
using boost::describe::operators::operator!=;
int main()
{
boost::hash<point> point_hasher;
point p1(0, 0);
point p2(1, 2);
point p3(4, 1);
point p4 = p1;
assert(point_hasher(p1) == point_hasher(p4));
// These tests could legally fail, but if they did it'd be a pretty bad
// hash function.
assert(point_hasher(p1) != point_hasher(p2));
assert(point_hasher(p1) != point_hasher(p3));
}
#else
#include <cstdio>
int main()
{
std::puts( "This example requires C++14." );
}
#endif

View File

@@ -14,6 +14,7 @@
#include <boost/container_hash/is_range.hpp>
#include <boost/container_hash/is_contiguous_range.hpp>
#include <boost/container_hash/is_unordered_range.hpp>
#include <boost/container_hash/is_described_class.hpp>
#include <boost/container_hash/detail/hash_tuple.hpp>
#include <boost/container_hash/detail/hash_mix.hpp>
#include <boost/container_hash/detail/hash_range.hpp>
@@ -25,7 +26,16 @@
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/type_traits/enable_if.hpp>
#include <boost/type_traits/conjunction.hpp>
#include <boost/type_traits/is_union.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/describe/bases.hpp>
#include <boost/describe/members.hpp>
#include <boost/cstdint.hpp>
#if defined(BOOST_DESCRIBE_CXX14)
# include <boost/mp11/algorithm.hpp>
#endif
#include <string>
#include <iterator>
#include <complex>
@@ -54,6 +64,10 @@
#include <variant>
#endif
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
# include <string_view>
#endif
namespace boost
{
@@ -383,6 +397,49 @@ namespace boost
return boost::hash_unordered_range( v.begin(), v.end() );
}
#endif
// described classes
#if defined(BOOST_DESCRIBE_CXX14)
#if defined(_MSC_VER) && _MSC_VER == 1900
# pragma warning(push)
# pragma warning(disable: 4100) // unreferenced formal parameter
#endif
template <typename T>
typename boost::enable_if_<container_hash::is_described_class<T>::value, std::size_t>::type
hash_value( T const& v )
{
static_assert( !boost::is_union<T>::value, "described unions are not supported" );
std::size_t r = 0;
using Bd = describe::describe_bases<T, describe::mod_any_access>;
mp11::mp_for_each<Bd>([&](auto D){
using B = typename decltype(D)::type;
boost::hash_combine( r, (B const&)v );
});
using Md = describe::describe_members<T, describe::mod_any_access>;
mp11::mp_for_each<Md>([&](auto D){
boost::hash_combine( r, v.*D.pointer );
});
return r;
}
#if defined(_MSC_VER) && _MSC_VER == 1900
# pragma warning(pop)
#endif
#endif
// std::unique_ptr, std::shared_ptr
@@ -438,6 +495,19 @@ namespace boost
return seed;
}
#endif
// std::nullptr_t
#if !defined(BOOST_NO_CXX11_NULLPTR)
template <typename T>
typename boost::enable_if_<boost::is_same<T, std::nullptr_t>::value, std::size_t>::type
hash_value( T const& /*v*/ )
{
return boost::hash_value( static_cast<void*>( nullptr ) );
}
#endif
// std::optional
@@ -577,6 +647,30 @@ namespace boost
};
#endif
}
// boost::unordered::hash_is_avalanching
namespace unordered
{
template<class T> struct hash_is_avalanching;
template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string<Ch> > >: boost::is_integral<Ch> {};
// boost::is_integral<char8_t> is false, but should be true (https://github.com/boostorg/type_traits/issues/175)
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
template<> struct hash_is_avalanching< boost::hash< std::basic_string<char8_t> > >: boost::true_type {};
#endif
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string_view<Ch> > >: boost::is_integral<Ch> {};
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
template<> struct hash_is_avalanching< boost::hash< std::basic_string_view<char8_t> > >: boost::true_type {};
#endif
#endif
} // namespace unordered
} // namespace boost
#endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP

View File

@@ -1,5 +1,5 @@
// Copyright 2005-2009 Daniel James.
// Copyright 2021 Peter Dimov.
// Copyright 2021, 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -17,6 +17,7 @@ namespace container_hash
template<class T> struct is_range;
template<class T> struct is_contiguous_range;
template<class T> struct is_unordered_range;
template<class T> struct is_described_class;
} // namespace container_hash

View File

@@ -10,7 +10,7 @@
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700) && !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/declval.hpp>

View File

@@ -0,0 +1,38 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED
#define BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_union.hpp>
#include <boost/describe/bases.hpp>
#include <boost/describe/members.hpp>
namespace boost
{
namespace container_hash
{
#if defined(BOOST_DESCRIBE_CXX11)
template<class T> struct is_described_class: boost::integral_constant<bool,
describe::has_describe_bases<T>::value &&
describe::has_describe_members<T>::value &&
!boost::is_union<T>::value>
{
};
#else
template<class T> struct is_described_class: boost::false_type
{
};
#endif
} // namespace container_hash
} // namespace boost
#endif // #ifndef BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED

View File

@@ -1,4 +1,4 @@
# Copyright 2018, 2019, 2021 Peter Dimov
# Copyright 2018, 2019, 2021, 2022 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +6,7 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
if(HAVE_BOOST_TEST)
boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility)
boost_test_jamfile(FILE Jamfile.v2
LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility Boost::unordered)
endif()

View File

@@ -101,3 +101,19 @@ run hash_unordered_map_test.cpp ;
run is_range_test3.cpp ;
run is_contiguous_range_test2.cpp ;
run is_unordered_range_test2.cpp ;
run is_contiguous_range_test3.cpp ;
run is_described_class_test.cpp
: : : <warnings>extra ;
run is_described_class_test2.cpp
: : : <warnings>extra ;
run is_described_class_test3.cpp
: : : <warnings>extra ;
run described_class_test.cpp
: : : <warnings>extra ;
run hash_is_avalanching_test.cpp ;
run hash_is_avalanching_test2.cpp ;
run hash_nullptr_test.cpp ;

View File

@@ -11,6 +11,8 @@ add_subdirectory(../.. boostorg/container_hash)
add_subdirectory(../../../config boostorg/config)
add_subdirectory(../../../type_traits boostorg/type_traits)
add_subdirectory(../../../static_assert boostorg/static_assert)
add_subdirectory(../../../describe boostorg/describe)
add_subdirectory(../../../mp11 boostorg/mp11)
add_executable(quick ../quick.cpp)
target_link_libraries(quick Boost::container_hash)

View File

@@ -0,0 +1,159 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wunused-private-field"
#endif
#include <boost/container_hash/hash.hpp>
#include <boost/describe/class.hpp>
#include <boost/describe/operators.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#if !defined(BOOST_DESCRIBE_CXX14)
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE( "Skipping test because BOOST_DESCRIBE_CXX14 is not defined" )
int main() {}
#else
struct X1
{
int m;
explicit X1( int m_ ): m( m_ ) {}
};
BOOST_DESCRIBE_STRUCT( X1, (), (m) )
struct X2
{
int m;
explicit X2( int m_ ): m( m_ ) {}
};
BOOST_DESCRIBE_STRUCT( X2, (), (m) )
struct X3
{
int m;
explicit X3( int m_ ): m( m_ ) {}
};
BOOST_DESCRIBE_STRUCT( X3, (), (m) )
class Y: public X1, protected X2, private X3
{
public:
int m1;
protected:
int m2;
private:
int m3;
public:
Y( int x1, int x2, int x3, int m1_, int m2_, int m3_ ):
X1( x1 ), X2( x2 ), X3( x3 ), m1( m1_ ), m2( m2_ ), m3( m3_ ) {}
BOOST_DESCRIBE_CLASS( Y, (X1, X2, X3), (m1), (m2), (m3) )
};
using boost::describe::operators::operator==;
using boost::describe::operators::operator!=;
using boost::describe::operators::operator<<;
int main()
{
Y y1( 0, 0, 0, 0, 0, 0 );
BOOST_TEST_EQ( y1, y1 );
BOOST_TEST_EQ( boost::hash<Y>()(y1), boost::hash<Y>()(y1) );
Y y2( 1, 0, 0, 0, 0, 0 );
BOOST_TEST_NE( y1, y2 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y2) );
Y y3( 0, 1, 0, 0, 0, 0 );
BOOST_TEST_NE( y1, y3 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y3) );
BOOST_TEST_NE( y2, y3 );
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y3) );
Y y4( 0, 0, 1, 0, 0, 0 );
BOOST_TEST_NE( y1, y4 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y4) );
BOOST_TEST_NE( y2, y4 );
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y4) );
BOOST_TEST_NE( y3, y4 );
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y4) );
Y y5( 0, 0, 0, 1, 0, 0 );
BOOST_TEST_NE( y1, y5 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y5) );
BOOST_TEST_NE( y2, y5 );
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y5) );
BOOST_TEST_NE( y3, y5 );
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y5) );
BOOST_TEST_NE( y4, y5 );
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y5) );
Y y6( 0, 0, 0, 0, 1, 0 );
BOOST_TEST_NE( y1, y6 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y6) );
BOOST_TEST_NE( y2, y6 );
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y6) );
BOOST_TEST_NE( y3, y6 );
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y6) );
BOOST_TEST_NE( y4, y6 );
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y6) );
BOOST_TEST_NE( y5, y6 );
BOOST_TEST_NE( boost::hash<Y>()(y5), boost::hash<Y>()(y6) );
Y y7( 0, 0, 0, 0, 0, 1 );
BOOST_TEST_NE( y1, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y7) );
BOOST_TEST_NE( y2, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y7) );
BOOST_TEST_NE( y3, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y7) );
BOOST_TEST_NE( y4, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y7) );
BOOST_TEST_NE( y5, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y5), boost::hash<Y>()(y7) );
BOOST_TEST_NE( y6, y7 );
BOOST_TEST_NE( boost::hash<Y>()(y6), boost::hash<Y>()(y7) );
return boost::report_errors();
}
#endif

View File

@@ -3,6 +3,8 @@
// 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)
#define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING
#include "./config.hpp"
#if !defined(BOOST_HASH_TEST_EXTENSIONS)

View File

@@ -14,8 +14,8 @@
#include <boost/core/lightweight_test.hpp>
#include "./compile_time.hpp"
void void_func1() { static int x = 1; ++x; }
void void_func2() { static int x = 2; --x; }
void void_func1() { static int x = 1; ++x; (void)x; }
void void_func2() { static int x = 2; --x; (void)x; }
int int_func1(int) { return 0; }
int int_func2(int) { return 1; }

View File

@@ -0,0 +1,41 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/hash.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/unordered/hash_traits.hpp>
#include <boost/config.hpp>
#include <string>
enum my_char { min = 0, max = 255 };
int main()
{
using boost::unordered::hash_is_avalanching;
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::string> > ));
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::wstring> > ));
#if !defined(BOOST_NO_CXX11_CHAR16_T)
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u16string> > ));
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u32string> > ));
#endif
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash< std::basic_string<char8_t> > > ));
#endif
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< boost::hash<std::basic_string<my_char> > > ));
return boost::report_errors();
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/hash.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/config.hpp>
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
BOOST_PRAGMA_MESSAGE( "Test skipped, BOOST_NO_CXX17_HDR_STRING_VIEW is defined" )
int main() {}
#else
#include <boost/unordered/hash_traits.hpp>
#include <string_view>
enum my_char { min = 0, max = 255 };
int main()
{
using boost::unordered::hash_is_avalanching;
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::string_view> > ));
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::wstring_view> > ));
#if !defined(BOOST_NO_CXX11_CHAR16_T)
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u16string_view> > ));
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u32string_view> > ));
#endif
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash< std::basic_string_view<char8_t> > > ));
#endif
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< boost::hash<std::basic_string_view<my_char> > > ));
return boost::report_errors();
}
#endif

View File

@@ -0,0 +1,41 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/hash.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/config.hpp>
#include <boost/config/pragma_message.hpp>
#include <cstddef>
#if defined(BOOST_NO_CXX11_NULLPTR)
BOOST_PRAGMA_MESSAGE( "Test skipped, BOOST_NO_CXX11_NULLPTR is defined" )
int main() {}
#else
template<class T> std::size_t hv( T const& x )
{
return boost::hash<T>()( x );
}
int main()
{
{
BOOST_TEST_EQ( hv((void*)0), hv(nullptr) );
}
{
int x = 0;
BOOST_TEST_EQ( hv((int*)0), hv(nullptr) );
BOOST_TEST_NE( hv(&x), hv(nullptr) );
(void)x;
}
return boost::report_errors();
}
#endif

View File

@@ -51,7 +51,7 @@ int main()
BOOST_TEST_TRAIT_TRUE((is_contiguous_range<std::wstring const>));
// std::vector doesn't have data() in C++03
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700) && !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
BOOST_TEST_TRAIT_TRUE((is_contiguous_range< std::vector<X> >));
BOOST_TEST_TRAIT_TRUE((is_contiguous_range< std::vector<X> const >));
#endif

View File

@@ -50,7 +50,7 @@ int main()
{
using boost::container_hash::is_contiguous_range;
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700) && !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
BOOST_TEST_TRAIT_TRUE((is_contiguous_range<X1>));

View File

@@ -0,0 +1,25 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/is_contiguous_range.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <cstddef>
struct X1
{
char const* begin() const;
char const* end() const;
std::size_t size() const;
char data[16];
};
int main()
{
using boost::container_hash::is_contiguous_range;
BOOST_TEST_TRAIT_FALSE((is_contiguous_range<X1>));
return boost::report_errors();
}

View File

@@ -0,0 +1,56 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/is_described_class.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <string>
struct X1
{
};
struct X2
{
int m;
};
struct Y1
{
};
BOOST_DESCRIBE_STRUCT( Y1, (), () )
struct Y2: Y1
{
int m;
};
BOOST_DESCRIBE_STRUCT( Y2, (Y1), (m) )
int main()
{
using boost::container_hash::is_described_class;
BOOST_TEST_TRAIT_FALSE((is_described_class<void>));
BOOST_TEST_TRAIT_FALSE((is_described_class<int>));
BOOST_TEST_TRAIT_FALSE((is_described_class<X1>));
BOOST_TEST_TRAIT_FALSE((is_described_class<X2>));
BOOST_TEST_TRAIT_FALSE((is_described_class<int[2]>));
BOOST_TEST_TRAIT_FALSE((is_described_class<std::string>));
#if defined(BOOST_DESCRIBE_CXX14)
BOOST_TEST_TRAIT_TRUE((is_described_class<Y1>));
BOOST_TEST_TRAIT_TRUE((is_described_class<Y2>));
#else
BOOST_TEST_TRAIT_FALSE((is_described_class<Y1>));
BOOST_TEST_TRAIT_FALSE((is_described_class<Y2>));
#endif
return boost::report_errors();
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/type_traits/integral_constant.hpp>
#include <boost/describe/class.hpp>
struct X
{
};
BOOST_DESCRIBE_STRUCT( X, (), () )
namespace boost
{
namespace container_hash
{
template<class T> struct is_described_class;
template<> struct is_described_class<X>: boost::false_type {};
} // namespace container_hash
} // namespace boost
#include <boost/container_hash/is_described_class.hpp>
#include <boost/core/lightweight_test_trait.hpp>
int main()
{
using boost::container_hash::is_described_class;
BOOST_TEST_TRAIT_FALSE((is_described_class<X>));
return boost::report_errors();
}

View File

@@ -0,0 +1,31 @@
// Copyright 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/container_hash/is_described_class.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test_trait.hpp>
union Y1
{
};
BOOST_DESCRIBE_STRUCT( Y1, (), () )
union Y2
{
int m1;
float m2;
};
BOOST_DESCRIBE_STRUCT( Y2, (), (m1, m2) )
int main()
{
using boost::container_hash::is_described_class;
BOOST_TEST_TRAIT_FALSE((is_described_class<Y1>));
BOOST_TEST_TRAIT_FALSE((is_described_class<Y2>));
return boost::report_errors();
}