Boost logo

Boost :

Subject: Re: [boost] [ptr_container] ptr_vector across DLL boundaries
From: Thorsten Ottosen (thorsten.ottosen_at_[hidden])
Date: 2009-06-26 04:45:27


Christian Schladetsch skrev:
> On Fri, Jun 26, 2009 at 9:26 AM, Thorsten Ottosen <
> thorsten.ottosen_at_[hidden]> wrote:
>
>> Christian Schladetsch skrev:
>>
>>> How does a clone_allocator make a new clone, if it needs access to the
>>> allocator in the parent ptr_container? [snip]
>>>
>>> What am I missing?
>>>
>> Nothing, I think. This seems like something it was not designed for,
>> on the cnotrary the clone is currently always allocated by someone else.
>
>
> Then what is the point of clone_allocator?

It does provide some room for customization. But it was deliberately
kept very conservative, so we can extend it when we need to in light of
user experience.

>> The current design uses static functions, and so there can be no stateful
>> allocator.
>>
>> I thought a little about this, but have not come up with a solution.
>> For example, how do we design an allocator that knows how to clone a
>> class hierarchy? Only the class itself knows how to clone itself because it
>> knows the exact type. OTOH, the classes in the class hieararchy doesn't know
>> about the allocator.
>>
>> We might try a new way to clone objects by replacing
>>
>> virtual Base::clone() const = 0;
>
>
> Isn't this what a copy constructor is for?

Types in object hierarchies usually don't expose their copy-constructor
to avoid sliving. Furthermore, and more importantly, you loose the
concrete type of the objects by having a container of pointers to the
base class. Virtual functions are the most elegant way to ask the object
for a copy of the right type.

> I've had a quick look at <boost/ptr_container/ptr_inserter.hpp> and related
> files, and it seems that whenever it currently says something like
>
> obj =
> container_type::clone_allocator_type::allocate_clone(*r);
>
> it could say instead
>
> obj =
> container_type::clone_allocator_type::allocate_clone(*r,
> cont.get_allocator());

I guess it could.

> And then in a custom clone_allocator, you could say:
>
> struct my_clone_allocator
> {
> template< class U, class Allocator >
> static U* allocate_clone( const U& r, Allocator &alloc)
> {
> typename Allocator::template rebind<U>::other
> my_alloc(alloc);
> U *clone = my_alloc.allocate(1);
> my_alloc.construct(clone, r);
> return clone;
> }
> }

This requires a public copy-constructor which we must not impose as a
requirement, and even so it would not work because the concrete type is
lost. (let's ignore that it is not exception-safe).

But this raises another question I have: would we want all the objects
to be allocated from the same allocator type using rebind<>, or would we
like to use two different allocators.

> Or, pass in a rebound allocator to allocate_clone; it doesn't really matter.
> Point being, can you just pass the container's allocator to the
> clone_allocator?

I can, but I feel it is unanswered whether it should be the underlying
container's allocator or simply the clone_allocator that somehow makes
it's own allocations.

And, as stated above, we also need to make sure that implementers of
clone() get the allocator. I'm not a fan of having users call rebind<>
inside each of their virtual clone() functions. It sounds very inelegant
to me.

That is why I suggested a non-templated allocator,
boost::clone_allocator, that can be used by all classes in the hierarchy.

> Another alternative is to make clone_allocator a concept and pass the
> either-or-both U and Allocator types to it at compile time, but I think it
> works just as well and more generally as a static method.

We have to allow non-static functions to support Robert's use case.

-Thorsten


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk