Gavin Lambert wrote:
On 10/6/2013 8:34 AM, Quoth Julian Gonggrijp:
As for safety, I think std::weak_ptr is not really safer that rtp::weak_ptr because it's just as easy to produce a dangling pointer (in fact std takes only 3 lines of code to do it while rtp requires 4) and that's a disaster no matter what. The fact that it can detect that it's dangling does however make it easier to debug.
I fail to see why "dangling pointers" are a disaster with std::weak_ptr.
Their implementation does not permit any access without locking the pointer, which will either succeed (and consequently prevent the object from being destroyed while it is being accessed) or fail (if the object has already been destroyed) and return a null pointer.
The code using the pointer has to be able to cope gracefully with failure to obtain a pointer, but this is built into the semantics of weak_ptrs -- if you can't write the code such that it can fail gracefully, then you should have been using a shared_ptr instead so that you can guarantee that the object won't disappear before you're ready for it to do so.
The std::weak_ptr detects the dangling pointer and changes it into a null pointer. This makes sense, because null pointers are easier to detect. However, as the surrounding code probably relies on a live pointer (because dangling pointers are never planned) the program is still going to fail. This is what I meant by "disaster". You say that the caller should have been using std::shared_ptr instead, but similar reasoning applies to the rtp pointers. All pointers (smart and raw) are associated with pitfalls that the user has to avoid. Now, you would be right to point out that I haven't properly spelled out the pitfalls for the rtp pointers; I will fix that omission in the next alpha version. Further note that the user probably chose std::weak_ptr in the first place in order to avoid cyclic ownership. If the only way to avoid the dangling pointer is to switch back to std::shared_ptr, this boils down to a choice between two evils: a dangling pointer or a memory leak. Fortunately this will usually not be the case, because (I believe) dangling pointers can always be avoided with more careful program logic. This however is equally true of the rtp pointers.
Bare pointers, on the other hand (including rtp::weak_ptr, from what I understand from this discussion, though I haven't examined the code specifically) have no such tracking/locking capability
Please forgive me for nagging about terminology, but rtp::weak_ptr should really be thought of as a smart pointer. It is a wrapper around a raw pointer that helps to take ownership management out of the user's hands, in this case by ensuring that the corresponding rtp::owner_ptr (or rtp::data_ptr) is the only entity that can deallocate the object. If you think that a smart pointer must on top of that be able to track whether a pointer is still live, you have effectively equated "smart pointer" to "reference-counted pointer" and you are forced to subscribe to the view that std::unique_ptr is a "bare" pointer.
so there is no way to ensure that a particular pointer value still points at the same object as it originally did.
Sure, it's still possible to get a null pointer access with std::weak_ptr, by locking one and then accessing the resulting shared_ptr without checking for validity (ie. lazy programmer). But in this case it will assert, and even failing that null accesses are far easier to track down and deal with than non-null-invalid-object accesses.
(I'm not saying that shared_ptrs don't have problems -- for example it's far too easy to get into trouble with cyclic references if you're not careful, and I've yet to find any good tools to track these down. But they don't have *this* particular problem, since solving that is pretty much their entire reason for existing in the first place.)
They don't have the specific problem that dangling pointers are hard to track down, thanks to reference counting, because dangling pointers change into null pointers on the spot. I think calling that the sole purpose of std::shared_ptr and std::weak_ptr would be giving them too little credit, they can do much more for the user than just that. Still, as you say, there are serious problems associated with them and I believe this is sufficient reason to look into different approaches. -Julian