As observed in another thread, the intrusive_ptr destructor in boost 1.32 does not honor its own status as defined in the constructor with respect to the addRef boolean. This snippet shows the problem: X * x = new X ; { intrusive_ptr<X> p(x, false) ; } // Here, x has been destroyed! Is this a bug, or some requirement (that I couldn't find) that if you ever pass "false" to the constructor, then that intrusive_ptr is assumed to never go out of scope? The fix would be simple: ... bool strong ; intrusive_ptr(T * p, bool add_ref = true): p_(p), strong(add_ref) { if(p_ != 0 && add_ref) intrusive_ptr_add_ref(p_); } ... ~intrusive_ptr() { if(p_ != 0 && strong) intrusive_ptr_release(p_); } though it is less clear whether it would break some assumption I'm not privy to. Also, the above "fix" would increase the stack storage from the typical 4 bytes to probably 8 (due to alignment) just to store an additional bit. Obviously, a less portable implementation could get away with storing the bool as the low order bit of p_, masking it off when required ... Should I go ahead an implement this fix with the knowledge that a future revision of boost will include it, or is there any other consideration that render this fix unlikely to make it into an official release ? BTW: I did check the "unit tests" for intrusive_ptr at: http://cvs.sourceforge.net/viewcvs.py/*checkout*/boost/boost/libs/ smart_ptr/test/intrusive_ptr_test.cpp and this case is clearly not accounted for. -- Jean-François Brouillet verec@mac.com
Jean-François Brouillet wrote:
As observed in another thread, the intrusive_ptr destructor in boost 1.32 does not honor its own status as defined in the constructor with respect to the addRef boolean.
This snippet shows the problem:
X * x = new X ; { intrusive_ptr<X> p(x, false) ; }
// Here, x has been destroyed!
Is this a bug, or some requirement (that I couldn't find) that if you ever pass "false" to the constructor, then that intrusive_ptr is assumed to never go out of scope?
http://boost.org/libs/smart_ptr/intrusive_ptr.html#constructors add_ref == false means that you (or someone else) already called addref on the pointer. It's typically used with factory functions that return addref'ed pointers.
Is this a bug, or some requirement (that I couldn't find) that if you ever pass "false" to the constructor, then that intrusive_ptr is assumed to never go out of scope?
http://boost.org/libs/smart_ptr/intrusive_ptr.html#constructors
The above link only has this to say:
constructors
intrusive_ptr(); // never throws Postconditions: get() == 0.
Throws: nothing.
intrusive_ptr(T * p, bool add_ref = true); Effects: if(p != 0 && add_ref) intrusive_ptr_add_ref(p);.
Postconditions: get() == p.
intrusive_ptr(intrusive_ptr const & r); // never throws template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r); // never throws Effects: if(r.get() != 0) intrusive_ptr_add_ref(r.get());.
Postconditions: get() == r.get(). Hardly a reference to "factory" or that "someone else" has already taken ownership ...
add_ref == false means that you (or someone else) already called addr ef on the pointer. It's typically used with factory functions that return addref'ed pointers.
That settles it then. I just cannot use intrusive_ptr. Since shared_ptr is already ruled out, well .... roll-my-own-time, I guess ... Too bad for my use of boost. Many thanks. -- JFB
Jean-François Brouillet wrote:
http://boost.org/libs/smart_ptr/intrusive_ptr.html#constructors
The above link only has this to say:
constructors
intrusive_ptr(); // never throws Postconditions: get() == 0.
Throws: nothing.
intrusive_ptr(T * p, bool add_ref = true); Effects: if(p != 0 && add_ref) intrusive_ptr_add_ref(p);.
Postconditions: get() == p.
[...] Right, so you can't call it a bug if the implementation does exactly as stated. ;-)
That settles it then.
I just cannot use intrusive_ptr. Since shared_ptr is already ruled out, well .... roll-my-own-time, I guess ...
Too bad for my use of boost.
<shrug>
I'm pretty sure that a shared_ptr-based port can closely approximate the
original, but a custom smart pointer would probably be a better choice for
idiomatic Java code.
As for intrusive_ptr, its main strength is that it has the size of a
pointer. Adding a boolean would likely meet serious opposition.
Your requirements are very interesting, however, and point to a
shared/intrusive hybrid along the lines of:
template<class T> class intrusive_ptr_2
{
T * px_;
intrusive_ptr<Object> pn_;
// insert smart pointer boilerplate here ;-)
};
It's an intrusive pointer that's close to shared_ptr in expressive power;
you can use it to point to a subobject, for example.
Extending intrusive_ptr to accommodate the above along the lines of
template
What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do.
Bad assumption with smart pointers. Ctors don't allocate resources, yet dtors deallocate them.
Which might only indicate that they are not so "smart" after all... ;-)
Clever quips aside, it indicates that your design principle doesn't generalize.
I fail to see the "generality" in the current design. An example where it actually makes sense to not "addRef" on ctor but to "remRef" on dtor would be most welcome.
If you're arguing that the design of intrusive_ptr should be changed,
I don't have such pretensions. I was only suggesting to *rename* the parameter ``addRef'' into something less confusing because of the asymmetric behavior. Initially, I thought it was a *bug*, hence the title of this thread. I was told that it is not so, and that what I call a bug is actually a design feature. That's fine with me. Except for the confusing name, first, and the inability anyone, so far, has had to demonstrate a useful example where this asymmetric behavior makes sense. I'm all hears if you can convince me that the only road-block between my goals and the boost xxxx_ptr family is just my lack of understanding.
Whatever specific change you're arguing for may have merit, but not on the basis of ctor/dtor symmetry.
That's an *opinion*, probably as valid as mine, which is the opposite, regardless of which has the most "mind-share". My stance is that symmetry is at the very heart of the C++ object model. "destruct" is the precise opposite to "construct". You can break it on whatever grounds you find suitable, but you can't expect people to have an easy time a) not being surprised, and b) understanding what was the overriding reason. Many thanks. -- JFB
Jean-François Brouillet
What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do.
Bad assumption with smart pointers. Ctors don't allocate resources, yet dtors deallocate them.
Which might only indicate that they are not so "smart" after all... ;-)
Clever quips aside, it indicates that your design principle doesn't generalize.
I fail to see the "generality" in the current design.
I wasn't talking about generality of design. I was talking about whether the design principle that ctors and dtors must be opposites holds up in general.
An example where it actually makes sense to not "addRef" on ctor but to "remRef" on dtor would be most welcome.
I have no opinion about that issue.
If you're arguing that the design of intrusive_ptr should be changed,
I don't have such pretensions. I was only suggesting to *rename* the parameter ``addRef'' into something less confusing because of the asymmetric behavior.
Naming is part of the design.
Initially, I thought it was a *bug*, hence the title of this thread. I was told that it is not so, and that what I call a bug is actually a design feature. That's fine with me. Except for the confusing name, first, and the inability anyone, so far, has had to demonstrate a useful example where this asymmetric behavior makes sense.
I'm all hears if you can convince me that the only road-block between my goals and the boost xxxx_ptr family is just my lack of understanding.
I don't have such pretensions ;-)
Whatever specific change you're arguing for may have merit, but not on the basis of ctor/dtor symmetry.
That's an *opinion*, probably as valid as mine, which is the opposite, regardless of which has the most "mind-share".
My stance is that symmetry is at the very heart of the C++ object model. "destruct" is the precise opposite to "construct".
In most cases, that's true.
You can break it on whatever grounds you find suitable, but you can't expect people to have an easy time a) not being surprised, and b) understanding what was the overriding reason.
Fair enough. P.S. Your use of quotation marks comes off as rather needlessly sarcastic and disparaging of those you're corresponding with. -- Dave Abrahams Boost Consulting www.boost-consulting.com
So setting add_ref to false does not suggest that intrusive_ptr_add_ref will not be called?
Oh yes it does. What is does NOT suggest is that ~intrusive_ptr WILL call intrusive_ptr_release() MO MATTER WHAT That is this asymmetry which is cause of confusion, and for which I've still got a hard time figuring out a valid use case that would require this rather odd behavior. Let's assume we had intrusive_ptr2 with _exactly_ the same interface as intrusive_ptr but with the added behavior that if the constructor is called with "false", then the destructor does NOT call intrusive_ptr_release. What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do. Why break the assumption held at the core of C++, that constructors and destructors have a mirror behavior w.r.t each other?
We already went over this. You can package 'this' in a shared_ptr<> in a member function by using enable_shared_from_this<>. You can package 'this ' in a shared_ptr<> in the constructor by using a null_deleter if the shared_ptr<> will not be used outside the class, so it can't outlive it.
I'm surely dense, but the problem I had what that each shared_ptr initially derived from``this'' (call this "A") consitutes the root of a graph for ALL subsequent shared_ptr derived from "A" (through assignment or copy construction), but each such root has its OWN IDEA about the reference count, which doesn't match the idea of any other such root. While I'm open to use "null_deleter" and all that zoo for *implemeting* a full-proof envelope, it is just not my intention to expose any "null_deleter", "enable_shared_from_this" and other whatnots to client code. Inside Object, and in the surrounding infrastructure, I'm prepared to do whatever it takes. Outside Object, only envelopes should be visible, both syntactically and semantically. And that envelope MUST behave the way a standard T * does in C++ in the presence of Boehm's GC (minus its idiosyncratic problems)
You can't package 'this' in a shared_ptr<> in the constructor and pass it to someone else; you can do this in a static create() function. However, ...
Another nail in the shared_ptr coffin :(
... if you want to stay true to the original Java code, GC may be the only choice. Few smart pointers can cope with user code of the form
xxx_ptr p1( new X ); xxx_ptr p2( new X );
p1->set_callback( p2 ); p2->set_callback( p1 );
That's why I'm very grateful you provided me with an URL to cyclic_ptr! I'm still wrapping my head around the code, but from the outset, this is the closest I've come to my requirements, so far.
Even if you take care of every 'this', you can't stop people from creatin g circular dependencies.
cyclic_ptr is precisely supposed to handle this case.
C++ code is usually designed from the ground up to avoid these scenarios
I don't have that luxury :-( Many thanks for your time. -- JFB
Jean-François Brouillet
So setting add_ref to false does not suggest that intrusive_ptr_add_ref will not be called?
Oh yes it does. What is does NOT suggest is that ~intrusive_ptr WILL call intrusive_ptr_release() MO MATTER WHAT
That is this asymmetry which is cause of confusion, and for which I've still got a hard time figuring out a valid use case that would require this rather odd behavior.
Let's assume we had intrusive_ptr2 with _exactly_ the same interface as intrusive_ptr but with the added behavior that if the constructor is called with "false", then the destructor does NOT call intrusive_ptr_release.
What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do.
Bad assumption with smart pointers. Ctors don't allocate resources, yet dtors deallocate them. -- Dave Abrahams Boost Consulting www.boost-consulting.com
What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do.
Bad assumption with smart pointers. Ctors don't allocate resources, yet dtors deallocate them.
Which might only indicate that they are not so "smart" after all... ;-) -- JFB
Jean-François Brouillet
What is the name you would choose for the "addRef" parameter? "managed" ? Or maybe, simply "addRef" after all, since you would _assume_ by virtue of the symmetric behaviour of a destructor w.r.t a constructor, that whatever the constructor does, the destructor un-does it, and whatever the constructor does NOT do, the destructor does NOT un-do.
Bad assumption with smart pointers. Ctors don't allocate resources, yet dtors deallocate them.
Which might only indicate that they are not so "smart" after all... ;-)
Clever quips aside, it indicates that your design principle doesn't generalize. If you're arguing that the design of intrusive_ptr should be changed, the basic idea that smart pointers take over management of already-allocated resources must be "smart" enough for you. Whatever specific change you're arguing for may have merit, but not on the basis of ctor/dtor symmetry. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (3)
-
David Abrahams
-
Jean-François Brouillet
-
Peter Dimov