Design summary: smart pointers in allocators

Phil Bouchard philippe@fornux.com
Wed Sep 3 09:22:00 GMT 2008


"Bob Walters" <bob.s.walters@gmail.com> wrote in message 
news:3d6744d10808261529o33724bees4071c6e713c7f816@mail.gmail.com...

[...]

> g)  the allocator's deallocate() method takes a pointer by value, but
> needs to ensure that all references to that address are cleared.  In
> some cases, the 'cheat' of using a deallocate() method which takes the
> pointer by reference allows the deallocation to occur by setting the
> pointer to null.  As a safety this method can also ensure that the
> pointer passed knows it is responsible for the address being
> deallocated (that ownership = true)
>
> I think the above may have some holes.  Possible.... but very ugly, I
> admit.  I just haven't thought of anything cleaner.  The only other
> alternatives that I've seen are:
> a) don't support smart pointers, require that all memory management be
> done by the allocator as intended by the standard.
> b) redesign some containers (like vector) to avoid the pointer as
> iterator idiom, and generally work to ensure that an
> allocator::pointer only points to a heap allocated address, and so on.
> In my thinking, this is pushing too much responsibility onto the
> containers, and involves far too much impact.
> c) do the container changes with the assumption that
> allocator::pointer is a smart pointer, and has to point to allocated
> memory only.  With vector, this means some internal pointer use
> allocator::pointer, and some remain normal T* to differentiate.
> However, this design then breaks all cases of using alocator::pointer
> for storage reasons (e.g. boost::interprocess::offset_pointer) and I'm
> opposed to this.

I apologize for the late reply but I had to see our options.  Verdict, I 
think _Pointer_adapter should have a generic release() function added to it. 
Hence supported smart ponters will also need this member function in order 
to be supported the class.  This way I don't think there will be any need 
for changes as far as the C++0x standards are concerned, regarding 
"allocator::deallocate(pointer &)".

But the pointer typedef and the pointer returned by allocate() and the one 
expected by deallocate() won't be necessarily the same.  In the case of 
shifted_allocator<> this means:

template <typename T>
    class shifted_allocator
    {
        ...
        typedef T                       value_type;
        typedef shifted<T>              element_type;
        typedef shifted_ptr<T>          pointer;
        typedef shifted_ptr<const T>    const_pointer;

        element_type * allocate(size_type s, const void * = 0);
        void deallocate(element_type *, size_type);
    };

Now if we define:

template <typename _Storage_policy >
    class _Pointer_adapter
    {
        ...
        typedef typename _Storage_policy::element_type   element_type;

        element_type * release()
        {
            return _M_impl.release();
        }
    };

Once again the smart pointer will have release() defined as such:

template <typename T>
    class smart_ptr
    {
        ...
        smart_ptr(element_type *);

        value_type * get();

        element_type * release()
        {
            element_type * __p = pointer_ref();

            pointer_ref() = 0;

            if (-- counter_ref())
                return 0;
            else
                return __p;
        }
    };


Then we will be able to write down generic mutative algorithms able to work 
will all types of smart pointers.  Let's consider clear() for lists and 
stacks (pseudo-code):

template <typename _Tp, typename _Alloc>
    void _List_base<_Tp, _Alloc>::_M_clear()
    {
        typedef _List_node_base<_Alloc>  _Node_base;

        typename _Node_base::pointer* __cur = & 
this->_M_impl._M_node->_M_next;

        while (__cur)
        {
            typename _Node_base::pointer* __next = & (*__cur)->_M_next;
            typename _Node_base::value_type* __tmp = __cur->release();

            if (__tmp)
            {
                _M_impl._Node_Alloc_type::destroy(__tmp);
                _M_impl._Node_Alloc_type::deallocate(__tmp);
                break; // can't go on
            }

            __cur = __next;
        }

The logic should work for all types of smart pointers and most containers. 
I need to confirm ordered containers.


-Phil





More information about the Libstdc++ mailing list