Boost logo

Boost :

Subject: Re: [boost] [smart ptr] Any interest in copy-on-write pointerforC++11?
From: Ralph Tandetzky (ralph.tandetzky_at_[hidden])
Date: 2013-02-13 07:10:06


On 02/11/2013 11:34 PM, Jeffrey Lee Hellrung, Jr. wrote:
> On Mon, Feb 11, 2013 at 1:47 PM, Peter Dimov <lists_at_[hidden]> wrote:
>>
>>> read_ptr<T> pr;
>>> if( write_ptr<T> pw = pr.write() )
>>> {
>>> pw->mutate();
>>> pr = std::move( pw );
>>> }
>>>
>>> I don't, however, particularly like the semantics of the case in which pr
>>> is already unique.
>> In fact, now that I think of it, .write should be a move, too.
> In the case that pr has a use_count of 1, definitely. I thought that's what
> you had been proposing all along :) Deep copy the pointed-to object only if
> the use_count >= 2. Whatever you get back, do your mutations, then pass it
> back to the read_ptr.
If you have two pointers, then it should be move semantics, I agree.
However, the above code is not strongly exception-safe. You would have
to write
         read_ptr<T> pr;
         if ( write_ptr<T> pw = pr.write() )
         {
             try
             {
                 pr->mutate(); // might throw
             }
             catch (...)
             {
                 pr = std::move( pw ); // no-throw
                 throw;
             }
             pr = std::move( pw ); // no-throw
         }
In order to fix it. (Or some scope_guard stuff, if you like.) The other
thing: It it not very clear at which point the cloning happens, if it is
necessary. There are 2 possibilities:
1. pr.write() does it. Or, if you want make it std::move(pr).
2. pw.operator->() does it.
In case 1 using std::move() would be bad, because moving should normally
be cheap and preferably not throw. Therefore I would prefer to use
pr.write().
In case 2 the pointer types probably have to be friends and they are
more tightly coupled. That isn't necessarily bad, because they are
shipped together.
I would prefer the variant 2. The two-pointer-solution makes it harder
to write exception-safe code. Therefore I would prefer a single pointer
type solution.

>> In this case, it's wouldn't be surprising to sometimes find pr holding a
>> NULL after the move.
>>
>> write_ptr<T> pw = std::move( pr );
>> pw->mutate();
>> pr - std::move( pw );
>>
>> The advantage here is that (1) in
>>
>> read_ptr<T> get_value();
>> write_ptr<T> p = get_value();
>>
>> you don't need a write() call,
> I would expect most of the time you're not going to be immediately
> modifying a read_ptr rvalue like above. The typical use case I'd expect is
> you have existing read_ptr lvalue which points to an object you want to
> mutate. So whether you write .write() or move(pr) is not a huge syntactic
> difference.
>> and (2) this avoids the mistake of doing
>>
>> pr.write();
>>
>> without storing the return value and as a result, losing *pr when it's
>> unique. Just doing
>>
>> std::move( pr );
>>
>> would do nothing, so it's safer.
>>
> That's a fair point, but I think this is really an abuse of rvalue
> reference syntax :) I guess one concern I have is if you're moving a smart
> ptr, I'd expect it to be as fast as a couple pointer assignments, but here
> it could incur a deep copy, and that behavior is entirely runtime-dependent.
>
> - Jeff
Not if you do the work in pw.operator->() as pointed out above.

Thank you for your ideas,
Ralph


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