
On Wed, Feb 13, 2013 at 4:10 AM, Ralph Tandetzky < ralph.tandetzky@googlemail.com> wrote:
On 02/11/2013 11:34 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Feb 11, 2013 at 1:47 PM, Peter Dimov <lists@pdimov.com> 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.)
Sure; probably sufficient to package this in a mutate(read_ptr<T>, F) free function if you want to reduce that boilerplate. Or, instead of using a write_ptr, use a very similar object, say, mutator, which, upon destruction, automatically moves itself back to the read_ptr: if (mutator<T> m = pr.write()) { m->mutate(); } // mutator<T>::~mutator() effects pr = std::move(m) or something similar The other thing: It it not very clear at which point the cloning happens,
if it is necessary.
I think calling it unclear is stretching things, but okay.
There are 2 possibilities: 1. pr.write() does it. Or, if you want make it std::move(pr).
I prefer .write() (or whatever you want to call it).
2. pw.operator->() does it.
Hmmm...I'm going to need more details on how this works and what the expected usage pattern is. It seems there are some non-trivial design considerations (more nontrivial than case 1) if you try to go this path. If you end up cloning on write_ptr::operator->(), does anything happen to the corresponding read_ptr? What happens if you get multiple write_ptr's from the same read_ptr? Are there any thread safety issues? 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().
Sounds good.
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.
Okay, (1) cloning on operator->() smells a lot like implicit copy-on-write, the avoidance of which was the entire reason for introducing a second smart pointer. And (2) I'm confused, because it looks like you want write_ptr::operator->() to do the cloning, but then seem to imply this is a "single pointer type solution"...? Does that mean there's no more read_ptr? I think explicit copy-on-write is better because read-only and write access to the target object are clearly syntactically distinguished. No need to keep checking the use_count within operator->() and make defensive copies. That may be what you're proposing but I'm not sure. - Jeff