Hi
It would be nice if someone got some time to review this PR.
Compared to other containers for which support of fancy allocator
pointer type have been added the main difference is that in
std::_Hashtable usage of the new node types is an abi breaking change,
no matter how fancy or not is the allocator's pointer type. This is
because in this case hash code is always cached and is put in memory
just after pointer to next node. Let me know if I need to be more
conservative.
libstdc++: Add fancy pointer support to std::_Hashtable [PR57272]
The fancy allocator pointer type support is added to
std::unordered_map,
std::unordered_multimap, std::unordered_multiset and
std::unordered_set
through the underlying std::_Hashtable class.
To respect ABI a new parallel hierarchy of node types has been added.
This change introduces new class template parameterized on the
allocator's
void_pointer type, __hashtable::_Node_base, and new class templates
parameterized on the allocator's pointer type, __hashtable::_Node,
__hashtable::_Iterator, __hashtable::_Local_iterator. The
_Iterator class
template is used for both iterator and const_iterator. The
_Local_iterator
class template is used for both local_iterator and
const_local_iterator.
Whether std::_Hashtable<K, V, A, KoV, E, H, RH, U, RP, T> should
use the old
__detail::_Hash_node<V, T::__hash_cached::value> or new
__hashtable::_Node<A::pointer> type family internally is
controlled by a new
__hashtable::_Node_traits traits template. Whether it should use
the old
__detail::_Node_iterator<V, T::__constant_iterators::value,
T::__hash_cached::value>
or new __hashtable::_Iterator<Const,
T::__constant_iterators::value, A::pointer>
type family internally is controlled by
__hashtable::_Iterator_traits traits
template.
In case anybody is currently using std::_Hashtable with an
allocator that has a
fancy pointer, this change will be an ABI break, because their
std::_Hashtable
instantiations would start to (correctly) use the fancy pointer
type. Note that
the new type family used in this case is always caching the hash
code at node
level.
Because std::_Hashtable will never use fancy pointers in C++98
mode, recompiling
everything to use fancy pointers isn't even possible if mixing
C++98 and C++11
code that uses std::_Hashtable. To alleviate this problem,
compiling with
-D_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE=0 will force
std::_Hashtable to have the
old, non-conforming behaviour and use raw pointers internally. For
testing
purposes, compiling with
-D_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE=9001 will force
std::_Hashtable to always use the new node types.
This macro is currently undocumented, which needs to be fixed.
libstdc++-v3/ChangeLog:
PR libstdc++/57272
* include/bits/hashtable.h
(__hashtable::_Node_base<>, __hashtable::_Node<>): New.
(__hashtable::_Iterator_base<>, __hashtable::_Iterator<>):
New.
(__hashtable::_Local_iterator<>): New.
(__hashtable::_Node_traits<>,
__hashtable::_Iterator_traits<>): New.
(__hashtable::__alloc_ptr<>): New template alias.
(_Hashtable<>::__node_type, __node_alloc_type,
__node_alloc_traits, __node_ptr)
(__node_base, __node_base_ptr, __buckets_ptr): Rename
respectively into...
(_Hashtable<>::_Node, _Node_alloc, _Node_alloc_traits,
_Node_ptr)
(_Node_base, _Base_ptr, _Buckets_ptr): ... those.
(_Hashtable<>::_Buckets_ptr_traits): New.
(_Hashtable<>::__hash_code_base_access): Remove.
(_Hashtable<>::_S_v, _S_next, _M_single_bucket_ptr): New.
(_Hashtable<>::_M_node_hash_code, _M_node_hash_code_ext,
_M_bucket_index)
(_M_bucket_index_ext, _M_key_equals, _M_key_equals_tr,
_M_equals, _M_equals_tr)
(_M_node_equals): New.
(_Hashtable<>::__location_type::_M_base): New.
(_Hashtable<>::_S_adapt): New.
(_Hashtable<>): Adapt.
* include/bits/hashtable_policy.h: Include
<bits/ptr_traits.h>.
Include <bits/stl_uninitialized.h>.
(_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE): New macro, default
to 1.
(_Hash_node_base::_M_base_ptr): New.
(_Hash_node<>::_M_node_ptr): New.
(_Hash_code_base<>::_M_hash_code, _M_bucket_index): Remove.
(_Hashtable_base<>::_M_key_equals, _M_key_equals_tr):
Adapt to only take key
type.
(_Hashtable_base<>::_M_equals, _M_equals_tr,
_M_node_equals): Remove.
(_Hashtable_alloc<>): Adapt.
* testsuite/23_containers/unordered_map/115939.cc: #undef
_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE as types associated
with fancy pointer
support do not suffer from the ambiguity issue tested here.
*
testsuite/23_containers/unordered_map/allocator/ext_ptr.cc: New test.
*
testsuite/23_containers/unordered_map/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_map/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_multimap/allocator/ext_ptr.cc: New
test case.
*
testsuite/23_containers/unordered_multimap/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_multimap/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_multiset/allocator/ext_ptr.cc: New
test case.
*
testsuite/23_containers/unordered_multiset/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_multiset/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_set/allocator/ext_ptr.cc: Adapt.
* testsuite/23_containers/unordered_set/instantiation_neg.cc:
Undef _GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE.
*
testsuite/23_containers/unordered_set/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_set/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
Ok to commit ?
See also:
https://forge.sourceware.org/gcc/gcc-TEST/pulls/32
François
On 19/03/2025 21:09, François Dumont wrote:
Hi
Following latest changes on unordered container I've rebased and
updated this patch to add fancy allocator 's pointer type support in
unordered containers.
libstdc++: Add fancy pointer support to std::_Hashtable [PR57272]
The fancy allocator pointer type support is added to
std::unordered_map,
std::unordered_multimap, std::unordered_multiset and
std::unordered_set
through the underlying std::_Hashtable class.
To respect ABI a new parralel hierarchy of node types has been
added.
This change introduces new class template parameterized on the
allocator's
void_pointer type, __hashtable::_Node_base, and new class templates
parameterized on the allocator's pointer type, __hashtable::_Node,
__hashtable::_Iterator, __hashtable::_Local_iterator. The
_Iterator class
template is used for both iterator and const_iterator. The
_Local_iterator
class template is used for both local_iterator and
const_local_iterator.
Whether std::_Hashtable<K, V, A, KoV, E, H, RH, U, RP, T> should
use the old
__detail::_Hash_node<V, T::__hash_cached::value> or new
__hashtable::_Node<A::pointer> type family internally is
controlled by a new
__hashtable::_Node_traits traits template. Whether it should use
the old
__detail::_Node_iterator<V, T::__constant_iterators::value,
T::__hash_cached::value>
or new __hashtable::_Iterator<Const,
T::__constant_iterators::value, A::pointer>
type family internally is controlled by
__hashtable::_Iterator_traits traits
template.
In case anybody is currently using std::_Hashtable with an
allocator that has a
fancy pointer, this change would be an ABI break, because their
std::_Hashtable
instantiations would start to (correctly) use the fancy pointer
type. If the
fancy pointer just contains a single pointer and so has the same
size, layout,
and object representation as a raw pointer, the code might still
work (despite
being an ODR violation). But if their fancy pointer has a different
representation, they would need to recompile all their code using
that
allocator with std::_Hashtable. Because std::_Hashtable will
never use fancy
pointers in C++98 mode, recompiling everything to use fancy
pointers isn't
even possible if mixing C++98 and C++11 code that uses
std::_Hashtable. To
alleviate this problem, compiling with
-D_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE=0
will force std::_Hashtable to have the old, non-conforming
behaviour and use
raw pointers internally. For testing purposes, compiling with
-D_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE=9001 will force
std::_Hashtable to
always use the new node types. This macro is currently
undocumented, which
needs to be fixed.
Note that the new type family used in case of fancy allocator
pointer type is
always caching the hash code at node level.
libstdc++-v3/ChangeLog:
PR libstdc++/57272
* include/bits/hashtable.h
(__hashtable::_Node_base<>, __hashtable::_Node<>): New.
(__hashtable::_Iterator_base<>,
__hashtable::_Iterator<>): New.
(__hashtable::_Local_iterator<>): New.
(__hashtable::_Node_traits<>,
__hashtable::_Iterator_traits<>): New.
(_Hashtable<>::__node_type, __node_alloc_type,
__node_alloc_traits,
__node_ptr, __node_base, __node_base_ptr, __buckets_ptr):
Rename
respectively into...
(__hashtable::__alloc_ptr<>): New template alias.
(_Hashtable<>::_Node, _Node_alloc, _Node_alloc_traits,
_Node_ptr,
_Node_base, _Base_ptr, _Buckets_ptr): ... those.
(_Hashtable<>::_Buckets_ptr_traits): New.
(_Hashtable<>::__hash_code_base_access): Remove.
(_Hashtable<>::_S_v, _S_next, _M_single_bucket_ptr): New.
(_Hashtable<>::_M_node_hash_code, _M_node_hash_code_ext,
_M_bucket_index,
_M_bucket_index_ext, _M_key_equals, _M_key_equals_tr,
_M_equals, _M_equals_tr,
_M_node_equals): New.
(_Hashtable<>::__location_type::_M_base): New.
(_Hashtable<>::_S_adapt): New.
(_Hashtable<>): Adapt.
* include/bits/hashtable_policy.h: Include
<bits/ptr_traits.h>.
Include <bits/stl_uninitialized.h>.
(_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE): New macro,
default to 1.
(_Hash_node_base::_M_base_ptr): New.
(_Hash_node<>::_M_node_ptr): New.
(_Hash_code_base<>::_M_hash_code, _M_bucket_index): Remove.
(_Hashtable_base<>::_M_key_equals, _M_key_equals_tr):
Adapt to only take key
type.
(_Hashtable_base<>::_M_equals, _M_equals_tr,
_M_node_equals): Remove.
(_Hashtable_alloc<>): Adapt.
* testsuite/23_containers/unordered_map/115939.cc: #undef
_GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE as types associated
with fancy pointer
support do not suffer from the ambiguity issue tested here.
*
testsuite/23_containers/unordered_map/allocator/ext_ptr.cc: New test.
*
testsuite/23_containers/unordered_map/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_map/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_multimap/allocator/ext_ptr.cc: New
test case.
*
testsuite/23_containers/unordered_multimap/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_multimap/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_multiset/allocator/ext_ptr.cc: New
test case.
*
testsuite/23_containers/unordered_multiset/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_multiset/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
*
testsuite/23_containers/unordered_set/allocator/ext_ptr.cc: Adapt.
*
testsuite/23_containers/unordered_set/instantiation_neg.cc:
Undef _GLIBCXX_USE_ALLOC_PTR_FOR_HASHTABLE.
*
testsuite/23_containers/unordered_set/requirements/explicit_instantiation/alloc_ptr.cc:
New test case.
*
testsuite/23_containers/unordered_set/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test case.
Available as a PR here:
https://forge.sourceware.org/gcc/gcc-TEST/pulls/32
François