On Thu, 1 Aug 2024 at 18:28, François Dumont <frs.dum...@gmail.com> wrote:

> Hi
>
> Here is a proposal to add fancy pointer support in std::_Rb_tree container.
>
> As you'll see there are still several usages of
> pointer_traits<>::pointer_to. The ones in _M_header_ptr() are
> unavoidable.


Yes, those are necessary.

The pointer_to use in _M_const_cast could be simplified by adding a
_M_self_ptr() member to _Rb_tree_pnode_base:

  _Base_ptr
  _M_self_ptr() _GLIBCXX_NOEXCEPT
  { return pointer_traits<_Base_ptr>::pointer_to(*this); }

  _Base_ptr
  _M_self_ptr_nc() const _GLIBCXX_NOEXCEPT
  {
    auto __self = const_cast<_Rb_tree_pnode_base*>(this);
    return __self->_M_self_ptr();
  }

 _Const_Base_ptr
  _M_self_ptr() const _GLIBCXX_NOEXCEPT
  { return pointer_traits<_Const_Base_ptr>::pointer_to(*this); }

Then _M_const_cast would do:

return iterator(_M_node->_M_self_ptr_nc());



> The ones to extract a node or to return a node to the
> allocator are more questionable. Are they fine ? Is there another way to
> mimic the static_cast<_Link_type> that can be done on raw pointers with
> fancy pointers ?
>

I think you can just do static_cast<_Link_type>(_M_node->_M_self_ptr())
i.e. convert the _Base_ptr to the derived _Link_type
(we should really rename _Link_type to _Node_ptr, I keep getting confused
by the fact that the actual links between nodes are _Base_ptr, so what does
_Link_type mean now?!)

Alternatively, you could add a _M_node_ptr() to the _Rb_tree_pnode_base
type:

template<typename _NodeT>
  __ptr_rebind<_BasePtr, _NodeT>
  _M_node_ptr() _GLIBCXX_NOEXCEPT
  {
    auto __node = static_cast<_NodeT*>(this);
    using _Node_ptr = __ptr_rebind<_BasePtr, _NodeT>;
    return pointer_traits<_Node_ptr>::pointer_to(*__node);
  }

Then:
  auto __np = __p->template _M_node_ptr<_Node_type>()
  _Alloc_traits::deallocate(_M_get_Node_allocator(), __np, 1);


Some more general comments:

_Rb_tree_pnode_base and the iterators could just take one template
argument, and use __ptr_rebind to get the const pointer from the non-const
pointer. We can add a helper to do that:

template<typename _Ptr> struct __add_const_to_ptr;
#if __cplusplus >= 201103L
template<typename _Ptr>
  using __add_const_to_ptr = __ptr_rebind<_Ptr, const typename
pointer_traits<_Ptr>::element_type>;
#else
template<typename _Ptr> struct __add_const_to_ptr;
template<typename _Tp>
  struct __add_const_to_ptr<_Tp*>
  { typedef const _Tp* __type; };
#endif

This will let you get the FooCPtr type from the FooPtr type.

A traits class could replace all the __conditional_t uses:

#if __cplusplus >= 201103L
template<typename _ValPtr>
struct _Rb_tree_node_traits
{
  using _Base = _Rb_tree_pnode_base<_ValPtr>;
  using _Node_type = _Rb_tree_pnode<_ValPtr>;
  using _Base_ptr = __ptr_rebind<_ValPtr, _Base>;
  using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
  using iterator = _Rb_tree_piterator<_ValPtr>;
  using const_iterator = _Rb_tree_const_piterator<_ValPtr>;
};
#else
template<typename _ValPtr>
struct _Rb_tree_node_traits;
#endif

template<typename _Val>
struct _Rb_tree_node_traits<_Val*>
{
  typedef _Rb_tree_node_base _Base;
  typedef _Rb_tree_node<_Val> _Node_type;
  typedef _Base* _Base_ptr;
  typedef _Node* _Node_ptr;
  typedef _Rb_tree_iterator<_Val> iterator;
  typedef _Rb_tree_const_iterator<_Val> const_iterator;
};

I think this would get rid of a lot of the #if checks and the
__conditional_t uses.

Otherwise this looks really good, nice work!


>
> A solution to this (potential) issue would be to:
>
> 1. Store _Node_type pointer in the data structure rather than _Node_base
> pointer. But then _Rb_tree_pheader _M_left/_M_right could not be
> initialized with a pointer to _M_parent which is only a _Node_base
> instance. We would then need to init it to nullptr but I haven't check
> impact on code and more importantly it would be an abi breaking change.
>
> 2. Still store _Node_type pointer in the data structure but then make
> _M_header a _Node_type instance. In this case memory area of the
> value_type would just be lost bytes.
>
>
>      libstdc++: Add fancy pointer support in std::[multi][map/set]
>

Let's just say "maps and sets" or "RB trees" instead of the more cryptic
std::[multi][map/set].


>      Support fancy allocator pointer type in std::_Rb_tree<>.
>
>      In case of fancy pointer type the container is now storing the
> pointer to
>      _Rb_tree_pnode<> as a pointer to _Rb_tree_pnode_base<>.
>
>      Many methods are adapted to take and return _Base_ptr in place of
> _Link_type.
>
>      libstdc++-v3/ChangeLog:
>
>              * include/bits/stl_tree.h
>              (_Rb_tree_pnode_base<>): New.
>              (_Rb_tree_node_base): Inherit from latter.
>              (_Rb_tree_pheader): New.
>              (_Rb_tree_header): Inherit from latter.
>              (_Rb_tree_node_val): New.
>              (_Rb_tree_node): Inherit from latter.
>              (_Rb_tree_pnode): New.
>              (_Rb_tree_helpers): New.
>              (_Rb_tree_piterator): New.
>              (_Rb_tree_const_piterator): New.
>              (_Rb_tree::_Node_base, _Rb_tree::_Node_type): New.
>              (_Rb_tree): Adapt to generalize usage of _Base_ptr in place
> of _Link_type.
>              * testsuite/23_containers/map/allocator/ext_ptr.cc: New
> test case.
>              * testsuite/23_containers/multimap/allocator/ext_ptr.cc:
> New test case.
>              * testsuite/23_containers/multiset/allocator/ext_ptr.cc:
> New test case.
>              * testsuite/23_containers/set/allocator/ext_ptr.cc: New
> test case.
>
> Tested under Linux x64.
>
> François
>

Reply via email to