mirror of
https://github.com/boostorg/unordered.git
synced 2026-08-11 16:11:18 +02:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e6b9291de | ||
|
|
ccf9a76ebe | ||
|
|
15cfef6967 | ||
|
|
7692065552 | ||
|
|
bbad4735aa | ||
|
|
1d25c1c053 | ||
|
|
260418e4ee | ||
|
|
aff242dbaf | ||
|
|
4e6d4ade6c | ||
|
|
4fbe450a65 | ||
|
|
bd5c9fc740 | ||
|
|
f411968ffd | ||
|
|
be7c63e1bf | ||
|
|
1df37051ae | ||
|
|
c4dbda0f78 | ||
|
|
fa895edac2 | ||
|
|
43186b04d1 | ||
|
|
67c5cdb3a6 | ||
|
|
75a94781d5 | ||
|
|
76185abe80 | ||
|
|
0d2751c5e1 |
+23
-5
@@ -102,16 +102,16 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
[
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.8* 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.8', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
"g++-4.8-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.9 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
"g++-4.9-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
@@ -468,7 +468,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
windows_pipeline(
|
||||
"Windows VS2015 msvc-14.0",
|
||||
"cppalliance/dronevs2015",
|
||||
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' },
|
||||
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest', 'B2_DONT_EMBED_MANIFEST': 1 },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
@@ -477,15 +477,33 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2017 msvc-14.1 permissive-",
|
||||
"cppalliance/dronevs2017",
|
||||
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17', CXXFLAGS: '/permissive-' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2019 msvc-14.2",
|
||||
"cppalliance/dronevs2019",
|
||||
{ TOOLSET: 'msvc-14.2', CXXSTD: '14,17,20,latest' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2019 msvc-14.2 permissive-",
|
||||
"cppalliance/dronevs2019",
|
||||
{ TOOLSET: 'msvc-14.2', CXXSTD: '14,17', CXXFLAGS: '/permissive-' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2022 msvc-14.3",
|
||||
"cppalliance/dronevs2022:1",
|
||||
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2022 msvc-14.3 permissive-",
|
||||
"cppalliance/dronevs2022:1",
|
||||
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17', CXXFLAGS: '/permissive-' },
|
||||
),
|
||||
]
|
||||
|
||||
+2
-1
@@ -20,4 +20,5 @@ b2 -d0 headers
|
||||
|
||||
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
if not "%CXXFLAGS%" == "" set CXXFLAGS=cxxflags=%CXXFLAGS%
|
||||
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% %CXXFLAGS% variant=debug,release embed-manifest-via=linker
|
||||
|
||||
@@ -265,6 +265,7 @@ jobs:
|
||||
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14,17', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release', cxxflags: '/permissive-' }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
|
||||
- { toolset: gcc, cxxstd: '11,14,17,2a', addrmd: '64', os: 'windows-2019', variant: 'debug,release' }
|
||||
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.85.0
|
||||
|
||||
* Optimized `emplace()` for a `value_type` or `init_type` (if applicable) argument to bypass creating an intermediate object. The argument is already the same type as the would-be intermediate object.
|
||||
* Optimized `emplace()` for `k,v` arguments on map containers to delay constructing the object until it is certain that an element should be inserted. This optimization happens when the map's `key_type` is move constructible or when the `k` argument is a `key_type`.
|
||||
* Fixed support for allocators with `explicit` copy constructors ({github-pr-url}/234[PR#234^]).
|
||||
* Fixed bug in the `const` version of `unordered_multimap::find(k, hash, eq)` ({github-pr-url}/238[PR#238^]).
|
||||
|
||||
== Release 1.84.0 - Major update
|
||||
|
||||
* Added `boost::concurrent_flat_set`.
|
||||
|
||||
@@ -908,7 +908,9 @@ Inserts an object, constructed with the arguments `args`, in the table if and on
|
||||
Requires:;; `value_type` is constructible from `args`.
|
||||
Returns:;; `true` if an insert took place.
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ Copyright (C) 2005-2008 Daniel James
|
||||
|
||||
Copyright (C) 2022-2023 Christian Mazakas
|
||||
|
||||
Copyright (C) 2022-2023 Joaquín M López Muñoz
|
||||
Copyright (C) 2022-2024 Joaquín M López Muñoz
|
||||
|
||||
Copyright (C) 2022-2023 Peter Dimov
|
||||
|
||||
Copyright (C) 2024 Braden Ganetsky
|
||||
|
||||
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)
|
||||
|
||||
@@ -750,6 +750,8 @@ Returns:;; The `bool` component of the return type is `true` if an insert took p
|
||||
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check.
|
||||
|
||||
---
|
||||
|
||||
@@ -769,6 +771,8 @@ Returns:;; The `bool` component of the return type is `true` if an insert took p
|
||||
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -797,7 +797,9 @@ If an insert took place, then the iterator points to the newly inserted element.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check. This optimization happens when the map's `key_type` is move constructible or when the `k` argument is a `key_type`.
|
||||
|
||||
---
|
||||
|
||||
@@ -818,7 +820,9 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
|
||||
+
|
||||
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check. This optimization happens when the map's `key_type` is move constructible or when the `k` argument is a `key_type`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -767,6 +767,8 @@ Returns:;; The `bool` component of the return type is `true` if an insert took p
|
||||
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load to be greater than the maximum load. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check. This optimization happens when `key_type` is move constructible or when the `k` argument is a `key_type`.
|
||||
|
||||
---
|
||||
|
||||
@@ -786,6 +788,8 @@ Returns:;; The `bool` component of the return type is `true` if an insert took p
|
||||
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load to be greater than the maximum load. +
|
||||
+
|
||||
If `args...` is of the form `k,v`, it delays constructing the whole object until it is certain that an element should be inserted, using only the `k` argument to check. This optimization happens when `key_type` is move constructible or when the `k` argument is a `key_type`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Copyright 2024 Braden Ganetsky.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_CONSTRUCTED_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_ALLOCATOR_CONSTRUCTED_HPP
|
||||
|
||||
#include <boost/core/allocator_traits.hpp>
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
|
||||
struct allocator_policy
|
||||
{
|
||||
template <class Allocator, class T, class... Args>
|
||||
static void construct(Allocator& a, T* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(a, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Allocator, class T>
|
||||
static void destroy(Allocator& a, T* p)
|
||||
{
|
||||
boost::allocator_destroy(a, p);
|
||||
}
|
||||
};
|
||||
|
||||
/* constructs a stack-based object with the given policy and allocator */
|
||||
template <class Allocator, class T, class Policy = allocator_policy>
|
||||
class allocator_constructed
|
||||
{
|
||||
opt_storage<T> storage;
|
||||
Allocator alloc;
|
||||
|
||||
public:
|
||||
template <class... Args>
|
||||
allocator_constructed(Allocator const& alloc_, Args&&... args)
|
||||
: alloc(alloc_)
|
||||
{
|
||||
Policy::construct(
|
||||
alloc, storage.address(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~allocator_constructed() { Policy::destroy(alloc, storage.address()); }
|
||||
|
||||
T& value() { return *storage.address(); }
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022-2024 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
@@ -518,7 +518,8 @@ namespace boost {
|
||||
}
|
||||
|
||||
grouped_bucket_array(size_type n, const Allocator& al)
|
||||
: empty_value<node_allocator_type>(empty_init_t(), al),
|
||||
: empty_value<node_allocator_type>(
|
||||
empty_init_t(), node_allocator_type(al)),
|
||||
size_index_(0), size_(0), buckets(), groups()
|
||||
{
|
||||
if (n == 0) {
|
||||
@@ -678,12 +679,17 @@ namespace boost {
|
||||
|
||||
bucket_allocator_type get_bucket_allocator() const
|
||||
{
|
||||
return this->get_node_allocator();
|
||||
return bucket_allocator_type(this->get_node_allocator());
|
||||
}
|
||||
|
||||
group_allocator_type get_group_allocator() const
|
||||
{
|
||||
return this->get_node_allocator();
|
||||
return group_allocator_type(this->get_node_allocator());
|
||||
}
|
||||
|
||||
Allocator get_allocator() const
|
||||
{
|
||||
return Allocator(this->get_node_allocator());
|
||||
}
|
||||
|
||||
size_type buckets_len() const noexcept { return size_ + 1; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* 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)
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
|
||||
#include <boost/unordered/detail/serialization_version.hpp>
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
@@ -50,13 +52,6 @@
|
||||
#include <execution>
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
@@ -90,8 +85,6 @@ public:
|
||||
cache_aligned_array(const cache_aligned_array&)=delete;
|
||||
cache_aligned_array& operator=(const cache_aligned_array&)=delete;
|
||||
|
||||
constexpr std::size_t size()const noexcept{return N;}
|
||||
|
||||
T& operator[](std::size_t pos)noexcept{return *data(pos);}
|
||||
|
||||
private:
|
||||
@@ -136,16 +129,18 @@ template<typename Mutex>
|
||||
class shared_lock
|
||||
{
|
||||
public:
|
||||
shared_lock(Mutex& m)noexcept:pm(&m){pm->lock_shared();}
|
||||
shared_lock(shared_lock&& x)noexcept:pm(x.pm){x.pm=nullptr;x.owns=false;}
|
||||
~shared_lock()noexcept{if(owns&&pm)pm->unlock_shared();}
|
||||
shared_lock(Mutex& m_)noexcept:m(m_){m.lock_shared();}
|
||||
~shared_lock()noexcept{if(owns)m.unlock_shared();}
|
||||
|
||||
void lock(){BOOST_ASSERT(!owns&&pm);pm->lock_shared();owns=true;}
|
||||
void unlock(){BOOST_ASSERT(owns&&pm);pm->unlock_shared();owns=false;}
|
||||
/* not used but VS in pre-C++17 mode needs to see it for RVO */
|
||||
shared_lock(const shared_lock&);
|
||||
|
||||
void lock(){BOOST_ASSERT(!owns);m.lock_shared();owns=true;}
|
||||
void unlock(){BOOST_ASSERT(owns);m.unlock_shared();owns=false;}
|
||||
|
||||
private:
|
||||
Mutex* pm;
|
||||
bool owns=true;
|
||||
Mutex &m;
|
||||
bool owns=true;
|
||||
};
|
||||
|
||||
/* VS in pre-C++17 mode can't implement RVO for std::lock_guard due to
|
||||
@@ -156,12 +151,14 @@ template<typename Mutex>
|
||||
class lock_guard
|
||||
{
|
||||
public:
|
||||
lock_guard(Mutex& m)noexcept:pm(&m){pm->lock();}
|
||||
lock_guard(lock_guard&& x)noexcept:pm(x.pm){x.pm=nullptr;}
|
||||
~lock_guard()noexcept{if(pm)pm->unlock();}
|
||||
lock_guard(Mutex& m_)noexcept:m(m_){m.lock();}
|
||||
~lock_guard()noexcept{m.unlock();}
|
||||
|
||||
/* not used but VS in pre-C++17 mode needs to see it for RVO */
|
||||
lock_guard(const lock_guard&);
|
||||
|
||||
private:
|
||||
Mutex* pm;
|
||||
Mutex &m;
|
||||
};
|
||||
|
||||
/* inspired by boost/multi_index/detail/scoped_bilock.hpp */
|
||||
@@ -203,17 +200,6 @@ private:
|
||||
template<typename Integral>
|
||||
struct atomic_integral
|
||||
{
|
||||
#if 0&&defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
operator Integral()const{return n.load(std::memory_order_acquire);}
|
||||
void operator=(Integral m){n.store(m,std::memory_order_release);}
|
||||
void operator|=(Integral m){n.fetch_or(m);}
|
||||
void operator&=(Integral m){n.fetch_and(m);}
|
||||
|
||||
atomic_integral& operator=(atomic_integral const& rhs) {
|
||||
n.store(rhs.n.load(std::memory_order_acquire),std::memory_order_release);
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
operator Integral()const{return n.load(std::memory_order_relaxed);}
|
||||
void operator=(Integral m){n.store(m,std::memory_order_relaxed);}
|
||||
void operator|=(Integral m){n.fetch_or(m,std::memory_order_relaxed);}
|
||||
@@ -223,7 +209,6 @@ struct atomic_integral
|
||||
n.store(rhs.n.load(std::memory_order_relaxed),std::memory_order_relaxed);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::atomic<Integral> n;
|
||||
};
|
||||
@@ -233,17 +218,6 @@ struct atomic_integral
|
||||
* unprotected_norehash_emplace_or_visit).
|
||||
*/
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
struct group_access
|
||||
{
|
||||
using insert_counter_type=std::atomic<boost::uint32_t>;
|
||||
|
||||
insert_counter_type& insert_counter(){return icnt;}
|
||||
|
||||
private:
|
||||
insert_counter_type icnt{0};
|
||||
};
|
||||
#else
|
||||
struct group_access
|
||||
{
|
||||
using mutex_type=rw_spinlock;
|
||||
@@ -259,7 +233,6 @@ private:
|
||||
mutex_type m;
|
||||
insert_counter_type cnt{0};
|
||||
};
|
||||
#endif
|
||||
|
||||
template<std::size_t Size>
|
||||
group_access* dummy_group_accesses()
|
||||
@@ -286,6 +259,7 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
typename boost::allocator_pointer<group_access_allocator_type>::type;
|
||||
|
||||
using super=table_arrays<Value,Group,SizePolicy,Allocator>;
|
||||
using allocator_type=typename super::allocator_type;
|
||||
|
||||
concurrent_table_arrays(const super& arrays,group_access_pointer pga):
|
||||
super{arrays},group_accesses_{pga}{}
|
||||
@@ -294,12 +268,11 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
return boost::to_address(group_accesses_);
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_(
|
||||
group_access_allocator_type al,std::size_t n)
|
||||
static concurrent_table_arrays new_(allocator_type al,std::size_t n)
|
||||
{
|
||||
super x{super::new_(al,n)};
|
||||
BOOST_TRY{
|
||||
return new_group_access(al,x);
|
||||
return new_group_access(group_access_allocator_type(al),x);
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
super::delete_(al,x);
|
||||
@@ -326,16 +299,6 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses()+i) group_access();
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
// initialize element access counters
|
||||
|
||||
static constexpr auto N=super::N;
|
||||
|
||||
for(std::size_t i=0;i<(arrays.groups_size_mask+1)*N-1;++i){
|
||||
arrays.elements()[i].access=0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_group_access(
|
||||
@@ -359,10 +322,9 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
return arrays;
|
||||
}
|
||||
|
||||
static void delete_(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
static void delete_(allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
delete_group_access(al,arrays);
|
||||
delete_group_access(group_access_allocator_type(al),arrays);
|
||||
super::delete_(al,arrays);
|
||||
}
|
||||
|
||||
@@ -519,12 +481,8 @@ public:
|
||||
concurrent_table(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
super{(std::max)(n,std::size_t(1)),h_,pred_,al_} // TODO: won't work with n==0
|
||||
#else
|
||||
super{n,h_,pred_,al_}
|
||||
#endif
|
||||
{}
|
||||
{}
|
||||
|
||||
concurrent_table(const concurrent_table& x):
|
||||
concurrent_table(x,x.exclusive_access()){}
|
||||
@@ -558,19 +516,7 @@ public:
|
||||
concurrent_table(std::move(x),x.make_empty_arrays())
|
||||
{}
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
~concurrent_table(){
|
||||
std::cout
|
||||
<<"version: 2024/10/21 12:15; "
|
||||
<<"lf: "<<(double)size()/capacity()<<"; "
|
||||
<<"size: "<<size()<<", "
|
||||
<<"capacity: "<<capacity()<<"; "
|
||||
<<"rehashes: "<<rehashes<<"; "
|
||||
<<"max probe:"<<max_probe<<"\n";
|
||||
}
|
||||
#else
|
||||
~concurrent_table()=default;
|
||||
#endif
|
||||
|
||||
concurrent_table& operator=(const concurrent_table& x)
|
||||
{
|
||||
@@ -723,21 +669,10 @@ public:
|
||||
|
||||
bool empty()const noexcept{return size()==0;}
|
||||
|
||||
auto size()const noexcept
|
||||
std::size_t size()const noexcept
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
using ssize_t=std::make_signed<std::size_t>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
ssize_t res=static_cast<ssize_t>(this->size_ctrl.size);
|
||||
for(const auto& sc:local_size_ctrls){
|
||||
res+=sc.size;
|
||||
}
|
||||
return res;
|
||||
#else
|
||||
auto lck=shared_access();
|
||||
return unprotected_size();
|
||||
#endif
|
||||
}
|
||||
|
||||
using super::max_size;
|
||||
@@ -748,6 +683,26 @@ public:
|
||||
return construct_and_emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/* Optimization for value_type and init_type, to avoid constructing twice */
|
||||
template<typename Value>
|
||||
BOOST_FORCEINLINE auto emplace(Value&& x)->typename std::enable_if<
|
||||
detail::is_similar_to_any<Value,value_type,init_type>::value,bool>::type
|
||||
{
|
||||
return emplace_impl(std::forward<Value>(x));
|
||||
}
|
||||
|
||||
/* Optimizations for maps for (k,v) to avoid eagerly constructing value */
|
||||
template <typename K, typename V>
|
||||
BOOST_FORCEINLINE auto emplace(K&& k, V&& v) ->
|
||||
typename std::enable_if<is_emplace_kv_able<concurrent_table, K>::value,
|
||||
bool>::type
|
||||
{
|
||||
alloc_cted_or_fwded_key_type<type_policy, Allocator, K&&> x(
|
||||
this->al(), std::forward<K>(k));
|
||||
return emplace_impl(
|
||||
try_emplace_args_t{}, x.move_or_fwd(), std::forward<V>(v));
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool
|
||||
insert(const init_type& x){return emplace_impl(x);}
|
||||
|
||||
@@ -865,30 +820,6 @@ public:
|
||||
return erase_if(x,[](const value_type&){return true;});
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
template<typename Key,typename F>
|
||||
BOOST_FORCEINLINE auto erase_if(const Key& x,F&& f)->typename std::enable_if<
|
||||
!is_execution_policy<Key>::value,std::size_t>::type
|
||||
{
|
||||
auto lck=shared_access();
|
||||
auto hash=this->hash_for(x);
|
||||
std::size_t res=0;
|
||||
unprotected_internal_visit(
|
||||
group_exclusive{},x,this->position_for(hash),hash,
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p)
|
||||
{
|
||||
if(f(cast_for(group_shared{},type_policy::value_from(*p)))){
|
||||
pg->reset(n);
|
||||
auto& sc=local_size_ctrl();
|
||||
sc.size.fetch_sub(1,std::memory_order_relaxed);
|
||||
sc.mcos.fetch_add(
|
||||
!pg->is_not_overflowed(hash),std::memory_order_relaxed);
|
||||
res=1;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
template<typename Key,typename F>
|
||||
BOOST_FORCEINLINE auto erase_if(const Key& x,F&& f)->typename std::enable_if<
|
||||
!is_execution_policy<Key>::value,std::size_t>::type
|
||||
@@ -907,7 +838,6 @@ public:
|
||||
});
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename F>
|
||||
std::size_t erase_if(F&& f)
|
||||
@@ -1061,16 +991,9 @@ private:
|
||||
using exclusive_lock_guard=reentrancy_checked<lock_guard<multimutex_type>>;
|
||||
using exclusive_bilock_guard=
|
||||
reentrancy_bichecked<scoped_bilock<multimutex_type>>;
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
struct group_shared_lock_guard{};
|
||||
struct group_exclusive_lock_guard{};
|
||||
using group_insert_counter_type=typename group_access::insert_counter_type;
|
||||
#else
|
||||
using group_shared_lock_guard=typename group_access::shared_lock_guard;
|
||||
using group_exclusive_lock_guard=typename group_access::exclusive_lock_guard;
|
||||
using group_insert_counter_type=typename group_access::insert_counter_type;
|
||||
#endif
|
||||
|
||||
concurrent_table(const concurrent_table& x,exclusive_lock_guard):
|
||||
super{x}{}
|
||||
@@ -1083,15 +1006,9 @@ private:
|
||||
concurrent_table&& x,const Allocator& al_,exclusive_lock_guard):
|
||||
super{std::move(x),al_}{}
|
||||
|
||||
static inline std::size_t thread_id()
|
||||
{
|
||||
thread_local auto id=(++thread_counter);
|
||||
return id;
|
||||
}
|
||||
|
||||
inline shared_lock_guard shared_access()const
|
||||
{
|
||||
thread_local auto id=thread_id()%mutexes.size();
|
||||
thread_local auto id=(++thread_counter)%mutexes.size();
|
||||
|
||||
return shared_lock_guard{this,mutexes[id]};
|
||||
}
|
||||
@@ -1122,21 +1039,13 @@ private:
|
||||
|
||||
inline group_shared_lock_guard access(group_shared,std::size_t pos)const
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
return {};
|
||||
#else
|
||||
return this->arrays.group_accesses()[pos].shared_access();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline group_exclusive_lock_guard access(
|
||||
group_exclusive,std::size_t pos)const
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
return {};
|
||||
#else
|
||||
return this->arrays.group_accesses()[pos].exclusive_access();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline group_insert_counter_type& insert_counter(std::size_t pos)const
|
||||
@@ -1266,118 +1175,12 @@ private:
|
||||
{f(cast_for(access_mode,type_policy::value_from(*p)));});
|
||||
}
|
||||
|
||||
/* check occupation with previous unsynced match */
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
static bool is_occupied(group_type* pg,std::size_t pos)
|
||||
{
|
||||
return pg->is_occupied(pos);
|
||||
//return true;
|
||||
}
|
||||
#else
|
||||
static bool is_occupied(group_type* pg,std::size_t pos)
|
||||
{
|
||||
return pg->is_occupied(pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
/* warning: forcing value to bool 'true' or 'false' in bool(pred()...) */
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4800)
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE auto load_access(element_type* p,F f)const
|
||||
->std::pair<decltype(f()),boost::uint32_t>
|
||||
{
|
||||
auto& acnt=p->access;
|
||||
for(;;){
|
||||
auto n=
|
||||
acnt.load(std::memory_order_acquire)&
|
||||
~boost::uint32_t(1);
|
||||
|
||||
auto res=f();
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if(BOOST_LIKELY(acnt.load(std::memory_order_relaxed)==n))return {res,n};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool save_access(element_type* p,boost::uint32_t n,F f)const
|
||||
{
|
||||
auto& acnt=p->access;
|
||||
if(!acnt.compare_exchange_strong(n,n+1,std::memory_order_acq_rel)){
|
||||
return false;
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
f();
|
||||
|
||||
acnt.store(n+2,std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE void save_access(element_type* p,F f)const /* no previous load cnt */
|
||||
{
|
||||
auto &acnt=p->access;
|
||||
for(;;){
|
||||
auto n=
|
||||
acnt.load(std::memory_order_acquire)&
|
||||
~boost::uint32_t(1);
|
||||
//if(n%2==1)continue;
|
||||
|
||||
if(save_access(p,n,f))return;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename Key,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_internal_visit(
|
||||
GroupAccessMode access_mode,
|
||||
const Key& x,std::size_t pos0,std::size_t hash,F&& f)const
|
||||
{
|
||||
startover:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
auto [pr,acnt]=load_access(p+n,[&]{
|
||||
return std::make_pair(is_occupied(pg,n),p[n].value);
|
||||
});
|
||||
auto [occupied,v]=pr;
|
||||
|
||||
if(BOOST_LIKELY(occupied&&this->pred()(x,this->key_from(v)))){
|
||||
if constexpr(std::is_same<GroupAccessMode,group_exclusive>::value){
|
||||
if(BOOST_UNLIKELY(!save_access(p+n,acnt,[&]{f(pg,n,p+n);}))){
|
||||
goto startover;
|
||||
}
|
||||
return 1;
|
||||
}else{
|
||||
element_type e{v};
|
||||
f(pg,n,&e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
template<typename GroupAccessMode,typename Key,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_internal_visit(
|
||||
GroupAccessMode access_mode,
|
||||
@@ -1409,7 +1212,6 @@ private:
|
||||
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename GroupAccessMode,typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_bulk_visit(
|
||||
@@ -1455,7 +1257,7 @@ private:
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(
|
||||
is_occupied(pg,n)&&
|
||||
pg->is_occupied(n)&&
|
||||
bool(this->pred()(*it,this->key_from(p[n]))))){
|
||||
f(cast_for(access_mode,type_policy::value_from(p[n])));
|
||||
++res;
|
||||
@@ -1530,7 +1332,7 @@ private:
|
||||
{
|
||||
auto lck=shared_access();
|
||||
|
||||
auto x=alloc_make_insert_type<type_policy>(
|
||||
alloc_cted_insert_type<type_policy,Allocator,Args...> x(
|
||||
this->al(),std::forward<Args>(args)...);
|
||||
int res=unprotected_norehash_emplace_or_visit(
|
||||
access_mode,std::forward<F>(f),type_policy::move(x.value()));
|
||||
@@ -1655,94 +1457,6 @@ private:
|
||||
bool commit_=false;
|
||||
};
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
struct latch_free_reserve_slot
|
||||
{
|
||||
latch_free_reserve_slot(group_type* pg,std::size_t pos):
|
||||
pc{reinterpret_cast<std::atomic<unsigned char>*>(pg)+pos}
|
||||
{
|
||||
unsigned char expected=0;
|
||||
succeeded_=pc->compare_exchange_weak(
|
||||
expected,1,std::memory_order_relaxed,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
~latch_free_reserve_slot()
|
||||
{
|
||||
if(succeeded_&&!commit_)pc->store(0,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool succeeded()const{return succeeded_;}
|
||||
|
||||
void commit(){commit_=true;}
|
||||
|
||||
std::atomic<unsigned char>* pc;
|
||||
bool succeeded_;
|
||||
bool commit_=false;
|
||||
};
|
||||
|
||||
struct assign_insert_counter_on_exit
|
||||
{
|
||||
~assign_insert_counter_on_exit()
|
||||
{
|
||||
counter.store(x,std::memory_order_release);
|
||||
}
|
||||
|
||||
group_insert_counter_type &counter;
|
||||
boost::uint32_t x;
|
||||
};
|
||||
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
BOOST_FORCEINLINE int
|
||||
unprotected_norehash_emplace_or_visit(
|
||||
GroupAccessMode access_mode,F&& f,Args&&... args)
|
||||
{
|
||||
const auto &k=this->key_from(std::forward<Args>(args)...);
|
||||
auto hash=this->hash_for(k);
|
||||
auto pos0=this->position_for(hash);
|
||||
|
||||
startover:
|
||||
boost::uint32_t counter=0;
|
||||
do{
|
||||
counter=insert_counter(pos0).load(std::memory_order_acquire);
|
||||
}
|
||||
while(BOOST_UNLIKELY(counter%2==1));
|
||||
|
||||
if(unprotected_visit(
|
||||
access_mode,k,pos0,hash,std::forward<F>(f)))return 0;
|
||||
|
||||
std::size_t pbn=max_probe;
|
||||
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
auto [ocuppied,acnt]=load_access(p+n,[&]{return is_occupied(pg,n);});
|
||||
if(ocuppied)goto startover;
|
||||
|
||||
auto res=false;
|
||||
if(!save_access(p+n,acnt,[&]{
|
||||
if(insert_counter(pos0).compare_exchange_strong(
|
||||
counter,counter+1,std::memory_order_relaxed)){
|
||||
assign_insert_counter_on_exit a{insert_counter(pos0),counter+2};
|
||||
this->construct_element(p+n,std::forward<Args>(args)...);
|
||||
pg->set(n,hash);
|
||||
res=true;
|
||||
}
|
||||
})||!res)goto startover;
|
||||
|
||||
auto& sc=local_size_ctrl();
|
||||
sc.size.fetch_add(1,std::memory_order_relaxed);
|
||||
sc.mcos.fetch_sub(!pg->is_not_overflowed(hash),std::memory_order_relaxed);
|
||||
return 1;
|
||||
}
|
||||
if(!pbn--)return -1;
|
||||
pg->mark_overflow(hash);
|
||||
}
|
||||
}
|
||||
#else
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
BOOST_FORCEINLINE int
|
||||
unprotected_norehash_emplace_or_visit(
|
||||
@@ -1784,30 +1498,13 @@ private:
|
||||
else return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void rehash_if_full()
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
auto lck=shared_access();
|
||||
auto p=calculate_size_ctrl();
|
||||
lck.unlock();
|
||||
if(p.first>=p.second){ // NB >=
|
||||
auto lck=exclusive_access();
|
||||
update_size_ctrl();
|
||||
++rehashes;
|
||||
this->unchecked_rehash_for_growth();
|
||||
max_probe=default_max_probe;
|
||||
}
|
||||
else{
|
||||
++max_probe;
|
||||
}
|
||||
#else
|
||||
auto lck=exclusive_access();
|
||||
if(this->size_ctrl.size==this->size_ctrl.ml){
|
||||
this->unchecked_rehash_for_growth();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
@@ -2048,62 +1745,6 @@ private:
|
||||
|
||||
static std::atomic<std::size_t> thread_counter;
|
||||
mutable multimutex_type mutexes;
|
||||
|
||||
#if defined(BOOST_UNORDERED_LATCH_FREE)
|
||||
struct alignas(64) local_size_ctrl_type
|
||||
{
|
||||
using ssize_t=std::make_signed<std::size_t>::type;
|
||||
|
||||
std::atomic<ssize_t> size=0;
|
||||
std::atomic<ssize_t> mcos=0;
|
||||
};
|
||||
static constexpr std::size_t default_max_probe=3;
|
||||
|
||||
mutable std::array<local_size_ctrl_type,128> local_size_ctrls;
|
||||
std::atomic<std::size_t> max_probe=default_max_probe;
|
||||
std::size_t rehashes=0;
|
||||
//unsigned char paddd[64];
|
||||
|
||||
local_size_ctrl_type& local_size_ctrl()const
|
||||
{
|
||||
return local_size_ctrls[thread_id()%local_size_ctrls.size()];
|
||||
}
|
||||
|
||||
std::pair<std::size_t,std::size_t> calculate_size_ctrl()
|
||||
{
|
||||
using ssize_t=std::make_signed<std::size_t>::type;
|
||||
|
||||
ssize_t ssize=0,smcos=0;
|
||||
for(const auto& sc:local_size_ctrls){
|
||||
ssize+=sc.size.load(std::memory_order_relaxed);
|
||||
smcos+=sc.mcos.load(std::memory_order_relaxed);
|
||||
}
|
||||
std::size_t size_=this->size_ctrl.size.load(std::memory_order_relaxed),
|
||||
ml_=this->size_ctrl.ml.load(std::memory_order_relaxed);
|
||||
size_+=ssize;
|
||||
if(ssize_t(ml_)>=smcos)ml_-=smcos;
|
||||
else ml_=0;
|
||||
auto max_ml=super::initial_max_load();
|
||||
if(ml_>max_ml)ml_=max_ml;
|
||||
return {size_,ml_};
|
||||
}
|
||||
|
||||
void update_size_ctrl()
|
||||
{
|
||||
using ssize_t=std::make_signed<std::size_t>::type;
|
||||
|
||||
ssize_t ssize=0,smcos=0;
|
||||
for(auto& sc:local_size_ctrls){
|
||||
ssize+=sc.size.exchange(0);
|
||||
smcos+=sc.mcos.exchange(0);
|
||||
}
|
||||
this->size_ctrl.size+=ssize;
|
||||
if(ssize_t(this->size_ctrl.ml)>=smcos)this->size_ctrl.ml-=smcos;
|
||||
else this->size_ctrl.ml=0;
|
||||
auto max_ml=super::initial_max_load();
|
||||
if(this->size_ctrl.ml>max_ml)this->size_ctrl.ml=max_ml;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T,typename H,typename P,typename A>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* Common base for Boost.Unordered open-addressing tables.
|
||||
*
|
||||
* Copyright 2022-2023 Joaquin M Lopez Munoz.
|
||||
* Copyright 2022-2024 Joaquin M Lopez Munoz.
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* 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)
|
||||
@@ -22,6 +23,7 @@
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/predef.h>
|
||||
#include <boost/unordered/detail/allocator_constructed.hpp>
|
||||
#include <boost/unordered/detail/narrow_cast.hpp>
|
||||
#include <boost/unordered/detail/mulx.hpp>
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
@@ -382,13 +384,11 @@ private:
|
||||
return (int)word[narrow_cast<unsigned char>(hash)];
|
||||
}
|
||||
|
||||
public:
|
||||
inline static unsigned char reduced_hash(std::size_t hash)
|
||||
{
|
||||
return narrow_cast<unsigned char>(match_word(hash));
|
||||
}
|
||||
|
||||
private:
|
||||
inline slot_type& at(std::size_t pos)
|
||||
{
|
||||
return m[pos];
|
||||
@@ -534,7 +534,6 @@ private:
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
inline static unsigned char reduced_hash(std::size_t hash)
|
||||
{
|
||||
static constexpr unsigned char table[]={
|
||||
@@ -559,7 +558,6 @@ public:
|
||||
return table[(unsigned char)hash];
|
||||
}
|
||||
|
||||
private:
|
||||
/* Copied from
|
||||
* https://github.com/simd-everywhere/simde/blob/master/simde/x86/
|
||||
* sse2.h#L3763
|
||||
@@ -979,7 +977,12 @@ struct arrays_holder
|
||||
arrays_holder(arrays_holder const&);
|
||||
arrays_holder& operator=(arrays_holder const&)=delete;
|
||||
|
||||
~arrays_holder(){if(!released_)arrays_.delete_(al_,arrays_);}
|
||||
~arrays_holder()
|
||||
{
|
||||
if(!released_){
|
||||
arrays_.delete_(typename Arrays::allocator_type(al_),arrays_);
|
||||
}
|
||||
}
|
||||
|
||||
const Arrays& release()
|
||||
{
|
||||
@@ -1224,22 +1227,15 @@ class alloc_cted_insert_type
|
||||
emplace_type,typename TypePolicy::element_type
|
||||
>::type;
|
||||
|
||||
alignas(insert_type) unsigned char storage[sizeof(insert_type)];
|
||||
Allocator al;
|
||||
using alloc_cted = allocator_constructed<Allocator, insert_type, TypePolicy>;
|
||||
alloc_cted val;
|
||||
|
||||
public:
|
||||
alloc_cted_insert_type(const Allocator& al_,Args&&... args):al{al_}
|
||||
alloc_cted_insert_type(const Allocator& al_,Args&&... args):val{al_,std::forward<Args>(args)...}
|
||||
{
|
||||
TypePolicy::construct(al,data(),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~alloc_cted_insert_type()
|
||||
{
|
||||
TypePolicy::destroy(al,data());
|
||||
}
|
||||
|
||||
insert_type* data(){return reinterpret_cast<insert_type*>(&storage);}
|
||||
insert_type& value(){return *data();}
|
||||
insert_type& value(){return val.value();}
|
||||
};
|
||||
|
||||
template<typename TypePolicy,typename Allocator,typename... Args>
|
||||
@@ -1249,6 +1245,51 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
|
||||
return {al,std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
template <typename TypePolicy, typename Allocator, typename KFwdRef,
|
||||
typename = void>
|
||||
class alloc_cted_or_fwded_key_type
|
||||
{
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
allocator_constructed<Allocator, key_type, TypePolicy> val;
|
||||
|
||||
public:
|
||||
alloc_cted_or_fwded_key_type(const Allocator& al_, KFwdRef k)
|
||||
: val(al_, std::forward<KFwdRef>(k))
|
||||
{
|
||||
}
|
||||
|
||||
key_type&& move_or_fwd() { return std::move(val.value()); }
|
||||
};
|
||||
|
||||
template <typename TypePolicy, typename Allocator, typename KFwdRef>
|
||||
class alloc_cted_or_fwded_key_type<TypePolicy, Allocator, KFwdRef,
|
||||
typename std::enable_if<
|
||||
is_similar<KFwdRef, typename TypePolicy::key_type>::value>::type>
|
||||
{
|
||||
// This specialization acts as a forwarding-reference wrapper
|
||||
BOOST_UNORDERED_STATIC_ASSERT(std::is_reference<KFwdRef>::value);
|
||||
KFwdRef ref;
|
||||
|
||||
public:
|
||||
alloc_cted_or_fwded_key_type(const Allocator&, KFwdRef k)
|
||||
: ref(std::forward<KFwdRef>(k))
|
||||
{
|
||||
}
|
||||
|
||||
KFwdRef move_or_fwd() { return std::forward<KFwdRef>(ref); }
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
using is_map =
|
||||
std::integral_constant<bool, !std::is_same<typename Container::key_type,
|
||||
typename Container::value_type>::value>;
|
||||
|
||||
template <typename Container, typename K>
|
||||
using is_emplace_kv_able = std::integral_constant<bool,
|
||||
is_map<Container>::value &&
|
||||
(is_similar<K, typename Container::key_type>::value ||
|
||||
is_complete_and_move_constructible<typename Container::key_type>::value)>;
|
||||
|
||||
/* table_core. The TypePolicy template parameter is used to generate
|
||||
* instantiations suitable for either maps or sets, and introduces non-standard
|
||||
* init_type and element_type:
|
||||
@@ -1266,7 +1307,7 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
|
||||
*
|
||||
* - TypePolicy::construct and TypePolicy::destroy are used for the
|
||||
* construction and destruction of the internal types: value_type,
|
||||
* init_type and element_type.
|
||||
* init_type, element_type, and key_type.
|
||||
*
|
||||
* - TypePolicy::move is used to provide move semantics for the internal
|
||||
* types used by the container during rehashing and emplace. These types
|
||||
@@ -1938,7 +1979,7 @@ private:
|
||||
|
||||
arrays_type new_arrays(std::size_t n)const
|
||||
{
|
||||
return arrays_type::new_(al(),n);
|
||||
return arrays_type::new_(typename arrays_type::allocator_type(al()),n);
|
||||
}
|
||||
|
||||
arrays_type new_arrays_for_growth()const
|
||||
@@ -1959,7 +2000,7 @@ private:
|
||||
|
||||
void delete_arrays(arrays_type& arrays_)noexcept
|
||||
{
|
||||
arrays_type::delete_(al(),arrays_);
|
||||
arrays_type::delete_(typename arrays_type::allocator_type(al()),arrays_);
|
||||
}
|
||||
|
||||
arrays_holder_type make_arrays(std::size_t n)const
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
|
||||
@@ -56,6 +57,12 @@ namespace boost {
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
@@ -65,6 +72,11 @@ namespace boost {
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, key_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
|
||||
@@ -82,6 +83,12 @@ namespace boost {
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
@@ -109,6 +116,11 @@ namespace boost {
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, key_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A>
|
||||
static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
*
|
||||
* Copyright 2022-2023 Joaquin M Lopez Munoz.
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* 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)
|
||||
@@ -18,6 +19,7 @@
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/unordered/detail/foa/core.hpp>
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -400,11 +402,34 @@ public:
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
|
||||
{
|
||||
auto x=alloc_make_insert_type<type_policy>(
|
||||
alloc_cted_insert_type<type_policy,Allocator,Args...> x(
|
||||
this->al(),std::forward<Args>(args)...);
|
||||
return emplace_impl(type_policy::move(x.value()));
|
||||
}
|
||||
|
||||
/* Optimization for value_type and init_type, to avoid constructing twice */
|
||||
template <typename T>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::is_similar_to_any<T, value_type, init_type>::value,
|
||||
std::pair<iterator, bool> >::type
|
||||
emplace(T&& x)
|
||||
{
|
||||
return emplace_impl(std::forward<T>(x));
|
||||
}
|
||||
|
||||
/* Optimizations for maps for (k,v) to avoid eagerly constructing value */
|
||||
template <typename K, typename V>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<is_emplace_kv_able<table, K>::value,
|
||||
std::pair<iterator, bool> >::type
|
||||
emplace(K&& k, V&& v)
|
||||
{
|
||||
alloc_cted_or_fwded_key_type<type_policy, Allocator, K&&> x(
|
||||
this->al(), std::forward<K>(k));
|
||||
return emplace_impl(
|
||||
try_emplace_args_t{}, x.move_or_fwd(), std::forward<V>(v));
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
|
||||
Key&& x,Args&&... args)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022-2024 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
//
|
||||
// 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)
|
||||
@@ -14,6 +15,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/allocator_constructed.hpp>
|
||||
#include <boost/unordered/detail/fca.hpp>
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
@@ -38,6 +40,7 @@
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <tuple> // std::forward_as_tuple
|
||||
|
||||
namespace boost {
|
||||
namespace tuples {
|
||||
@@ -103,6 +106,10 @@ namespace boost {
|
||||
template <class T> no_key(T const&) {}
|
||||
};
|
||||
|
||||
struct converting_key
|
||||
{
|
||||
};
|
||||
|
||||
namespace func {
|
||||
template <class T> inline void ignore_unused_variable_warning(T const&)
|
||||
{
|
||||
@@ -1359,14 +1366,14 @@ namespace boost {
|
||||
}
|
||||
|
||||
table(std::size_t num_buckets, hasher const& hf, key_equal const& eq,
|
||||
node_allocator_type const& a)
|
||||
value_allocator const& a)
|
||||
: functions(hf, eq), size_(0), mlf_(1.0f), max_load_(0),
|
||||
buckets_(num_buckets, a)
|
||||
{
|
||||
recalculate_max_load();
|
||||
}
|
||||
|
||||
table(table const& x, node_allocator_type const& a)
|
||||
table(table const& x, value_allocator const& a)
|
||||
: functions(x), size_(0), mlf_(x.mlf_), max_load_(0),
|
||||
buckets_(x.size_, a)
|
||||
{
|
||||
@@ -1381,7 +1388,7 @@ namespace boost {
|
||||
x.max_load_ = 0;
|
||||
}
|
||||
|
||||
table(table& x, node_allocator_type const& a,
|
||||
table(table& x, value_allocator const& a,
|
||||
boost::unordered::detail::move_tag m)
|
||||
: functions(x, m), size_(0), mlf_(x.mlf_), max_load_(0),
|
||||
buckets_(x.bucket_count(), a)
|
||||
@@ -1912,6 +1919,16 @@ namespace boost {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
emplace_return emplace_unique(converting_key, K&& k, V&& v)
|
||||
{
|
||||
using alloc_cted = allocator_constructed<node_allocator_type,
|
||||
typename Types::key_type>;
|
||||
alloc_cted key(this->node_alloc(), std::forward<K>(k));
|
||||
return emplace_unique(
|
||||
key.value(), std::move(key.value()), std::forward<V>(v));
|
||||
}
|
||||
|
||||
template <typename Key> emplace_return try_emplace_unique(Key&& k)
|
||||
{
|
||||
std::size_t key_hash = this->hash(k);
|
||||
@@ -2705,7 +2722,7 @@ namespace boost {
|
||||
inline void table<Types>::rehash_impl(std::size_t num_buckets)
|
||||
{
|
||||
bucket_array_type new_buckets(
|
||||
num_buckets, buckets_.get_node_allocator());
|
||||
num_buckets, buckets_.get_allocator());
|
||||
|
||||
BOOST_TRY
|
||||
{
|
||||
@@ -2834,9 +2851,13 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static no_key extract(Arg1 const&, Arg2 const&)
|
||||
static typename std::conditional<
|
||||
(is_similar<Arg1, key_type>::value ||
|
||||
is_complete_and_move_constructible<key_type>::value),
|
||||
converting_key, no_key>::type
|
||||
extract(Arg1 const&, Arg2 const&)
|
||||
{
|
||||
return no_key();
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class Arg3, class... Args>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace boost {
|
||||
typedef std::pair<K const, M> value_type;
|
||||
typedef H hasher;
|
||||
typedef P key_equal;
|
||||
typedef K key_type;
|
||||
typedef K const const_key_type;
|
||||
|
||||
typedef
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2023 Braden Ganetsky
|
||||
// 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_THROW_EXCEPTION_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
|
||||
BOOST_NOINLINE BOOST_NORETURN inline void throw_out_of_range(
|
||||
char const* message)
|
||||
{
|
||||
boost::throw_exception(std::out_of_range(message));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
//
|
||||
// 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)
|
||||
@@ -48,6 +49,20 @@ namespace boost {
|
||||
|
||||
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template <class T, class = void> struct is_complete : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_complete<T, void_t<int[sizeof(T)]> > : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using is_complete_and_move_constructible =
|
||||
typename std::conditional<is_complete<T>::value,
|
||||
std::is_move_constructible<T>, std::false_type>::type;
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
|
||||
/* std::is_trivially_default_constructible not provided */
|
||||
template <class T>
|
||||
@@ -147,6 +162,23 @@ namespace boost {
|
||||
!std::is_convertible<Key, const_iterator>::value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using remove_cvref_t =
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
template <class T, class U>
|
||||
using is_similar = std::is_same<remove_cvref_t<T>, remove_cvref_t<U> >;
|
||||
|
||||
template <class, class...> struct is_similar_to_any : std::false_type
|
||||
{
|
||||
};
|
||||
template <class T, class U, class... Us>
|
||||
struct is_similar_to_any<T, U, Us...>
|
||||
: std::conditional<is_similar<T, U>::value, is_similar<T, U>,
|
||||
is_similar_to_any<T, Us...> >::type
|
||||
{
|
||||
};
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
// https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
|
||||
// https://eel.is/c++draft/container.requirements#unord.req.general-243
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
#include <boost/unordered/detail/foa/flat_map_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
#include <boost/unordered/detail/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_map_fwd.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
@@ -459,8 +459,8 @@ namespace boost {
|
||||
// TODO: someday refactor this to conditionally serialize the key and
|
||||
// include it in the error message
|
||||
//
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_flat_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_flat_map");
|
||||
}
|
||||
|
||||
mapped_type const& at(key_type const& key) const
|
||||
@@ -469,8 +469,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_flat_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_flat_map");
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -483,8 +483,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_flat_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_flat_map");
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -497,8 +497,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_flat_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_flat_map");
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James.
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
#include <boost/unordered/detail/map.hpp>
|
||||
#include <boost/unordered/detail/serialize_fca_container.hpp>
|
||||
#include <boost/unordered/detail/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
@@ -157,7 +159,7 @@ namespace boost {
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.node_alloc();
|
||||
return allocator_type(table_.node_alloc());
|
||||
}
|
||||
|
||||
// // iterators
|
||||
@@ -253,12 +255,15 @@ namespace boost {
|
||||
node_type extract(const_iterator position)
|
||||
{
|
||||
return node_type(
|
||||
table_.extract_by_iterator_unique(position), table_.node_alloc());
|
||||
table_.extract_by_iterator_unique(position),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
node_type extract(const key_type& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
return node_type(
|
||||
table_.extract_by_key_impl(k),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@@ -267,8 +272,9 @@ namespace boost {
|
||||
node_type>::type
|
||||
extract(Key&& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(std::forward<Key>(k)),
|
||||
table_.node_alloc());
|
||||
return node_type(
|
||||
table_.extract_by_key_impl(std::forward<Key>(k)),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
insert_return_type insert(node_type&& np)
|
||||
@@ -805,7 +811,7 @@ namespace boost {
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.node_alloc();
|
||||
return allocator_type(table_.node_alloc());
|
||||
}
|
||||
|
||||
// iterators
|
||||
@@ -1586,8 +1592,8 @@ namespace boost {
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
std::out_of_range("Unable to find key in unordered_map."));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"Unable to find key in unordered_map.");
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -1602,8 +1608,8 @@ namespace boost {
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
std::out_of_range("Unable to find key in unordered_map."));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"Unable to find key in unordered_map.");
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -1620,8 +1626,8 @@ namespace boost {
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
std::out_of_range("Unable to find key in unordered_map."));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"Unable to find key in unordered_map.");
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -1638,8 +1644,8 @@ namespace boost {
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
std::out_of_range("Unable to find key in unordered_map."));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"Unable to find key in unordered_map.");
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -2051,8 +2057,7 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq) const
|
||||
{
|
||||
return const_iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
#include <boost/unordered/detail/foa/node_map_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
#include <boost/unordered/detail/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
@@ -554,8 +554,8 @@ namespace boost {
|
||||
// TODO: someday refactor this to conditionally serialize the key and
|
||||
// include it in the error message
|
||||
//
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_node_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_node_map");
|
||||
}
|
||||
|
||||
mapped_type const& at(key_type const& key) const
|
||||
@@ -564,8 +564,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_node_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_node_map");
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -578,8 +578,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_node_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_node_map");
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -592,8 +592,8 @@ namespace boost {
|
||||
if (pos != table_.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
boost::throw_exception(
|
||||
std::out_of_range("key was not found in unordered_node_map"));
|
||||
boost::unordered::detail::throw_out_of_range(
|
||||
"key was not found in unordered_node_map");
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James.
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -154,7 +155,7 @@ namespace boost {
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.node_alloc();
|
||||
return allocator_type(table_.node_alloc());
|
||||
}
|
||||
|
||||
// iterators
|
||||
@@ -252,12 +253,15 @@ namespace boost {
|
||||
node_type extract(const_iterator position)
|
||||
{
|
||||
return node_type(
|
||||
table_.extract_by_iterator_unique(position), table_.node_alloc());
|
||||
table_.extract_by_iterator_unique(position),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
node_type extract(const key_type& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
return node_type(
|
||||
table_.extract_by_key_impl(k),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@@ -266,7 +270,9 @@ namespace boost {
|
||||
node_type>::type
|
||||
extract(const Key& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
return node_type(
|
||||
table_.extract_by_key_impl(k),
|
||||
allocator_type(table_.node_alloc()));
|
||||
}
|
||||
|
||||
insert_return_type insert(node_type&& np)
|
||||
@@ -647,7 +653,7 @@ namespace boost {
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.node_alloc();
|
||||
return allocator_type(table_.node_alloc());
|
||||
}
|
||||
|
||||
// iterators
|
||||
|
||||
@@ -85,12 +85,14 @@ local FCA_TESTS =
|
||||
contains_tests
|
||||
copy_tests
|
||||
deduction_tests
|
||||
emplace_smf_tests
|
||||
emplace_tests
|
||||
equality_tests
|
||||
equivalent_keys_tests
|
||||
erase_equiv_tests
|
||||
erase_if
|
||||
erase_tests
|
||||
explicit_alloc_ctor_tests
|
||||
extract_tests
|
||||
find_tests
|
||||
fwd_map_test
|
||||
@@ -201,8 +203,10 @@ local FOA_TESTS =
|
||||
assign_tests
|
||||
insert_tests
|
||||
insert_hint_tests
|
||||
emplace_smf_tests
|
||||
emplace_tests
|
||||
erase_tests
|
||||
explicit_alloc_ctor_tests
|
||||
merge_tests
|
||||
find_tests
|
||||
at_tests
|
||||
@@ -325,6 +329,7 @@ local CFOA_TESTS =
|
||||
rw_spinlock_test7
|
||||
rw_spinlock_test8
|
||||
reentrancy_check_test
|
||||
explicit_alloc_ctor_tests
|
||||
;
|
||||
|
||||
for local test in $(CFOA_TESTS)
|
||||
|
||||
+12
-22
@@ -968,11 +968,9 @@ namespace {
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * values.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::destructor, value_type_cardinality * values.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@@ -998,11 +996,9 @@ namespace {
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * values.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::destructor, value_type_cardinality * values.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@@ -1030,14 +1026,11 @@ namespace {
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * values.size());
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::destructor,
|
||||
value_type_cardinality * values.size() +
|
||||
value_type_cardinality * reference_cont.size());
|
||||
raii::destructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor,
|
||||
2 * value_type_cardinality * reference_cont.size());
|
||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@@ -1063,14 +1056,11 @@ namespace {
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * values.size());
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::destructor,
|
||||
value_type_cardinality * values.size() +
|
||||
value_type_cardinality * reference_cont.size());
|
||||
raii::destructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor,
|
||||
2 * value_type_cardinality * reference_cont.size());
|
||||
raii::move_constructor, value_type_cardinality * reference_cont.size());
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
|
||||
@@ -712,9 +712,9 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * 11u);
|
||||
raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
@@ -730,9 +730,9 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * 11u);
|
||||
raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
@@ -748,9 +748,9 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * 11u);
|
||||
raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
|
||||
@@ -766,9 +766,9 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size());
|
||||
raii::copy_constructor, value_type_cardinality * init_list.size() / 2u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, value_type_cardinality * 11u);
|
||||
raii::move_constructor, 0u);
|
||||
}
|
||||
check_raii_counts();
|
||||
}
|
||||
@@ -867,6 +867,9 @@ namespace {
|
||||
template <class X, class GF>
|
||||
void flat_constructor(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<value_type>::value;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
@@ -886,8 +889,8 @@ namespace {
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
|
||||
BOOST_TEST_EQ(old_dc, 0u);
|
||||
BOOST_TEST_GT(old_mc, 0u);
|
||||
BOOST_TEST_GT(old_cc, 0u);
|
||||
BOOST_TEST_EQ(old_mc, 0u);
|
||||
BOOST_TEST_EQ(old_cc, value_type_cardinality * flat.size());
|
||||
|
||||
X x(std::move(flat));
|
||||
|
||||
@@ -931,8 +934,8 @@ namespace {
|
||||
auto const old_cc = +raii::copy_constructor;
|
||||
|
||||
BOOST_TEST_EQ(old_dc, 0u);
|
||||
BOOST_TEST_GT(old_mc, 0u);
|
||||
BOOST_TEST_GT(old_cc, 0u);
|
||||
BOOST_TEST_EQ(old_mc, 0u);
|
||||
BOOST_TEST_EQ(old_cc, 2u * value_type_cardinality * x.size());
|
||||
|
||||
flat_container<X> flat(std::move(x));
|
||||
|
||||
|
||||
+256
-11
@@ -1,9 +1,11 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "../helpers/count.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
@@ -51,7 +53,7 @@ namespace {
|
||||
|
||||
struct lvalue_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
template <class T, class X> void call_impl(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
@@ -66,28 +68,33 @@ namespace {
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(
|
||||
raii::default_constructor, value_type_cardinality * values.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
|
||||
std::uint64_t const default_constructors = value_type_cardinality == 2
|
||||
? values.size() + num_inserts
|
||||
: values.size();
|
||||
BOOST_TEST_EQ(raii::default_constructor, default_constructors);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
|
||||
call_impl(values, x);
|
||||
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
|
||||
}
|
||||
} lvalue_emplacer;
|
||||
|
||||
struct norehash_lvalue_emplacer_type : public lvalue_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
|
||||
x.reserve(values.size());
|
||||
lvalue_emplacer_type::operator()(values, x);
|
||||
BOOST_TEST_EQ(raii::move_constructor, value_type_cardinality * x.size());
|
||||
lvalue_emplacer_type::call_impl(values, x);
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
}
|
||||
} norehash_lvalue_emplacer;
|
||||
|
||||
@@ -170,6 +177,74 @@ namespace {
|
||||
}
|
||||
} lvalue_emplace_or_visit;
|
||||
|
||||
struct copy_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto const& r : s) {
|
||||
bool b = x.emplace(r);
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} copy_emplacer;
|
||||
|
||||
struct move_emplacer_type
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
for (auto& r : s) {
|
||||
bool b = x.emplace(std::move(r));
|
||||
if (b) {
|
||||
++num_inserts;
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_TEST_EQ(num_inserts, x.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
if (std::is_same<T, typename X::value_type>::value &&
|
||||
!std::is_same<typename X::key_type,
|
||||
typename X::value_type>::value) { // map value_type can only be
|
||||
// copied, no move
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
}
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) // C4127
|
||||
#endif
|
||||
BOOST_TEST_GT(raii::move_constructor, value_type_cardinality * x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} move_emplacer;
|
||||
|
||||
template <class X, class GF, class F>
|
||||
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
|
||||
{
|
||||
@@ -220,9 +295,179 @@ UNORDERED_TEST(
|
||||
((map)(set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_emplacer)(norehash_lvalue_emplacer)
|
||||
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
|
||||
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace {
|
||||
using converting_key_type = basic_raii<struct converting_key_tag_>;
|
||||
using converting_value_type = basic_raii<struct converting_value_tag_>;
|
||||
|
||||
class counted_key_type : public basic_raii<struct counted_key_tag_>
|
||||
{
|
||||
public:
|
||||
using basic_raii::basic_raii;
|
||||
counted_key_type() = default;
|
||||
counted_key_type(const converting_key_type& k) : counted_key_type(k.x_) {}
|
||||
};
|
||||
class counted_value_type : public basic_raii<struct counted_value_tag_>
|
||||
{
|
||||
public:
|
||||
using basic_raii::basic_raii;
|
||||
counted_value_type() = default;
|
||||
counted_value_type(const converting_value_type& v)
|
||||
: counted_value_type(v.x_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void reset_counts()
|
||||
{
|
||||
counted_key_type::reset_counts();
|
||||
counted_value_type::reset_counts();
|
||||
converting_key_type::reset_counts();
|
||||
converting_value_type::reset_counts();
|
||||
}
|
||||
|
||||
using test::smf_count;
|
||||
|
||||
template <class T> smf_count count_for()
|
||||
{
|
||||
return test::smf_count{
|
||||
(int)T::default_constructor.load(std::memory_order_relaxed),
|
||||
(int)T::copy_constructor.load(std::memory_order_relaxed),
|
||||
(int)T::move_constructor.load(std::memory_order_relaxed),
|
||||
(int)T::copy_assignment.load(std::memory_order_relaxed),
|
||||
(int)T::move_assignment.load(std::memory_order_relaxed),
|
||||
(int)T::destructor.load(std::memory_order_relaxed)};
|
||||
}
|
||||
|
||||
enum emplace_kind
|
||||
{
|
||||
copy,
|
||||
move
|
||||
};
|
||||
|
||||
enum emplace_status
|
||||
{
|
||||
fail,
|
||||
success
|
||||
};
|
||||
|
||||
struct counted_key_checker_type
|
||||
{
|
||||
using key_type = counted_key_type;
|
||||
void operator()(emplace_kind kind, emplace_status status)
|
||||
{
|
||||
int copies = (kind == copy && status == success) ? 1 : 0;
|
||||
int moves = (kind == move && status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(
|
||||
count_for<counted_key_type>(), (smf_count{0, copies, moves, 0, 0, 0}));
|
||||
}
|
||||
} counted_key_checker;
|
||||
|
||||
struct converting_key_checker_type
|
||||
{
|
||||
using key_type = converting_key_type;
|
||||
void operator()(emplace_kind, emplace_status status)
|
||||
{
|
||||
int moves = (status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(
|
||||
count_for<counted_key_type>(), (smf_count{1, 0, moves, 0, 0, 1}));
|
||||
}
|
||||
} converting_key_checker;
|
||||
|
||||
struct counted_value_checker_type
|
||||
{
|
||||
using mapped_type = counted_value_type;
|
||||
void operator()(emplace_kind kind, emplace_status status)
|
||||
{
|
||||
int copies = (kind == copy && status == success) ? 1 : 0;
|
||||
int moves = (kind == move && status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(count_for<counted_value_type>(),
|
||||
(smf_count{0, copies, moves, 0, 0, 0}));
|
||||
}
|
||||
} counted_value_checker;
|
||||
|
||||
struct converting_value_checker_type
|
||||
{
|
||||
using mapped_type = converting_value_type;
|
||||
void operator()(emplace_kind, emplace_status status)
|
||||
{
|
||||
int ctors = (status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(
|
||||
count_for<counted_value_type>(), (smf_count{ctors, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
} converting_value_checker;
|
||||
|
||||
template <class X, class KC, class VC>
|
||||
void emplace_map_key_value(
|
||||
X*, emplace_kind kind, KC key_checker, VC value_checker)
|
||||
{
|
||||
using container = X;
|
||||
using key_type = typename KC::key_type;
|
||||
using mapped_type = typename VC::mapped_type;
|
||||
|
||||
container x;
|
||||
key_type key{};
|
||||
key_type key2 = key;
|
||||
mapped_type value{};
|
||||
mapped_type value2 = value;
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = (kind == copy) ? x.emplace(key, value)
|
||||
: x.emplace(std::move(key), std::move(value));
|
||||
BOOST_TEST_EQ(ret, true);
|
||||
key_checker(kind, success);
|
||||
value_checker(kind, success);
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_key_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_value_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
bool ret = x.emplace(key2, value2);
|
||||
BOOST_TEST_EQ(ret, false);
|
||||
key_checker(kind, fail);
|
||||
value_checker(kind, fail);
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_key_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_value_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
bool ret = x.emplace(std::move(key2), std::move(value2));
|
||||
BOOST_TEST_EQ(ret, false);
|
||||
key_checker(kind, fail);
|
||||
value_checker(kind, fail);
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_key_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(
|
||||
count_for<converting_value_type>(), (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<counted_key_type, counted_value_type>*
|
||||
test_counted_flat_map = {};
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace_map_key_value,
|
||||
((test_counted_flat_map))
|
||||
((copy)(move))
|
||||
((counted_key_checker)(converting_key_checker))
|
||||
((counted_value_checker)(converting_value_checker))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -33,7 +33,7 @@ static std::size_t const num_threads =
|
||||
|
||||
std::atomic_bool should_throw{false};
|
||||
|
||||
constexpr std::uint32_t throw_threshold = 2500;
|
||||
constexpr std::uint32_t throw_threshold = 2300;
|
||||
constexpr std::uint32_t alloc_throw_threshold = 10;
|
||||
|
||||
void enable_exceptions() { should_throw = true; }
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include "../unordered/explicit_alloc_ctor_tests.cpp"
|
||||
+46
-23
@@ -1,5 +1,6 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
|
||||
@@ -207,7 +208,8 @@ template <class T> struct stateful_allocator2
|
||||
bool operator!=(stateful_allocator2 const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
struct raii
|
||||
template <class Tag>
|
||||
struct basic_raii
|
||||
{
|
||||
static std::atomic<std::uint32_t> default_constructor;
|
||||
static std::atomic<std::uint32_t> copy_constructor;
|
||||
@@ -219,17 +221,17 @@ struct raii
|
||||
|
||||
int x_ = -1;
|
||||
|
||||
raii() { ++default_constructor; }
|
||||
raii(int const x) : x_{x} { ++default_constructor; }
|
||||
raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; }
|
||||
raii(raii&& rhs) noexcept : x_{rhs.x_}
|
||||
basic_raii() { ++default_constructor; }
|
||||
basic_raii(int const x) : x_{x} { ++default_constructor; }
|
||||
basic_raii(basic_raii const& rhs) : x_{rhs.x_} { ++copy_constructor; }
|
||||
basic_raii(basic_raii&& rhs) noexcept : x_{rhs.x_}
|
||||
{
|
||||
rhs.x_ = -1;
|
||||
++move_constructor;
|
||||
}
|
||||
~raii() { ++destructor; }
|
||||
~basic_raii() { ++destructor; }
|
||||
|
||||
raii& operator=(raii const& rhs)
|
||||
basic_raii& operator=(basic_raii const& rhs)
|
||||
{
|
||||
++copy_assignment;
|
||||
if (this != &rhs) {
|
||||
@@ -238,7 +240,7 @@ struct raii
|
||||
return *this;
|
||||
}
|
||||
|
||||
raii& operator=(raii&& rhs) noexcept
|
||||
basic_raii& operator=(basic_raii&& rhs) noexcept
|
||||
{
|
||||
++move_assignment;
|
||||
if (this != &rhs) {
|
||||
@@ -248,37 +250,37 @@ struct raii
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==(raii const& lhs, raii const& rhs)
|
||||
friend bool operator==(basic_raii const& lhs, basic_raii const& rhs)
|
||||
{
|
||||
return lhs.x_ == rhs.x_;
|
||||
}
|
||||
|
||||
friend bool operator!=(raii const& lhs, raii const& rhs)
|
||||
friend bool operator!=(basic_raii const& lhs, basic_raii const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend bool operator==(raii const& lhs, int const x) { return lhs.x_ == x; }
|
||||
friend bool operator!=(raii const& lhs, int const x)
|
||||
friend bool operator==(basic_raii const& lhs, int const x) { return lhs.x_ == x; }
|
||||
friend bool operator!=(basic_raii const& lhs, int const x)
|
||||
{
|
||||
return !(lhs.x_ == x);
|
||||
}
|
||||
|
||||
friend bool operator==(int const x, raii const& rhs) { return rhs.x_ == x; }
|
||||
friend bool operator==(int const x, basic_raii const& rhs) { return rhs.x_ == x; }
|
||||
|
||||
friend bool operator!=(int const x, raii const& rhs)
|
||||
friend bool operator!=(int const x, basic_raii const& rhs)
|
||||
{
|
||||
return !(rhs.x_ == x);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, raii const& rhs)
|
||||
friend std::ostream& operator<<(std::ostream& os, basic_raii const& rhs)
|
||||
{
|
||||
os << "{ x_: " << rhs.x_ << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& os, std::pair<raii const, raii> const& rhs)
|
||||
std::ostream& os, std::pair<basic_raii const, basic_raii> const& rhs)
|
||||
{
|
||||
os << "pair<" << rhs.first << ", " << rhs.second << ">";
|
||||
return os;
|
||||
@@ -294,16 +296,30 @@ struct raii
|
||||
move_assignment = 0;
|
||||
}
|
||||
|
||||
friend void swap(raii& lhs, raii& rhs) { std::swap(lhs.x_, rhs.x_); }
|
||||
friend void swap(basic_raii& lhs, basic_raii& rhs) { std::swap(lhs.x_, rhs.x_); }
|
||||
};
|
||||
|
||||
std::atomic<std::uint32_t> raii::default_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::copy_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::move_constructor{0};
|
||||
std::atomic<std::uint32_t> raii::destructor{0};
|
||||
std::atomic<std::uint32_t> raii::copy_assignment{0};
|
||||
std::atomic<std::uint32_t> raii::move_assignment{0};
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::default_constructor(0);
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::copy_constructor(0);
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::move_constructor(0);
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::destructor(0);
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::copy_assignment(0);
|
||||
template <class Tag> std::atomic<std::uint32_t> basic_raii<Tag>::move_assignment(0);
|
||||
|
||||
struct raii_tag_
|
||||
{
|
||||
};
|
||||
class raii : public basic_raii<raii_tag_>
|
||||
{
|
||||
using basic_raii::basic_raii;
|
||||
};
|
||||
|
||||
template <class Tag>
|
||||
std::size_t hash_value(basic_raii<Tag> const& r) noexcept
|
||||
{
|
||||
boost::hash<int> hasher;
|
||||
return hasher(r.x_);
|
||||
}
|
||||
std::size_t hash_value(raii const& r) noexcept
|
||||
{
|
||||
boost::hash<int> hasher;
|
||||
@@ -311,6 +327,13 @@ std::size_t hash_value(raii const& r) noexcept
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <class Tag> struct hash<basic_raii<Tag>>
|
||||
{
|
||||
std::size_t operator()(basic_raii<Tag> const& r) const noexcept
|
||||
{
|
||||
return hash_value(r);
|
||||
}
|
||||
};
|
||||
template <> struct hash<raii>
|
||||
{
|
||||
std::size_t operator()(raii const& r) const noexcept
|
||||
|
||||
@@ -156,6 +156,9 @@ namespace {
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
|
||||
// some versions of old gcc have trouble eliding copies here
|
||||
// https://godbolt.org/z/Ebo6TbvaG
|
||||
#elif BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000)
|
||||
// seemingly same problem, though the snippet above does not reveal it
|
||||
#else
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
#endif
|
||||
@@ -488,8 +491,10 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::default_constructor, value_type_cardinality * values2.size());
|
||||
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
|
||||
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
|
||||
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
|
||||
// skip test
|
||||
#else
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
@@ -525,8 +530,10 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::default_constructor, value_type_cardinality * values2.size());
|
||||
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
|
||||
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
|
||||
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
|
||||
// skip test
|
||||
#else
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
|
||||
+111
-1
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright 2008-2009 Daniel James.
|
||||
// Copyright 2024 Braden Ganetsky.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +8,7 @@
|
||||
#define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
|
||||
namespace test {
|
||||
struct object_count
|
||||
@@ -84,6 +86,114 @@ namespace test {
|
||||
return global_object_count.constructions - constructions_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct smf_count
|
||||
{
|
||||
int default_constructions = 0;
|
||||
int copy_constructions = 0;
|
||||
int move_constructions = 0;
|
||||
int copy_assignments = 0;
|
||||
int move_assignments = 0;
|
||||
int destructions = 0;
|
||||
|
||||
#if (BOOST_CXX_VERSION < 201402L) || (defined(_MSC_VER) && _MSC_VER < 1910)
|
||||
smf_count() = default;
|
||||
|
||||
smf_count(int default_constructions_, int copy_constructions_,
|
||||
int move_constructions_, int copy_assignments_, int move_assignments_,
|
||||
int destructions_)
|
||||
: default_constructions(default_constructions_),
|
||||
copy_constructions(copy_constructions_),
|
||||
move_constructions(move_constructions_),
|
||||
copy_assignments(copy_assignments_),
|
||||
move_assignments(move_assignments_), destructions(destructions_)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void reset() { *this = smf_count(); }
|
||||
|
||||
void default_construct() { ++default_constructions; }
|
||||
void copy_construct() { ++copy_constructions; }
|
||||
void move_construct() { ++move_constructions; }
|
||||
void copy_assign() { ++copy_assignments; }
|
||||
void move_assign() { ++move_assignments; }
|
||||
void destruct() { ++destructions; }
|
||||
|
||||
friend bool operator==(smf_count const& lhs, smf_count const& rhs)
|
||||
{
|
||||
return lhs.default_constructions == rhs.default_constructions &&
|
||||
lhs.copy_constructions == rhs.copy_constructions &&
|
||||
lhs.move_constructions == rhs.move_constructions &&
|
||||
lhs.copy_assignments == rhs.copy_assignments &&
|
||||
lhs.move_assignments == rhs.move_assignments &&
|
||||
lhs.destructions == rhs.destructions;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, smf_count const& c)
|
||||
{
|
||||
out << "[default_constructions: " << c.default_constructions
|
||||
<< ", copy_constructions: " << c.copy_constructions
|
||||
<< ", move_constructions: " << c.move_constructions
|
||||
<< ", copy_assignments: " << c.copy_assignments
|
||||
<< ", move_assignments: " << c.move_assignments
|
||||
<< ", destructions: " << c.destructions << "]";
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Tag> class smf_counted_object
|
||||
{
|
||||
public:
|
||||
static smf_count count;
|
||||
static void reset_count() { count.reset(); }
|
||||
|
||||
smf_counted_object(int index) : smf_counted_object() { index_ = index; }
|
||||
|
||||
smf_counted_object() : index_(++running_index)
|
||||
{
|
||||
count.default_construct();
|
||||
}
|
||||
smf_counted_object(smf_counted_object const& rhs) : index_(rhs.index_)
|
||||
{
|
||||
count.copy_construct();
|
||||
}
|
||||
smf_counted_object(smf_counted_object&& rhs) noexcept : index_(rhs.index_)
|
||||
{
|
||||
count.move_construct();
|
||||
}
|
||||
smf_counted_object& operator=(smf_counted_object const& rhs)
|
||||
{
|
||||
count.copy_assign();
|
||||
index_ = rhs.index_;
|
||||
return *this;
|
||||
}
|
||||
smf_counted_object& operator=(smf_counted_object&& rhs) noexcept
|
||||
{
|
||||
count.move_assign();
|
||||
index_ = rhs.index_;
|
||||
return *this;
|
||||
}
|
||||
~smf_counted_object() { count.destruct(); }
|
||||
|
||||
friend bool operator==(
|
||||
smf_counted_object const& lhs, smf_counted_object const& rhs)
|
||||
{
|
||||
return lhs.index_ == rhs.index_;
|
||||
}
|
||||
|
||||
friend std::size_t hash_value(smf_counted_object const& x)
|
||||
{
|
||||
return boost::hash<int>()(x.index_);
|
||||
}
|
||||
|
||||
int index_;
|
||||
|
||||
private:
|
||||
static int running_index;
|
||||
};
|
||||
template <class Tag> smf_count smf_counted_object<Tag>::count = {};
|
||||
template <class Tag> int smf_counted_object<Tag>::running_index = 0;
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,4 +21,10 @@
|
||||
#include "postfix.hpp"
|
||||
// clang-format on
|
||||
|
||||
#if defined(BOOST_LIBSTDCXX_VERSION)
|
||||
#if BOOST_LIBSTDCXX_VERSION < 60000
|
||||
#define BOOST_UNORDERED_NO_INIT_TYPE_TESTS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
//
|
||||
// Copyright 2023-2024 Braden Ganetsky.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/count.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
namespace emplace_smf_tests {
|
||||
using test::smf_count;
|
||||
using test::smf_counted_object;
|
||||
|
||||
using converting_key = smf_counted_object<struct cvt_key_tag_>;
|
||||
using converting_value = smf_counted_object<struct cvt_value_tag_>;
|
||||
|
||||
class counted_key : public smf_counted_object<struct key_tag_>
|
||||
{
|
||||
public:
|
||||
using smf_counted_object::smf_counted_object;
|
||||
counted_key() = default;
|
||||
counted_key(const converting_key& k) : counted_key(k.index_) {}
|
||||
};
|
||||
class counted_value : public smf_counted_object<struct value_tag_>
|
||||
{
|
||||
public:
|
||||
using smf_counted_object::smf_counted_object;
|
||||
counted_value() = default;
|
||||
counted_value(const converting_value& v) : counted_value(v.index_) {}
|
||||
};
|
||||
|
||||
class immovable_key : public smf_counted_object<struct imm_key_tag_>
|
||||
{
|
||||
public:
|
||||
using smf_counted_object::smf_counted_object;
|
||||
immovable_key(immovable_key&&) = delete;
|
||||
immovable_key& operator=(immovable_key&&) = delete;
|
||||
};
|
||||
class immovable_value : public smf_counted_object<struct imm_value_tag_>
|
||||
{
|
||||
public:
|
||||
using smf_counted_object::smf_counted_object;
|
||||
immovable_value(immovable_value&&) = delete;
|
||||
immovable_value& operator=(immovable_value&&) = delete;
|
||||
};
|
||||
|
||||
void reset_counts()
|
||||
{
|
||||
counted_key::reset_count();
|
||||
counted_value::reset_count();
|
||||
converting_key::reset_count();
|
||||
converting_value::reset_count();
|
||||
immovable_key::reset_count();
|
||||
immovable_value::reset_count();
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_flat_map<counted_key, counted_value>* test_smf_map;
|
||||
static boost::unordered_node_map<counted_key, counted_value>*
|
||||
test_smf_node_map;
|
||||
static boost::unordered_flat_set<counted_value>* test_smf_set;
|
||||
static boost::unordered_node_set<counted_value>* test_smf_node_set;
|
||||
#define EMPLACE_SMF_TESTS_MAP_ARGS ((test_smf_map)(test_smf_node_map))
|
||||
#define EMPLACE_SMF_TESTS_SET_ARGS ((test_smf_set)(test_smf_node_set))
|
||||
#else
|
||||
static boost::unordered_map<counted_key, counted_value>* test_smf_map;
|
||||
static boost::unordered_set<counted_value>* test_smf_set;
|
||||
#define EMPLACE_SMF_TESTS_MAP_ARGS ((test_smf_map))
|
||||
#define EMPLACE_SMF_TESTS_SET_ARGS ((test_smf_set))
|
||||
#endif
|
||||
|
||||
template <class X> static void emplace_smf_value_type_map(X*)
|
||||
{
|
||||
using container = X;
|
||||
using value_type = typename container::value_type;
|
||||
|
||||
container x;
|
||||
|
||||
{
|
||||
value_type val{counted_key{}, counted_value{}};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(val);
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
value_type val{counted_key{}, counted_value{}};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(std::move(val));
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(value_type{counted_key{}, counted_value{}});
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{1, 1, 1, 0, 0, 2}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||
}
|
||||
|
||||
{
|
||||
counted_key key{};
|
||||
counted_value value{};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(value_type{std::move(key), std::move(value)});
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 1, 0, 0, 1}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_TEST(emplace_smf_value_type_map, EMPLACE_SMF_TESTS_MAP_ARGS)
|
||||
|
||||
template <class X> static void emplace_smf_init_type_map(X*)
|
||||
{
|
||||
using container = X;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
using init_type = typename container::init_type;
|
||||
#else
|
||||
using raw_key =
|
||||
typename std::remove_const<typename container::key_type>::type;
|
||||
using init_type = std::pair<raw_key, typename container::mapped_type>;
|
||||
#endif
|
||||
|
||||
container x;
|
||||
|
||||
{
|
||||
init_type val{counted_key{}, counted_value{}};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(val);
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
init_type val{counted_key{}, counted_value{}};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(std::move(val));
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(init_type{counted_key{}, counted_value{}});
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 2, 0, 0, 2}));
|
||||
}
|
||||
|
||||
{
|
||||
counted_key key{};
|
||||
counted_value value{};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(init_type{std::move(key), std::move(value)});
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 2, 0, 0, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_TEST(emplace_smf_init_type_map, EMPLACE_SMF_TESTS_MAP_ARGS)
|
||||
|
||||
template <class X> static void emplace_smf_value_type_set(X*)
|
||||
{
|
||||
using container = X;
|
||||
using value_type = typename container::value_type;
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_same<value_type, typename container::init_type>::value);
|
||||
#endif
|
||||
BOOST_STATIC_ASSERT(std::is_same<value_type, counted_value>::value);
|
||||
|
||||
container x;
|
||||
|
||||
{
|
||||
counted_value val{};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(val);
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 1, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
counted_value val{};
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(std::move(val));
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{0, 0, 1, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
x.clear();
|
||||
reset_counts();
|
||||
x.emplace(counted_value{});
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{1, 0, 1, 0, 0, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_TEST(emplace_smf_value_type_set, EMPLACE_SMF_TESTS_SET_ARGS)
|
||||
|
||||
enum emplace_kind
|
||||
{
|
||||
copy,
|
||||
move
|
||||
};
|
||||
|
||||
enum emplace_status
|
||||
{
|
||||
fail,
|
||||
success
|
||||
};
|
||||
|
||||
struct counted_key_checker_type
|
||||
{
|
||||
using key_type = counted_key;
|
||||
void operator()(emplace_kind kind, emplace_status status)
|
||||
{
|
||||
int copies = (kind == copy && status == success) ? 1 : 0;
|
||||
int moves = (kind == move && status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{0, copies, moves, 0, 0, 0}));
|
||||
}
|
||||
} counted_key_checker;
|
||||
|
||||
struct converting_key_checker_type
|
||||
{
|
||||
using key_type = converting_key;
|
||||
void operator()(emplace_kind, emplace_status status)
|
||||
{
|
||||
int moves = (status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(counted_key::count, (smf_count{1, 0, moves, 0, 0, 1}));
|
||||
}
|
||||
} converting_key_checker;
|
||||
|
||||
struct counted_value_checker_type
|
||||
{
|
||||
using mapped_type = counted_value;
|
||||
void operator()(emplace_kind kind, emplace_status status)
|
||||
{
|
||||
int copies = (kind == copy && status == success) ? 1 : 0;
|
||||
int moves = (kind == move && status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(
|
||||
counted_value::count, (smf_count{0, copies, moves, 0, 0, 0}));
|
||||
}
|
||||
} counted_value_checker;
|
||||
|
||||
struct converting_value_checker_type
|
||||
{
|
||||
using mapped_type = converting_value;
|
||||
void operator()(emplace_kind, emplace_status status)
|
||||
{
|
||||
int ctors = (status == success) ? 1 : 0;
|
||||
BOOST_TEST_EQ(counted_value::count, (smf_count{ctors, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
} converting_value_checker;
|
||||
|
||||
template <class X, class KC, class VC>
|
||||
static void emplace_smf_key_value_map(
|
||||
X*, emplace_kind kind, KC key_checker, VC value_checker)
|
||||
{
|
||||
using container = X;
|
||||
using key_type = typename KC::key_type;
|
||||
using mapped_type = typename VC::mapped_type;
|
||||
|
||||
container x;
|
||||
key_type key{};
|
||||
key_type key2 = key;
|
||||
mapped_type value{};
|
||||
mapped_type value2 = value;
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = (kind == copy) ? x.emplace(key, value)
|
||||
: x.emplace(std::move(key), std::move(value));
|
||||
BOOST_TEST_EQ(ret.second, true);
|
||||
key_checker(kind, success);
|
||||
value_checker(kind, success);
|
||||
BOOST_TEST_EQ(converting_key::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(converting_value::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = x.emplace(key2, value2);
|
||||
BOOST_TEST_EQ(ret.second, false);
|
||||
key_checker(kind, fail);
|
||||
value_checker(kind, fail);
|
||||
BOOST_TEST_EQ(converting_key::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(converting_value::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = x.emplace(std::move(key2), std::move(value2));
|
||||
BOOST_TEST_EQ(ret.second, false);
|
||||
key_checker(kind, fail);
|
||||
value_checker(kind, fail);
|
||||
BOOST_TEST_EQ(converting_key::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(converting_value::count, (smf_count{0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace_smf_key_value_map,
|
||||
EMPLACE_SMF_TESTS_MAP_ARGS
|
||||
((copy)(move))
|
||||
((counted_key_checker)(converting_key_checker))
|
||||
((counted_value_checker)(converting_value_checker))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
template <class X> static void emplace_smf_key_value_map_immovable_key(X*)
|
||||
{
|
||||
#ifndef BOOST_UNORDERED_NO_INIT_TYPE_TESTS
|
||||
using container = X;
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_same<immovable_key, typename X::key_type>::value);
|
||||
using mapped_type = typename X::mapped_type;
|
||||
|
||||
container x;
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = x.emplace(0, 0);
|
||||
BOOST_TEST_EQ(ret.second, true);
|
||||
BOOST_TEST_EQ(immovable_key::count, (smf_count{1, 0, 0, 0, 0, 0}));
|
||||
BOOST_TEST_EQ(mapped_type::count, (smf_count{1, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
{
|
||||
reset_counts();
|
||||
auto ret = x.emplace(0, 1);
|
||||
BOOST_TEST_EQ(ret.second, false);
|
||||
BOOST_TEST_EQ(immovable_key::count, (smf_count{1, 0, 0, 0, 0, 1}));
|
||||
BOOST_TEST_EQ(mapped_type::count, (smf_count{1, 0, 0, 0, 0, 1}));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
static boost::unordered_node_map<immovable_key, counted_value>*
|
||||
test_smf_node_map_immovable_key_counted_value;
|
||||
static boost::unordered_node_map<immovable_key, immovable_value>*
|
||||
test_smf_node_map_immovable_key_immovable_value;
|
||||
#define EMPLACE_SMF_TESTS_MAP_IMMOVABLE_ARGS \
|
||||
((test_smf_node_map_immovable_key_counted_value)(test_smf_node_map_immovable_key_immovable_value))
|
||||
#else
|
||||
static boost::unordered_map<immovable_key, counted_value>*
|
||||
test_smf_map_immovable_key_counted_value;
|
||||
static boost::unordered_map<immovable_key, immovable_value>*
|
||||
test_smf_map_immovable_key_immovable_value;
|
||||
#define EMPLACE_SMF_TESTS_MAP_IMMOVABLE_ARGS \
|
||||
((test_smf_map_immovable_key_counted_value)(test_smf_map_immovable_key_immovable_value))
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace_smf_key_value_map_immovable_key,
|
||||
EMPLACE_SMF_TESTS_MAP_IMMOVABLE_ARGS
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace emplace_smf_tests
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
#endif
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
template <class T> struct explicit_allocator
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
explicit_allocator() {}
|
||||
|
||||
template <class U>
|
||||
explicit explicit_allocator(const explicit_allocator<U>&) {}
|
||||
|
||||
template<class U>
|
||||
bool operator==(explicit_allocator<U> const &) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
bool operator!=(explicit_allocator<U> const &) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return std::allocator<T>().allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n)
|
||||
{
|
||||
std::allocator<T>().deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Container,typename = void> struct has_extract:
|
||||
std::false_type {};
|
||||
|
||||
template<typename Container>
|
||||
struct has_extract<
|
||||
Container, boost::void_t<typename Container::insert_return_type>
|
||||
>: std::true_type {};
|
||||
|
||||
template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
Container&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
Container& c, std::true_type)
|
||||
{
|
||||
auto n = c.extract(c.begin());
|
||||
c.insert(std::move(n));
|
||||
n = c.extract(typename Container::key_type());
|
||||
c.insert(std::move(n));
|
||||
}
|
||||
|
||||
template <class Container> void test_explicit_alloc_ctor_extract(Container& c)
|
||||
{
|
||||
test_explicit_alloc_ctor_extract(c, has_extract<Container>());
|
||||
}
|
||||
|
||||
template <class Container> void test_explicit_alloc_ctor()
|
||||
{
|
||||
using key_type = typename Container::key_type;
|
||||
using value_type = typename Container::value_type;
|
||||
using hasher = typename Container::hasher;
|
||||
using allocator_type = typename Container::allocator_type;
|
||||
|
||||
allocator_type al;
|
||||
std::initializer_list<value_type> il{};
|
||||
hasher h;
|
||||
std::vector<value_type> v;
|
||||
Container c,
|
||||
c2(0),
|
||||
c3(v.begin(), v.end()),
|
||||
c4(c3),
|
||||
c5(std::move(c4)),
|
||||
c6(v.begin(), v.end(), al),
|
||||
c7(al),
|
||||
c8(c7, al),
|
||||
c9(std::move(c8), al),
|
||||
c10(il),
|
||||
c11(0, al),
|
||||
c12(0, h, al),
|
||||
c13(v.begin(), v.end(), 0, al),
|
||||
c14(v.begin(), v.end(), 0, h, al),
|
||||
c15(il, al),
|
||||
c16(il, 0, al),
|
||||
c17(il, 0, h, al);
|
||||
value_type x{};
|
||||
key_type k{};
|
||||
|
||||
c = c2;
|
||||
c = std::move(c2);
|
||||
c = il;
|
||||
c.swap(c3);
|
||||
c.insert(x);
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
(void) c.visit(k, [](const value_type&) {});
|
||||
#else
|
||||
(void) c.find(k);
|
||||
#endif
|
||||
test_explicit_alloc_ctor_extract(c);
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (explicit_alloc_ctor) {
|
||||
#if defined(BOOST_UNORDERED_CFOA_TESTS)
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_explicit_alloc_ctor<boost::unordered_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_node_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
#else
|
||||
test_explicit_alloc_ctor<boost::unordered_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_multimap<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
test_explicit_alloc_ctor<boost::unordered_multiset<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
#endif
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -112,6 +113,7 @@ namespace find_tests {
|
||||
typedef typename test::random_values<X>::iterator value_iterator;
|
||||
test::random_values<X> v(500, generator);
|
||||
X x(v.begin(), v.end());
|
||||
X const& x_const = x;
|
||||
|
||||
compatible_hash h;
|
||||
compatible_predicate eq;
|
||||
@@ -119,6 +121,7 @@ namespace find_tests {
|
||||
for (value_iterator it = v.begin(), end = v.end(); it != end; ++it) {
|
||||
typename X::key_type key = test::get_key<X>(*it);
|
||||
BOOST_TEST(x.find(key) == x.find(compatible_key(key), h, eq));
|
||||
BOOST_TEST(x_const.find(key) == x_const.find(compatible_key(key), h, eq));
|
||||
}
|
||||
|
||||
test::random_values<X> v2(20, generator);
|
||||
@@ -126,6 +129,7 @@ namespace find_tests {
|
||||
for (value_iterator it = v2.begin(), end = v2.end(); it != end; ++it) {
|
||||
typename X::key_type key = test::get_key<X>(*it);
|
||||
BOOST_TEST(x.find(key) == x.find(compatible_key(key), h, eq));
|
||||
BOOST_TEST(x_const.find(key) == x_const.find(compatible_key(key), h, eq));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_LIBSTDCXX_VERSION)
|
||||
#if BOOST_LIBSTDCXX_VERSION < 60000
|
||||
#define BOOST_UNORDERED_NO_INIT_TYPE_TESTS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct move_only
|
||||
{
|
||||
int x_ = -1;
|
||||
|
||||
Reference in New Issue
Block a user