Boost logo

Boost :

Subject: Re: [boost] [smart ptr] Any interest in copy-on-write pointer for C++11?
From: Ralph Tandetzky (ralph.tandetzky_at_[hidden])
Date: 2013-02-11 13:15:11

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"

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.

>>> 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.

> 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.

> It is worth adding interactions with nullptr_t?

I never needed it. I'm not sure. Maybe for completeness sake. Should I
add it?

> 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.)

> 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?

> Best,
> Vicente

Thank you for your great feedback!

Boost list run by bdawes at, gregod at, cpdaniel at, john at