mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-11 13:34:06 +00:00
Merge branch 'feature/foa_fast_iteration' of https://github.com/boostorg/unordered into feature/foa_fast_iteration
This commit is contained in:
commit
7faec20f26
@ -6,7 +6,7 @@ local library = "unordered";
|
|||||||
|
|
||||||
local triggers =
|
local triggers =
|
||||||
{
|
{
|
||||||
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
|
branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ]
|
||||||
};
|
};
|
||||||
|
|
||||||
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -75,7 +75,7 @@ jobs:
|
|||||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
|
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
|
||||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
|
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
|
||||||
|
|
||||||
timeout-minutes: 120
|
timeout-minutes: 180
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
container: ${{matrix.container}}
|
container: ${{matrix.container}}
|
||||||
env: {B2_USE_CCACHE: 1}
|
env: {B2_USE_CCACHE: 1}
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363].
|
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363].
|
||||||
* Replaced the previous post-mixing process for open-addressing containers with
|
* Replaced the previous post-mixing process for open-addressing containers with
|
||||||
a new algorithm based on extended multiplication by a constant.
|
a new algorithm based on extended multiplication by a constant.
|
||||||
|
* Fixed bug in internal emplace() impl where stack-local types were not properly
|
||||||
|
constructed using the Allocator of the container which breaks uses-allocator
|
||||||
|
construction.
|
||||||
|
|
||||||
== Release 1.81.0 - Major update
|
== Release 1.81.0 - Major update
|
||||||
|
|
||||||
|
@ -1186,6 +1186,14 @@ _STL_RESTORE_DEPRECATED_WARNING
|
|||||||
*/
|
*/
|
||||||
constexpr static float const mlf = 0.875f;
|
constexpr static float const mlf = 0.875f;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
union uninitialized_storage
|
||||||
|
{
|
||||||
|
T t_;
|
||||||
|
uninitialized_storage(){}
|
||||||
|
~uninitialized_storage(){}
|
||||||
|
};
|
||||||
|
|
||||||
/* foa::table interface departs in a number of ways from that of C++ unordered
|
/* foa::table interface departs in a number of ways from that of C++ unordered
|
||||||
* associative containers because it's not for end-user consumption
|
* associative containers because it's not for end-user consumption
|
||||||
* (boost::unordered_[flat|node]_[map|set]) wrappers complete it as
|
* (boost::unordered_[flat|node]_[map|set]) wrappers complete it as
|
||||||
@ -1220,11 +1228,17 @@ constexpr static float const mlf = 0.875f;
|
|||||||
* a copyable const std::string&&. foa::table::insert is extended to accept
|
* a copyable const std::string&&. foa::table::insert is extended to accept
|
||||||
* both init_type and value_type references.
|
* both init_type and value_type references.
|
||||||
*
|
*
|
||||||
* - TypePolicy::move(element_type&) returns a temporary object for value
|
* - TypePolicy::construct and TypePolicy::destroy are used for the
|
||||||
* transfer on rehashing, move copy/assignment, and merge. For flat map, this
|
* construction and destruction of the internal types: value_type, init_type
|
||||||
* object is a std::pair<Key&&,T&&>, which is generally cheaper to move
|
* and element_type.
|
||||||
* than std::pair<const Key,T>&& because of the constness in Key. For
|
*
|
||||||
* node-based tables, this is used to transfer ownership of pointer.
|
* - TypePolicy::move is used to provide move semantics for the internal
|
||||||
|
* types used by the container during rehashing and emplace. These types
|
||||||
|
* are init_type, value_type and emplace_type. During insertion, a
|
||||||
|
* stack-local type will be created based on the constructibility of the
|
||||||
|
* value_type and the supplied arguments. TypePolicy::move is used here
|
||||||
|
* for transfer of ownership. Similarly, TypePolicy::move is also used
|
||||||
|
* during rehashing when elements are moved to the new table.
|
||||||
*
|
*
|
||||||
* - TypePolicy::extract returns a const reference to the key part of
|
* - TypePolicy::extract returns a const reference to the key part of
|
||||||
* a value of type value_type, init_type, element_type or
|
* a value of type value_type, init_type, element_type or
|
||||||
@ -1475,18 +1489,25 @@ public:
|
|||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
|
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
|
||||||
{
|
{
|
||||||
/* We dispatch based on whether or not the value_type is constructible from
|
using emplace_type=typename std::conditional<
|
||||||
* an rvalue reference of the deduced emplace_type. We do this specifically
|
std::is_constructible<init_type,Args...>::value,
|
||||||
* for the case of the node-based containers. To this end, we're able to
|
init_type,
|
||||||
* avoid allocating a node when a duplicate element is attempted to be
|
value_type
|
||||||
* inserted. For immovable types, we instead dispatch to the routine that
|
>::type;
|
||||||
* unconditionally allocates via `type_policy::construct()`.
|
|
||||||
*/
|
using insert_type=typename std::conditional<
|
||||||
return emplace_value(
|
|
||||||
std::is_constructible<
|
std::is_constructible<
|
||||||
value_type,
|
value_type,emplace_type>::value,
|
||||||
emplace_type<Args...>&&>{},
|
emplace_type,element_type
|
||||||
std::forward<Args>(args)...);
|
>::type;
|
||||||
|
|
||||||
|
uninitialized_storage<insert_type> s;
|
||||||
|
auto *p=std::addressof(s.t_);
|
||||||
|
|
||||||
|
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
destroy_on_exit<insert_type> guard{al(),p};
|
||||||
|
return emplace_impl(type_policy::move(*p));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key,typename... Args>
|
template<typename Key,typename... Args>
|
||||||
@ -1671,15 +1692,6 @@ private:
|
|||||||
template<typename,typename,typename,typename> friend class table;
|
template<typename,typename,typename,typename> friend class table;
|
||||||
using arrays_type=table_arrays<element_type,group_type,size_policy>;
|
using arrays_type=table_arrays<element_type,group_type,size_policy>;
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
using emplace_type = typename std::conditional<
|
|
||||||
std::is_constructible<
|
|
||||||
init_type,Args...
|
|
||||||
>::value,
|
|
||||||
init_type,
|
|
||||||
value_type
|
|
||||||
>::type;
|
|
||||||
|
|
||||||
struct clear_on_exit
|
struct clear_on_exit
|
||||||
{
|
{
|
||||||
~clear_on_exit(){x.clear();}
|
~clear_on_exit(){x.clear();}
|
||||||
@ -1698,6 +1710,14 @@ private:
|
|||||||
bool rollback_=false;
|
bool rollback_=false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct destroy_on_exit
|
||||||
|
{
|
||||||
|
Allocator &a;
|
||||||
|
T *p;
|
||||||
|
~destroy_on_exit(){type_policy::destroy(a,p);};
|
||||||
|
};
|
||||||
|
|
||||||
Hash& h(){return hash_base::get();}
|
Hash& h(){return hash_base::get();}
|
||||||
const Hash& h()const{return hash_base::get();}
|
const Hash& h()const{return hash_base::get();}
|
||||||
Pred& pred(){return pred_base::get();}
|
Pred& pred(){return pred_base::get();}
|
||||||
@ -1961,29 +1981,6 @@ private:
|
|||||||
#pragma warning(pop) /* C4800 */
|
#pragma warning(pop) /* C4800 */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
|
|
||||||
std::true_type /* movable value_type */,Args&&... args
|
|
||||||
) {
|
|
||||||
using emplace_type_t = emplace_type<Args...>;
|
|
||||||
return emplace_impl(emplace_type_t(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
|
|
||||||
std::false_type /* immovable value_type */,Args&&... args
|
|
||||||
) {
|
|
||||||
alignas(element_type)
|
|
||||||
unsigned char buf[sizeof(element_type)];
|
|
||||||
element_type* p = reinterpret_cast<element_type*>(buf);
|
|
||||||
|
|
||||||
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
|
||||||
destroy_element_on_exit d{this,p};
|
|
||||||
(void)d;
|
|
||||||
|
|
||||||
return emplace_impl(type_policy::move(*p));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
|
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
|
||||||
{
|
{
|
||||||
|
60
include/boost/unordered/detail/foa/element_type.hpp
Normal file
60
include/boost/unordered/detail/foa/element_type.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* Copyright 2023 Christian Mazakas.
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
* http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* See https://www.boost.org/libs/unordered for library home page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||||
|
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||||
|
|
||||||
|
namespace boost{
|
||||||
|
namespace unordered{
|
||||||
|
namespace detail{
|
||||||
|
namespace foa{
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct element_type
|
||||||
|
{
|
||||||
|
using value_type=T;
|
||||||
|
value_type* p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we use a deleted copy constructor here so the type is no longer
|
||||||
|
* trivially copy-constructible which inhibits our memcpy
|
||||||
|
* optimizations when copying the tables
|
||||||
|
*/
|
||||||
|
element_type() = default;
|
||||||
|
element_type(value_type* p_):p(p_){}
|
||||||
|
element_type(element_type const&) = delete;
|
||||||
|
element_type(element_type&& rhs) noexcept
|
||||||
|
{
|
||||||
|
p = rhs.p;
|
||||||
|
rhs.p = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
element_type& operator=(element_type const&)=delete;
|
||||||
|
element_type& operator=(element_type&& rhs)noexcept
|
||||||
|
{
|
||||||
|
if (this!=&rhs){
|
||||||
|
p=rhs.p;
|
||||||
|
rhs.p=nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(element_type& rhs)noexcept
|
||||||
|
{
|
||||||
|
auto tmp=p;
|
||||||
|
p=rhs.p;
|
||||||
|
rhs.p=tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
@ -45,20 +45,30 @@ struct node_handle_base
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
using node_value_type=typename type_policy::value_type;
|
using node_value_type=typename type_policy::value_type;
|
||||||
node_value_type* p_=nullptr;
|
element_type p_;
|
||||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
|
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
node_value_type& element()noexcept
|
node_value_type& data()noexcept
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!empty());
|
return *(p_.p);
|
||||||
return *p_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node_value_type const& element()const noexcept
|
node_value_type const& data()const noexcept
|
||||||
|
{
|
||||||
|
return *(p_.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
element_type& element()noexcept
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!empty());
|
BOOST_ASSERT(!empty());
|
||||||
return *p_;
|
return p_;
|
||||||
|
}
|
||||||
|
|
||||||
|
element_type const& element()const noexcept
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!empty());
|
||||||
|
return p_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator& al()noexcept
|
Allocator& al()noexcept
|
||||||
@ -73,32 +83,29 @@ struct node_handle_base
|
|||||||
return a_.t_;
|
return a_.t_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void emplace(node_value_type* p,Allocator a)
|
|
||||||
{
|
|
||||||
BOOST_ASSERT(empty());
|
|
||||||
p_=p;
|
|
||||||
new(&a_.t_)Allocator(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emplace(element_type&& x,Allocator a)
|
void emplace(element_type&& x,Allocator a)
|
||||||
{
|
{
|
||||||
emplace(x.p,a);
|
BOOST_ASSERT(empty());
|
||||||
|
auto* p=x.p;
|
||||||
|
p_.p=p;
|
||||||
|
new(&a_.t_)Allocator(a);
|
||||||
x.p=nullptr;
|
x.p=nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
al().~Allocator();
|
a_.t_.~Allocator();
|
||||||
p_=nullptr;
|
p_.p=nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr node_handle_base()noexcept{}
|
constexpr node_handle_base()noexcept:p_{nullptr}{}
|
||||||
|
|
||||||
node_handle_base(node_handle_base&& nh) noexcept
|
node_handle_base(node_handle_base&& nh) noexcept
|
||||||
{
|
{
|
||||||
|
p_.p = nullptr;
|
||||||
if (!nh.empty()){
|
if (!nh.empty()){
|
||||||
emplace(nh.p_,nh.al());
|
emplace(std::move(nh.p_),nh.al());
|
||||||
nh.reset();
|
nh.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,12 +117,12 @@ struct node_handle_base
|
|||||||
if(nh.empty()){ /* empty(), nh.empty() */
|
if(nh.empty()){ /* empty(), nh.empty() */
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
}else{ /* empty(), !nh.empty() */
|
}else{ /* empty(), !nh.empty() */
|
||||||
emplace(nh.p_,std::move(nh.al()));
|
emplace(std::move(nh.p_),std::move(nh.al()));
|
||||||
nh.reset();
|
nh.reset();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(nh.empty()){ /* !empty(), nh.empty() */
|
if(nh.empty()){ /* !empty(), nh.empty() */
|
||||||
type_policy::destroy(al(),p_);
|
type_policy::destroy(al(),&p_);
|
||||||
reset();
|
reset();
|
||||||
}else{ /* !empty(), !nh.empty() */
|
}else{ /* !empty(), !nh.empty() */
|
||||||
bool const pocma=
|
bool const pocma=
|
||||||
@ -124,12 +131,12 @@ struct node_handle_base
|
|||||||
|
|
||||||
BOOST_ASSERT(pocma||al()==nh.al());
|
BOOST_ASSERT(pocma||al()==nh.al());
|
||||||
|
|
||||||
type_policy::destroy(al(),p_);
|
type_policy::destroy(al(),&p_);
|
||||||
if(pocma){
|
if(pocma){
|
||||||
al()=std::move(nh.al());
|
al()=std::move(nh.al());
|
||||||
}
|
}
|
||||||
|
|
||||||
p_=nh.p_;
|
p_=std::move(nh.p_);
|
||||||
nh.reset();
|
nh.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +144,7 @@ struct node_handle_base
|
|||||||
if(empty()){ /* empty(), nh.empty() */
|
if(empty()){ /* empty(), nh.empty() */
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
}else{ /* !empty(), !nh.empty() */
|
}else{ /* !empty(), !nh.empty() */
|
||||||
type_policy::destroy(al(),p_);
|
type_policy::destroy(al(),&p_);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,14 +154,14 @@ struct node_handle_base
|
|||||||
~node_handle_base()
|
~node_handle_base()
|
||||||
{
|
{
|
||||||
if(!empty()){
|
if(!empty()){
|
||||||
type_policy::destroy(al(),p_);
|
type_policy::destroy(al(),&p_);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allocator_type get_allocator()const noexcept{return al();}
|
allocator_type get_allocator()const noexcept{return al();}
|
||||||
explicit operator bool()const noexcept{ return !empty();}
|
explicit operator bool()const noexcept{ return !empty();}
|
||||||
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_==nullptr;}
|
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
|
||||||
|
|
||||||
void swap(node_handle_base& nh) noexcept(
|
void swap(node_handle_base& nh) noexcept(
|
||||||
boost::allocator_is_always_equal<Allocator>::type::value||
|
boost::allocator_is_always_equal<Allocator>::type::value||
|
||||||
@ -165,12 +172,12 @@ struct node_handle_base
|
|||||||
if(nh.empty()) {
|
if(nh.empty()) {
|
||||||
/* nothing to do here */
|
/* nothing to do here */
|
||||||
} else {
|
} else {
|
||||||
emplace(nh.p_, nh.al());
|
emplace(std::move(nh.p_), nh.al());
|
||||||
nh.reset();
|
nh.reset();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(nh.empty()){
|
if(nh.empty()){
|
||||||
nh.emplace(p_,al());
|
nh.emplace(std::move(p_),al());
|
||||||
reset();
|
reset();
|
||||||
}else{
|
}else{
|
||||||
bool const pocs=
|
bool const pocs=
|
||||||
@ -180,7 +187,7 @@ struct node_handle_base
|
|||||||
BOOST_ASSERT(pocs || al()==nh.al());
|
BOOST_ASSERT(pocs || al()==nh.al());
|
||||||
|
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(p_,nh.p_);
|
p_.swap(nh.p_);
|
||||||
if(pocs)swap(al(),nh.al());
|
if(pocs)swap(al(),nh.al());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
include/boost/unordered/detail/requires_cxx11.hpp
Normal file
21
include/boost/unordered/detail/requires_cxx11.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||||
|
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||||
|
|
||||||
|
// Copyright 2023 Peter Dimov
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt
|
||||||
|
|
||||||
|
#include <boost/config.hpp>
|
||||||
|
#include <boost/config/pragma_message.hpp>
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
|
||||||
|
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
|
||||||
|
defined(BOOST_NO_CXX11_DECLTYPE) || \
|
||||||
|
defined(BOOST_NO_CXX11_CONSTEXPR) || \
|
||||||
|
defined(BOOST_NO_CXX11_NOEXCEPT)
|
||||||
|
|
||||||
|
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
@ -53,6 +53,11 @@ namespace boost {
|
|||||||
return kv.first;
|
return kv.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static moved_type move(init_type& x)
|
||||||
|
{
|
||||||
|
return {std::move(x.first), std::move(x.second)};
|
||||||
|
}
|
||||||
|
|
||||||
static moved_type move(element_type& x)
|
static moved_type move(element_type& x)
|
||||||
{
|
{
|
||||||
// TODO: we probably need to launder here
|
// TODO: we probably need to launder here
|
||||||
@ -61,12 +66,23 @@ namespace boost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class A, class... Args>
|
template <class A, class... Args>
|
||||||
static void construct(A& al, element_type* p, Args&&... args)
|
static void construct(A& al, init_type* p, Args&&... args)
|
||||||
{
|
{
|
||||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
template <class A, class... Args>
|
||||||
|
static void construct(A& al, value_type* p, Args&&... args)
|
||||||
|
{
|
||||||
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||||
|
{
|
||||||
|
boost::allocator_destroy(al, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||||
{
|
{
|
||||||
boost::allocator_destroy(al, p);
|
boost::allocator_destroy(al, p);
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,12 @@ namespace boost {
|
|||||||
static element_type&& move(element_type& x) { return std::move(x); }
|
static element_type&& move(element_type& x) { return std::move(x); }
|
||||||
|
|
||||||
template <class A, class... Args>
|
template <class A, class... Args>
|
||||||
static void construct(A& al, element_type* p, Args&&... args)
|
static void construct(A& al, value_type* p, Args&&... args)
|
||||||
{
|
{
|
||||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||||
{
|
{
|
||||||
boost::allocator_destroy(al, p);
|
boost::allocator_destroy(al, p);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||||
#include <boost/core/explicit_operator_bool.hpp>
|
#include <boost/core/explicit_operator_bool.hpp>
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/move/move.hpp>
|
#include <boost/move/move.hpp>
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/unordered/detail/foa.hpp>
|
#include <boost/unordered/detail/foa.hpp>
|
||||||
|
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||||
#include <boost/unordered/detail/type_traits.hpp>
|
#include <boost/unordered/detail/type_traits.hpp>
|
||||||
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
||||||
@ -45,23 +46,7 @@ namespace boost {
|
|||||||
using value_type = std::pair<Key const, T>;
|
using value_type = std::pair<Key const, T>;
|
||||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||||
|
|
||||||
struct element_type
|
using element_type=foa::element_type<value_type>;
|
||||||
{
|
|
||||||
value_type* p;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* we use a deleted copy constructor here so the type is no longer
|
|
||||||
* trivially copy-constructible which inhibits our memcpy
|
|
||||||
* optimizations when copying the tables
|
|
||||||
*/
|
|
||||||
element_type() = default;
|
|
||||||
element_type(element_type const&) = delete;
|
|
||||||
element_type(element_type&& rhs) noexcept
|
|
||||||
{
|
|
||||||
p = rhs.p;
|
|
||||||
rhs.p = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static value_type& value_from(element_type const& x) { return *(x.p); }
|
static value_type& value_from(element_type const& x) { return *(x.p); }
|
||||||
|
|
||||||
@ -77,6 +62,11 @@ namespace boost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static element_type&& move(element_type& x) { return std::move(x); }
|
static element_type&& move(element_type& x) { return std::move(x); }
|
||||||
|
static moved_type move(init_type& x)
|
||||||
|
{
|
||||||
|
return {std::move(x.first), std::move(x.second)};
|
||||||
|
}
|
||||||
|
|
||||||
static moved_type move(value_type& x)
|
static moved_type move(value_type& x)
|
||||||
{
|
{
|
||||||
return {std::move(const_cast<raw_key_type&>(x.first)),
|
return {std::move(const_cast<raw_key_type&>(x.first)),
|
||||||
@ -96,6 +86,18 @@ namespace boost {
|
|||||||
construct(al, p, *copy.p);
|
construct(al, p, *copy.p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class A, class... Args>
|
||||||
|
static void construct(A& al, init_type* p, Args&&... args)
|
||||||
|
{
|
||||||
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class... Args>
|
||||||
|
static void construct(A& al, value_type* p, Args&&... args)
|
||||||
|
{
|
||||||
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
template <class A, class... Args>
|
template <class A, class... Args>
|
||||||
static void construct(A& al, element_type* p, Args&&... args)
|
static void construct(A& al, element_type* p, Args&&... args)
|
||||||
{
|
{
|
||||||
@ -106,10 +108,11 @@ namespace boost {
|
|||||||
}
|
}
|
||||||
BOOST_CATCH(...)
|
BOOST_CATCH(...)
|
||||||
{
|
{
|
||||||
boost::allocator_deallocate(al,
|
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||||
boost::pointer_traits<
|
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
|
|
||||||
1);
|
boost::allocator_deallocate(
|
||||||
|
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||||
BOOST_RETHROW
|
BOOST_RETHROW
|
||||||
}
|
}
|
||||||
BOOST_CATCH_END
|
BOOST_CATCH_END
|
||||||
@ -118,16 +121,22 @@ namespace boost {
|
|||||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||||
{
|
{
|
||||||
boost::allocator_destroy(al, p);
|
boost::allocator_destroy(al, p);
|
||||||
boost::allocator_deallocate(al,
|
}
|
||||||
boost::pointer_traits<
|
|
||||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p),
|
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||||
1);
|
{
|
||||||
|
boost::allocator_destroy(al, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||||
{
|
{
|
||||||
if (p->p) {
|
if (p->p) {
|
||||||
destroy(al,p->p);
|
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||||
|
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||||
|
|
||||||
|
destroy(al, p->p);
|
||||||
|
boost::allocator_deallocate(
|
||||||
|
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -137,8 +146,7 @@ namespace boost {
|
|||||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
using base_type =
|
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||||
detail::foa::node_handle_base<TypePolicy, Allocator>;
|
|
||||||
|
|
||||||
using typename base_type::type_policy;
|
using typename base_type::type_policy;
|
||||||
|
|
||||||
@ -157,13 +165,13 @@ namespace boost {
|
|||||||
key_type& key() const
|
key_type& key() const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!this->empty());
|
BOOST_ASSERT(!this->empty());
|
||||||
return const_cast<key_type&>(this->element().first);
|
return const_cast<key_type&>(this->data().first);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped_type& mapped() const
|
mapped_type& mapped() const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!this->empty());
|
BOOST_ASSERT(!this->empty());
|
||||||
return const_cast<mapped_type&>(this->element().second);
|
return const_cast<mapped_type&>(this->data().second);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@ -402,10 +410,7 @@ namespace boost {
|
|||||||
|
|
||||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||||
|
|
||||||
typename map_types::element_type x;
|
auto itp = table_.insert(std::move(nh.element()));
|
||||||
x.p = std::addressof(nh.element());
|
|
||||||
|
|
||||||
auto itp = table_.insert(std::move(x));
|
|
||||||
if (itp.second) {
|
if (itp.second) {
|
||||||
nh.reset();
|
nh.reset();
|
||||||
return {itp.first, true, node_type{}};
|
return {itp.first, true, node_type{}};
|
||||||
@ -422,10 +427,7 @@ namespace boost {
|
|||||||
|
|
||||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||||
|
|
||||||
typename map_types::element_type x;
|
auto itp = table_.insert(std::move(nh.element()));
|
||||||
x.p = std::addressof(nh.element());
|
|
||||||
|
|
||||||
auto itp = table_.insert(std::move(x));
|
|
||||||
if (itp.second) {
|
if (itp.second) {
|
||||||
nh.reset();
|
nh.reset();
|
||||||
return itp.first;
|
return itp.first;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/unordered/detail/foa.hpp>
|
#include <boost/unordered/detail/foa.hpp>
|
||||||
|
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||||
#include <boost/unordered/detail/type_traits.hpp>
|
#include <boost/unordered/detail/type_traits.hpp>
|
||||||
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
||||||
@ -41,23 +42,7 @@ namespace boost {
|
|||||||
|
|
||||||
static Key const& extract(value_type const& key) { return key; }
|
static Key const& extract(value_type const& key) { return key; }
|
||||||
|
|
||||||
struct element_type
|
using element_type=foa::element_type<value_type>;
|
||||||
{
|
|
||||||
value_type* p;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* we use a deleted copy constructor here so the type is no longer
|
|
||||||
* trivially copy-constructible which inhibits our memcpy
|
|
||||||
* optimizations when copying the tables
|
|
||||||
*/
|
|
||||||
element_type() = default;
|
|
||||||
element_type(element_type const&) = delete;
|
|
||||||
element_type(element_type&& rhs) noexcept
|
|
||||||
{
|
|
||||||
p = rhs.p;
|
|
||||||
rhs.p = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||||
static Key const& extract(element_type const& k) { return *k.p; }
|
static Key const& extract(element_type const& k) { return *k.p; }
|
||||||
@ -78,6 +63,12 @@ namespace boost {
|
|||||||
x.p = nullptr;
|
x.p = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class A, class... Args>
|
||||||
|
static void construct(A& al, value_type* p, Args&&... args)
|
||||||
|
{
|
||||||
|
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
template <class A, class... Args>
|
template <class A, class... Args>
|
||||||
static void construct(A& al, element_type* p, Args&&... args)
|
static void construct(A& al, element_type* p, Args&&... args)
|
||||||
{
|
{
|
||||||
@ -100,16 +91,16 @@ namespace boost {
|
|||||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||||
{
|
{
|
||||||
boost::allocator_destroy(al, p);
|
boost::allocator_destroy(al, p);
|
||||||
boost::allocator_deallocate(al,
|
|
||||||
boost::pointer_traits<
|
|
||||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p),
|
|
||||||
1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||||
{
|
{
|
||||||
if (p->p) {
|
if (p->p) {
|
||||||
destroy(al, p->p);
|
destroy(al, p->p);
|
||||||
|
boost::allocator_deallocate(al,
|
||||||
|
boost::pointer_traits<typename boost::allocator_pointer<
|
||||||
|
A>::type>::pointer_to(*(p->p)),
|
||||||
|
1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -119,8 +110,7 @@ namespace boost {
|
|||||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
using base_type =
|
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||||
detail::foa::node_handle_base<TypePolicy, Allocator>;
|
|
||||||
|
|
||||||
using typename base_type::type_policy;
|
using typename base_type::type_policy;
|
||||||
|
|
||||||
@ -137,7 +127,7 @@ namespace boost {
|
|||||||
value_type& value() const
|
value_type& value() const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!this->empty());
|
BOOST_ASSERT(!this->empty());
|
||||||
return const_cast<value_type&>(this->element());
|
return const_cast<value_type&>(this->data());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@ -390,10 +380,7 @@ namespace boost {
|
|||||||
|
|
||||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||||
|
|
||||||
typename set_types::element_type x;
|
auto itp = table_.insert(std::move(nh.element()));
|
||||||
x.p=std::addressof(nh.element());
|
|
||||||
|
|
||||||
auto itp = table_.insert(std::move(x));
|
|
||||||
if (itp.second) {
|
if (itp.second) {
|
||||||
nh.reset();
|
nh.reset();
|
||||||
return {itp.first, true, node_type{}};
|
return {itp.first, true, node_type{}};
|
||||||
@ -410,10 +397,7 @@ namespace boost {
|
|||||||
|
|
||||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||||
|
|
||||||
typename set_types::element_type x;
|
auto itp = table_.insert(std::move(nh.element()));
|
||||||
x.p=std::addressof(nh.element());
|
|
||||||
|
|
||||||
auto itp = table_.insert(std::move(x));
|
|
||||||
if (itp.second) {
|
if (itp.second) {
|
||||||
nh.reset();
|
nh.reset();
|
||||||
return itp.first;
|
return itp.first;
|
||||||
@ -478,7 +462,7 @@ namespace boost {
|
|||||||
node_type extract(key_type const& key)
|
node_type extract(key_type const& key)
|
||||||
{
|
{
|
||||||
auto pos = find(key);
|
auto pos = find(key);
|
||||||
return pos!=end()?extract(pos):node_type();
|
return pos != end() ? extract(pos) : node_type();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class K>
|
template <class K>
|
||||||
@ -489,7 +473,7 @@ namespace boost {
|
|||||||
extract(K const& key)
|
extract(K const& key)
|
||||||
{
|
{
|
||||||
auto pos = find(key);
|
auto pos = find(key);
|
||||||
return pos!=end()?extract(pos):node_type();
|
return pos != end() ? extract(pos) : node_type();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class H2, class P2>
|
template <class H2, class P2>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||||
#include <boost/core/explicit_operator_bool.hpp>
|
#include <boost/core/explicit_operator_bool.hpp>
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/move/move.hpp>
|
#include <boost/move/move.hpp>
|
||||||
|
@ -144,6 +144,7 @@ build_foa init_type_insert_tests ;
|
|||||||
build_foa max_load_tests ;
|
build_foa max_load_tests ;
|
||||||
build_foa extract_tests ;
|
build_foa extract_tests ;
|
||||||
build_foa node_handle_tests ;
|
build_foa node_handle_tests ;
|
||||||
|
build_foa uses_allocator ;
|
||||||
|
|
||||||
run unordered/hash_is_avalanching_test.cpp ;
|
run unordered/hash_is_avalanching_test.cpp ;
|
||||||
|
|
||||||
|
189
test/unordered/uses_allocator.cpp
Normal file
189
test/unordered/uses_allocator.cpp
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
|
||||||
|
// Copyright 2023 Christian Mazakas.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#include "../helpers/test.hpp"
|
||||||
|
|
||||||
|
#include <boost/unordered/detail/implementation.hpp>
|
||||||
|
#include <boost/unordered/unordered_flat_map.hpp>
|
||||||
|
#include <boost/unordered/unordered_flat_set.hpp>
|
||||||
|
#include <boost/unordered/unordered_node_map.hpp>
|
||||||
|
#include <boost/unordered/unordered_node_set.hpp>
|
||||||
|
|
||||||
|
#include <boost/config.hpp>
|
||||||
|
#include <boost/config/pragma_message.hpp>
|
||||||
|
#include <boost/config/workaround.hpp>
|
||||||
|
|
||||||
|
#if BOOST_CXX_VERSION <= 199711L || \
|
||||||
|
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) || \
|
||||||
|
(defined(BOOST_LIBSTDCXX_VERSION) && BOOST_CXX_VERSION > 201703L) || \
|
||||||
|
(defined(BOOST_MSVC_FULL_VER) && BOOST_MSVC_FULL_VER >= 192000000 && \
|
||||||
|
BOOST_MSVC_FULL_VER < 193000000)
|
||||||
|
|
||||||
|
// automatically disable this test for C++03 builds so we can use the STL's
|
||||||
|
// scoped_allocator_adaptor
|
||||||
|
// we remove C++20 support for libstdc++ builds because of:
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108952
|
||||||
|
//
|
||||||
|
// msvc-14.2 w/ C++20 is similarly affected
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_PRAGMA_MESSAGE("uses_allocator tests require C++11, scoped_allocator")
|
||||||
|
|
||||||
|
int main() {}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <scoped_allocator>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template <class T> struct allocator
|
||||||
|
{
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
int tag_ = -1;
|
||||||
|
|
||||||
|
allocator() = default;
|
||||||
|
allocator(int tag) : tag_{tag} {}
|
||||||
|
allocator(allocator const&) = default;
|
||||||
|
allocator(allocator&&) = default;
|
||||||
|
|
||||||
|
template <class U> allocator(allocator<U> const& rhs) : tag_{rhs.tag_} {}
|
||||||
|
|
||||||
|
BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); }
|
||||||
|
|
||||||
|
allocator& operator=(allocator const& rhs)
|
||||||
|
{
|
||||||
|
tag_ = rhs.tag_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator& operator=(allocator&& rhs) noexcept
|
||||||
|
{
|
||||||
|
tag_ = rhs.tag_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(allocator const&) const { return true; }
|
||||||
|
bool operator!=(allocator const&) const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct raii_tracker
|
||||||
|
{
|
||||||
|
static int count;
|
||||||
|
static int copy_count;
|
||||||
|
static int move_count;
|
||||||
|
static int alloc_move_count;
|
||||||
|
|
||||||
|
using allocator_type = allocator<int>;
|
||||||
|
|
||||||
|
allocator_type a_;
|
||||||
|
|
||||||
|
raii_tracker(allocator_type a) : a_(a) { ++count; }
|
||||||
|
raii_tracker(int, allocator_type const& a) : a_(a) { ++count; }
|
||||||
|
|
||||||
|
raii_tracker(raii_tracker const&) { ++copy_count; }
|
||||||
|
raii_tracker(raii_tracker&&) noexcept { ++move_count; }
|
||||||
|
raii_tracker(raii_tracker&&, allocator_type const& a) noexcept : a_(a)
|
||||||
|
{
|
||||||
|
++alloc_move_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator_type get_allocator() const noexcept { return a_; }
|
||||||
|
|
||||||
|
friend bool operator==(raii_tracker const&, raii_tracker const&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int raii_tracker::count = 0;
|
||||||
|
int raii_tracker::copy_count = 0;
|
||||||
|
int raii_tracker::move_count = 0;
|
||||||
|
int raii_tracker::alloc_move_count = 0;
|
||||||
|
|
||||||
|
static void reset_counts()
|
||||||
|
{
|
||||||
|
raii_tracker::count = 0;
|
||||||
|
raii_tracker::copy_count = 0;
|
||||||
|
raii_tracker::move_count = 0;
|
||||||
|
raii_tracker::alloc_move_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t hash_value(raii_tracker const&) { return 0; }
|
||||||
|
|
||||||
|
using map_allocator_type = std::scoped_allocator_adaptor<
|
||||||
|
allocator<std::pair<raii_tracker const, raii_tracker> >, allocator<int> >;
|
||||||
|
|
||||||
|
using set_allocator_type =
|
||||||
|
std::scoped_allocator_adaptor<allocator<raii_tracker>, allocator<int> >;
|
||||||
|
|
||||||
|
using map_type = boost::unordered_flat_map<raii_tracker, raii_tracker,
|
||||||
|
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||||
|
|
||||||
|
using node_map_type = boost::unordered_node_map<raii_tracker, raii_tracker,
|
||||||
|
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||||
|
|
||||||
|
using set_type = boost::unordered_flat_set<raii_tracker,
|
||||||
|
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||||
|
|
||||||
|
using node_set_type = boost::unordered_node_set<raii_tracker,
|
||||||
|
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||||
|
|
||||||
|
map_type* flat_map;
|
||||||
|
node_map_type* node_map;
|
||||||
|
|
||||||
|
set_type* flat_set;
|
||||||
|
node_set_type* node_set;
|
||||||
|
|
||||||
|
template <class X> static void map_uses_allocator_construction(X*)
|
||||||
|
{
|
||||||
|
reset_counts();
|
||||||
|
|
||||||
|
map_allocator_type alloc(
|
||||||
|
allocator<std::pair<raii_tracker const, raii_tracker> >{12},
|
||||||
|
allocator<int>{34});
|
||||||
|
|
||||||
|
X map(1, alloc);
|
||||||
|
map.emplace(
|
||||||
|
std::piecewise_construct, std::make_tuple(1337), std::make_tuple(7331));
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(raii_tracker::count, 2);
|
||||||
|
BOOST_TEST_EQ(raii_tracker::move_count, 0);
|
||||||
|
BOOST_TEST_EQ(raii_tracker::alloc_move_count, 2);
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(map.begin()->first.get_allocator().tag_, 34);
|
||||||
|
BOOST_TEST_EQ(map.begin()->second.get_allocator().tag_, 34);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class X> static void set_uses_allocator_construction(X*)
|
||||||
|
{
|
||||||
|
reset_counts();
|
||||||
|
|
||||||
|
set_allocator_type alloc(allocator<raii_tracker>{12}, allocator<int>{34});
|
||||||
|
|
||||||
|
X set(1, alloc);
|
||||||
|
set.emplace();
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(raii_tracker::count, 1);
|
||||||
|
BOOST_TEST_EQ(raii_tracker::move_count, 0);
|
||||||
|
BOOST_TEST_EQ(raii_tracker::alloc_move_count, 1);
|
||||||
|
|
||||||
|
BOOST_TEST_EQ(set.begin()->get_allocator().tag_, 34);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNORDERED_TEST(map_uses_allocator_construction, ((flat_map)(node_map)))
|
||||||
|
UNORDERED_TEST(set_uses_allocator_construction, ((flat_set)(node_set)))
|
||||||
|
|
||||||
|
RUN_TESTS()
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user