[smart_ptr] enable_shared_from_this::shared_from_this( this );

I had yet another idea which might improve the situation for the virtual inheritance problem that my optimized implementation of enable_shared_from_this<T> has. Additionally, the solution provides another useful feature. Currently, shared_from_this() has no parameter and thus needs to either store the pointer to T* (as part of the internal weak_ptr<T>) like the 1.35.0-solution does, or it needs to use the this-pointer. The this-pointer for the class is of type enable_shared_from_this<T>*, not of type T* - this is why my improved version needs to static-cast the this-pointer, which in turn requires a non-virtual inheritance between enable_shared_from_this<T> and T. The problem can be solved by either extending or modifying the interface: When we add the following method(s): template< typename U > shared_ptr<U> shared_from_this( U* u ); template< typename U > shared_ptr<U const> shared_from_this( U const* u ); it's then possible for a method of T to call shared_from_this(this) instead of shared_from_this() to gain a shared_ptr<T>. Additionally, the interface is generally useful, as it could be called with other pointers, e.g. to sub-objects (member variables) of T. It's IMHO a nice match to the shared_ptr-constructor which takes a shared_ptr and an additional plain pointer. Peter, Frank, what do you think?

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 16 April 2008 15:46 pm, Daniel Frey wrote:
The problem can be solved by either extending or modifying the interface: When we add the following method(s):
template< typename U > shared_ptr<U> shared_from_this( U* u );
template< typename U > shared_ptr<U const> shared_from_this( U const* u );
it's then possible for a method of T to call shared_from_this(this) instead of shared_from_this() to gain a shared_ptr<T>.
Additionally, the interface is generally useful, as it could be called with other pointers, e.g. to sub-objects (member variables) of T. It's IMHO a nice match to the shared_ptr-constructor which takes a shared_ptr and an additional plain pointer.
Wouldn't it be safer to make it a non-template? The aliasing constructor can be easily misused. If the user really wants to use the aliasing constructor, they could always pass the return value from shared_from_this() to the aliasing constructor. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIBmyj5vihyNWuA4URAs0ZAJ466WCMmJ5EZz98n8su/1+IYJ4MyACeOtMg ExRZ6AWIn6SQHOmfDAWjnV8= =gKNc -----END PGP SIGNATURE-----

On Wed, 2008-04-16 at 17:16 -0400, Frank Mori Hess wrote:
Daniel Frey: template< typename U > shared_ptr<U> shared_from_this( U* u );
template< typename U > shared_ptr<U const> shared_from_this( U const* u );
Wouldn't it be safer to make it a non-template? The aliasing constructor can be easily misused. If the user really wants to use the aliasing constructor, they could always pass the return value from shared_from_this() to the aliasing constructor.
In which case it is useless, as it cannot accept "this" and therefore doesn't solve any problem at all. Passing this allows you to pass the type of "this", which is the derived type T, not the type of enable_shared_from_this<T>. Regards, Daniel

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Thursday 17 April 2008 01:42 am, Daniel Frey wrote:
On Wed, 2008-04-16 at 17:16 -0400, Frank Mori Hess wrote:
Daniel Frey: template< typename U > shared_ptr<U> shared_from_this( U* u );
template< typename U > shared_ptr<U const> shared_from_this( U const* u );
Wouldn't it be safer to make it a non-template? The aliasing constructor can be easily misused. If the user really wants to use the aliasing constructor, they could always pass the return value from shared_from_this() to the aliasing constructor.
In which case it is useless, as it cannot accept "this" and therefore doesn't solve any problem at all. Passing this allows you to pass the type of "this", which is the derived type T, not the type of enable_shared_from_this<T>.
No, what I was trying to convey is you have the T type inside enabled_shared_from_this<T>. To be clear: template<typename T> enable_shared_from_this { shared_ptr<T> shared_from_this(T *t); //... }; Whether you remove the template parameter from the shared_from_this method, or or follow Peter's suggestion and remove it from enable_shared_from_this class, it seems like you have one more template than you need. Also, there is no reason not to add a simple helper free function: template<typename T> shared_ptr<T> shared_from_this(T *t) { return t->shared_from_this(t); } which would provide some convenience and a sanity check for the common case usage. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIB09q5vihyNWuA4URAsk4AJ9XMFrEZCYA/y/6ujGQM0SxoJTmIwCdErKS DjD2nFRshjUQl/3HRjwLEVA= =dSld -----END PGP SIGNATURE-----

Frank Mori Hess wrote:
Also, there is no reason not to add a simple helper free function:
template<typename T> shared_ptr<T> shared_from_this(T *t) { return t->shared_from_this(t); }
Not bad. This could be made a friend of esft and the only (public) accessor.

On Thu, 2008-04-17 at 09:23 -0400, Frank Mori Hess wrote:
No, what I was trying to convey is you have the T type inside enabled_shared_from_this<T>. To be clear:
template<typename T> enable_shared_from_this { shared_ptr<T> shared_from_this(T *t); //... };
Ah, I see.
Also, there is no reason not to add a simple helper free function:
template<typename T> shared_ptr<T> shared_from_this(T *t) { return t->shared_from_this(t); }
which would provide some convenience and a sanity check for the common case usage.
And as Peter suggested, this could be the only public interface. I really like that idea and direction. :) Although it's a breaking change, it seems cleaner and more powerful to me. Peter, what's your point of view on this type of breaking change to enable_shared_from_this' interface? Do you seriously consider it? Regards, Daniel

Daniel Frey wrote:
Although it's a breaking change, it seems cleaner and more powerful to me. Peter, what's your point of view on this type of breaking change to enable_shared_from_this' interface? Do you seriously consider it?
We need to keep in mind that std::tr1::enable_shared_from_this and std::enable_shared_from_this are still specified in the "old way", so it seems to me that even if we decide to go ahead with this change, we still have to keep the 1.35-like base class for compatibility with TR1 and C++0x.

On Thu, 2008-04-17 at 18:04 +0300, Peter Dimov wrote:
Daniel Frey wrote:
Although it's a breaking change, it seems cleaner and more powerful to me. Peter, what's your point of view on this type of breaking change to enable_shared_from_this' interface? Do you seriously consider it?
We need to keep in mind that std::tr1::enable_shared_from_this and std::enable_shared_from_this are still specified in the "old way", so it seems to me that even if we decide to go ahead with this change, we still have to keep the 1.35-like base class for compatibility with TR1 and C++0x.
I think it's possible to have shared_from_this_base (one plain pointer overhead, not a template), which can be used with either the free function boost::shared_from_this(x) or as a base class for enable_shared_from_this (template, 1.35.0 or trunk version). The 1.35.0 version would then specialize sp_accept_owner, store the plain pointer and shared_from_this() could call the free function in it's implementation. As an experiment, should I start to commit small chunks into trunk? I have most things in my local copy of trunk anyway. If you monitor the changes, we could revert them if you object to something (which is a truly object oriented approach ;) Regards, Daniel

On Thu, 2008-04-17 at 18:04 +0300, Peter Dimov wrote:
We need to keep in mind that std::tr1::enable_shared_from_this and std::enable_shared_from_this are still specified in the "old way", so it seems to me that even if we decide to go ahead with this change, we still have to keep the 1.35-like base class for compatibility with TR1 and C++0x.
If it's not too late, std::enable_shared_from_this (C++0x) could be the base class I called shared_from_this_base in the other mail and std::tr1::enable_shared_from_this (TR1) could use it to be implemented. I see that it's a bit unfortunate, but as long as C++0x is not yet finished we should IMHO consider it. Regards, Daniel

Daniel Frey: ...
template< typename U > shared_ptr<U> shared_from_this( U* u );
This would even allow you to drop the template parameter of esft_light. Interesting. Getting back to your proposed changes, I unfortunately can't think of a mutually satisfactory way to integrate them. I don't like shared_ptr( shared_count & pn, Y* px ); because it moves from the lvalue pn, auto_ptr style. The proper interface wouldn't do that; it would copy from lvalues and move from rvalues by default, while still providing an explicit way to move from lvalues. On the other hand, the safe C++03 approximation of the proper interface is inefficient, and I don't want to introduce move semantics emulation just for that semi-internal constructor.

On Thu, 2008-04-17 at 00:22 +0300, Peter Dimov wrote:
Daniel Frey: ...
template< typename U > shared_ptr<U> shared_from_this( U* u );
This would even allow you to drop the template parameter of esft_light. Interesting.
That would only be possible if I drop shared_from_this(), which would break the interface for every use case. OTOH, if you consider a breaking change to the interface, it's easier for the user as he simply passes the this-pointer in all cases (except where he wants to pass a different pointer) and doesn't need to make a decision. The possibility of passing something else than "this"
Getting back to your proposed changes, I unfortunately can't think of a mutually satisfactory way to integrate them. I don't like
shared_ptr( shared_count & pn, Y* px );
because it moves from the lvalue pn, auto_ptr style. The proper interface wouldn't do that; it would copy from lvalues and move from rvalues by default, while still providing an explicit way to move from lvalues.
In fact my copy of the trunk already contains const shared_count& and a shared_count&& overload, since I also felt that taking and modifying a reference is too "dirty". I think I wouldn't mind the additional overhead in case of C++03, as I think that some other cases are optimized anyway, so it might not be that bad. If you like, I could post a patch and an analysis of all cases where it optimizes or pessimizes something. Adding yet another overload for the auto_ptr-style might not be worth it. Regards, Daniel

On Thu, 2008-04-17 at 07:44 +0200, I wrote:
That would only be possible if I drop shared_from_this(), which would break the interface for every use case. OTOH, if you consider a breaking change to the interface, it's easier for the user as he simply passes the this-pointer in all cases (except where he wants to pass a different pointer) and doesn't need to make a decision. The possibility of passing something else than "this"
...is no worse or dangerous than having the shared_ptr-ctor which takes a shared_ptr and a plain pointer AFAICT. (Yes, I should have finished this sentence before sending the first mail ;)
participants (3)
-
Daniel Frey
-
Frank Mori Hess
-
Peter Dimov