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