Update foa to construct stack-locals with the user's Allocator during emplace()

This commit is contained in:
Christian Mazakas 2023-02-23 11:06:14 -08:00
parent 0ff1fa0f6e
commit 86318c1e88
5 changed files with 126 additions and 69 deletions

View File

@ -1129,6 +1129,25 @@ _STL_RESTORE_DEPRECATED_WARNING
*/
constexpr static float const mlf = 0.875f;
template <class T>
union storage
{
T t_;
storage(){}
~storage(){}
};
template <class TypePolicy,class A,class T>
struct drop_guard
{
using type_policy=TypePolicy;
A& a;
T* p;
~drop_guard(){type_policy::destroy(a,p);};
};
/* foa::table interface departs in a number of ways from that of C++ unordered
* associative containers because it's not for end-user consumption
* (boost::unordered_[flat|node]_[map|set]) wrappers complete it as
@ -1418,18 +1437,35 @@ public:
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
{
/* We dispatch based on whether or not the value_type is constructible from
* an rvalue reference of the deduced emplace_type. We do this specifically
* for the case of the node-based containers. To this end, we're able to
* avoid allocating a node when a duplicate element is attempted to be
* inserted. For immovable types, we instead dispatch to the routine that
* unconditionally allocates via `type_policy::construct()`.
*/
return emplace_value(
using emplace_type=typename std::conditional<
std::is_constructible<init_type,Args...>::value,
init_type,
value_type
>::type;
using insert_type=typename std::conditional<
std::is_constructible<
value_type,
emplace_type<Args...>&&>{},
std::forward<Args>(args)...);
value_type,emplace_type>::value,
emplace_type,element_type
>::type;
using alloc_insert_type=typename std::conditional<
std::is_constructible<
value_type,emplace_type>::value,
emplace_type,value_type
>::type;
using alloc_type=
typename boost::allocator_rebind<Allocator,alloc_insert_type>::type;
storage<insert_type> s;
alloc_type alloc{al()};
auto *p=std::addressof(s.t_);
type_policy::construct(alloc,p,std::forward<Args>(args)...);
drop_guard<type_policy,alloc_type,insert_type> guard{alloc,p};
return emplace_impl(type_policy::move(*p));
}
template<typename Key,typename... Args>
@ -1614,15 +1650,6 @@ private:
template<typename,typename,typename,typename> friend class table;
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
{
~clear_on_exit(){x.clear();}
@ -1904,29 +1931,6 @@ private:
#pragma warning(pop) /* C4800 */
#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>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
{

View File

@ -105,6 +105,9 @@ struct node_handle_base
node_handle_base& operator=(node_handle_base&& nh)noexcept
{
element_type x;
x.p=p_;
if(this!=&nh){
if(empty()){
if(nh.empty()){ /* empty(), nh.empty() */
@ -115,7 +118,7 @@ struct node_handle_base
}
}else{
if(nh.empty()){ /* !empty(), nh.empty() */
type_policy::destroy(al(),p_);
type_policy::destroy(al(),&x);
reset();
}else{ /* !empty(), !nh.empty() */
bool const pocma=
@ -124,7 +127,7 @@ struct node_handle_base
BOOST_ASSERT(pocma||al()==nh.al());
type_policy::destroy(al(),p_);
type_policy::destroy(al(),&x);
if(pocma){
al()=std::move(nh.al());
}
@ -137,7 +140,7 @@ struct node_handle_base
if(empty()){ /* empty(), nh.empty() */
/* nothing to do */
}else{ /* !empty(), !nh.empty() */
type_policy::destroy(al(),p_);
type_policy::destroy(al(),&x);
reset();
}
}
@ -147,7 +150,9 @@ struct node_handle_base
~node_handle_base()
{
if(!empty()){
type_policy::destroy(al(),p_);
element_type x;
x.p=p_;
type_policy::destroy(al(),&x);
reset();
}
}

View File

@ -53,6 +53,11 @@ namespace boost {
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::get<0>(std::move(x)), std::get<1>(std::move(x))};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
@ -61,11 +66,26 @@ namespace boost {
}
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)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
using alloc_type =
typename boost::allocator_rebind<A, value_type>::type;
alloc_type alloc(al);
std::allocator_traits<alloc_type>::construct(
alloc, 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, element_type* p) noexcept
{
boost::allocator_destroy(al, p);

View File

@ -77,6 +77,11 @@ namespace boost {
}
static element_type&& move(element_type& x) { return std::move(x); }
static moved_type move(init_type& x)
{
return {std::get<0>(std::move(x)), std::get<1>(std::move(x))};
}
static moved_type move(value_type& x)
{
return {std::move(const_cast<raw_key_type&>(x.first)),
@ -96,6 +101,18 @@ namespace boost {
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>
static void construct(A& al, element_type* p, Args&&... args)
{
@ -106,10 +123,11 @@ namespace boost {
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al,
boost::pointer_traits<
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
1);
using pointer_type=typename boost::allocator_pointer<A>::type;
using pointer_traits=boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al,pointer_traits::pointer_to(*(p->p)),1);
BOOST_RETHROW
}
BOOST_CATCH_END
@ -118,16 +136,22 @@ namespace boost {
template <class A> static void destroy(A& al, value_type* p) noexcept
{
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, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al,p->p);
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 +161,7 @@ namespace boost {
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type =
detail::foa::node_handle_base<TypePolicy, Allocator>;
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;

View File

@ -78,6 +78,12 @@ namespace boost {
x.p = nullptr;
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
@ -100,16 +106,16 @@ namespace boost {
template <class A> static void destroy(A& al, value_type* p) noexcept
{
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
{
if (p->p) {
destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
}
}
};
@ -119,8 +125,7 @@ namespace boost {
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type =
detail::foa::node_handle_base<TypePolicy, Allocator>;
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
@ -391,7 +396,7 @@ namespace boost {
BOOST_ASSERT(get_allocator() == nh.get_allocator());
typename set_types::element_type x;
x.p=std::addressof(nh.element());
x.p = std::addressof(nh.element());
auto itp = table_.insert(std::move(x));
if (itp.second) {
@ -411,7 +416,7 @@ namespace boost {
BOOST_ASSERT(get_allocator() == nh.get_allocator());
typename set_types::element_type x;
x.p=std::addressof(nh.element());
x.p = std::addressof(nh.element());
auto itp = table_.insert(std::move(x));
if (itp.second) {
@ -478,7 +483,7 @@ namespace boost {
node_type extract(key_type const& key)
{
auto pos = find(key);
return pos!=end()?extract(pos):node_type();
return pos != end() ? extract(pos) : node_type();
}
template <class K>
@ -489,7 +494,7 @@ namespace boost {
extract(K const& key)
{
auto pos = find(key);
return pos!=end()?extract(pos):node_type();
return pos != end() ? extract(pos) : node_type();
}
template <class H2, class P2>