|
Boost : |
Subject: Re: [boost] [smart ptr] Any interest in copy-on-write pointer for C++11?
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2013-02-11 15:48:28
Le 11/02/13 19:15, Ralph Tandetzky a écrit :
> On 02/10/2013 10:27 AM, Vicente J. Botet Escriba wrote:
>> Le 10/02/13 03:04, Vicente J. Botet Escriba a écrit :
>>> Le 08/02/13 16:16, Ralph Tandetzky a écrit :
>>>> Hi,
>>>>
>>>> is there any interest in a copy-on-write pointer implementation? I
>>>> wrote a cow_ptr<T> template class for C++11 which has the following
>>>> use cases:
>>>>
>>>>
>>> As others I would prefer another name for the class. Could you
>>> compare how you class relates to value_ptr as defined here
>>> (file://localhost/Users/viboes/Downloads/n3339-1.pdf)?
>>
>> I meant " N3339: A Preliminary Proposal for a Deep-Copying Smart
>> Pointer"
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf
>
> Thank you for this awesome reference. That was very enlightening. The
> most obvious difference is that when value_ptrs are copied, then the
> pointee object is cloned instantly. There is no copy-on-write. But the
> value semantics are quite similar. Both value_ptr and cow_ptr support
> polymorphic cloning.
>
> I might use some ideas in value_ptr's source code like the automaticly
> choosing between default_clone and default_copy.
Glad to hear this inspired you.
>
>>>> 3. You can add cloning to a class hierarchy from the outside. With
>>>> cow_ptr<Base> a( new Derived1 );
>>>> cow_ptr<Base> b( new Derived2 );
>>>> cow_ptr<Base> c;
>>>> c = a; // performs a shallow copy.
>>>> c->doSomething(); // makes a deep copy of a as a
>>>> Derived1 class.
>>>> // There is no slicing involved.
>>>> you copy Base objects polymorphically. The class Base can even be
>>>> abstract here. It is only required that Derived1 and Derived2 be
>>>> CopyConstructible.
>>>>
>>>
>>> It seems to me that the copy of polymorphic object works only with
>>> the help of the user and that the following should be mentioned as a
>>> limitation
>>>
>>> Base* ba = new Derived1;
>>> cow_ptr<Base> a( ba );
>>> cow_ptr<Base> c;
>>> c = a; // performs a shallow copy.
>>> c->doSomething(); // couldn't makes a deep copy of a as
>>> a Derived1 class as the type has been lost.
>>> // There is a slicing involved.
>
> In the DefaultCloner there is an assertion
> assert( typeid( *p ) == typeid( const T& ) );
> which would be violated in the line you marked. So you'll notice that
> during debugging when the copy is performed. There's no way to check
> that at compile-time unfortunately.
Ok. So typeid( *p ) == typeid( const T& ) is a precondition of the
constructor from a Y*. I find this quite unfortunate. Which is the
advantage this provide to the user? Better performances? if yes where?
>
>>>
>> If this is confirmed, I see it as a show-stopper, and the cloning
>> strategy must be redefined.
>>
>> I don't see any relational operators. Is this intentional?
>
> To a degree. It's not totally obvious, if only the pointers will be
> compared, or if pointers are equal, if the pointed-to objects will be
> compared for equality as well. I'd prefer the first variant and I will
> add them in the next revision.
Could your class store nullptr?
Does your class have value semantics or pointer semantics?
IMHO, it should have value semantics and the comparison should be made
on the stored value.
>
>> It is worth adding interactions with nullptr_t?
>
> I never needed it. I'm not sure. Maybe for completeness sake. Should I
> add it?
This depend on what do you want, pointer or value semantic. If you want
pointer semantic, interactions with nullptr_t should be added, otherwise
not evidently.
>
>>
>> What about a T* release() member function?
>>
>
> Rather not. Should this make a copy? It has to, if there's more than
> one copy. If there's only one copy, then it depends if there is an
> internal concrete_counter or a wrapping_counter. A wrapping counter
> wouldn't be able to release without copying. But the most compelling
> reason for me is that the caller wouldn't know for sure how to delete
> the object. The cow_ptr could have been initialized with some kind of
> special deleter. (It's kind of similar to shared_ptr.)
>
OK, I see.
>> What about a reset(U*) member function ?
>
> This stuff can be done with the constructors and the assignment
> operators. So, strictly speaking there's no need for it. You might get
> a bit better performance with reset() though. (Is performance actually
> the reason for shared_ptr and unique_ptr to provide a reset()
> function?) At first I wanted to keep the class interface simple. I
> might add it in the future. Are there compelling reasons to add it now?
>
I suspect that if the operation can be implemented with better
performances than using constructor and assignment it is a compelling
reason.
Best,
Vicente
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk