[shared_ptr] enable_shared_from_any ?

Many apologies if this has already been debated and discarded, but I have been using boost for a little while now, and most specifically boost::shared_ptr. I recently found enable_shared_from_this{2} and, in SVN, enable_shared_from_raw, and while both of these are extremely useful, ISTM they do not go quite far enough. Specifically, what I would like to see is a solution to the following code: C1 *p = new C1(); shared_ptr<C1> ptr(p); ... ...some time later.... shared_ptr<C1> ptr2<p>; and the resulting mess because of double-deallocation. Yes, one solution id discipline, but for a library developer, a better solution might be robustness in the face of (mild) ignorance or misuse. And yes, this is sloppy, but in our case we have a set of interfaces that want to implement variant function return types based on derived class pointers, and we can not do that by returning shared_ptr objects. We need to return raw pointers, then rewrap them. One solution is shared_from_this etc. Another is to extend shared_from_this so that *any* shared_ptr created from and object that inherits, say, enable_shared_from_any, will always get a shared pointer that works as one might expect. To this end we have built a (trivial) class that inherits from shared_ptr<T> and a matching base class, that together function to achieve what we need. The only real problem is we have to duplicate some parts of shared_ptr (constructors and operators mainly), the second problem is that not being experts in the very fine details of templates, boost, and cross-platform development, it would be extremely convenient if this functionality were implemented within shred_ptr in a similar way to the way enable_shared_from_this is done -- ie. only for those classes that implement an appropriate base class. The third problem is that because it does not have access to shared_ptr internals, it is not as efficient as it might be. Is submitting a patch to shared_ptr the appropriate way forward? Has this been discussed, debated and discarded before? If there is interest I am very happy to submit a patch, and I would also welcome any pointers (no pun intended) as to where the disadvantages and advantages of this kind of approach have been discussed before.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Saturday 15 May 2010, Philip Warner wrote:
And yes, this is sloppy, but in our case we have a set of interfaces that want to implement variant function return types based on derived class pointers, and we can not do that by returning shared_ptr objects. We need to return raw pointers, then rewrap them.
Are you saying you want to have a covariant return types with shared_ptr? Have you considered doing something like the following (completely untested): class Base { typedef X FReturnType; private: template<typename T> friend shared_ptr<T::FReturnType> g(T &t); virtual FReturnType* f(); }; class Derived: public Base { public: typedef DerivedFromX FReturnType; private: template<typename T> friend shared_ptr<T::FReturnType> g(T &t); virtual FReturnType* f(); } template<typename T> shared_ptr<typename T::FReturnType> g(T &t) { return shared_ptr<typename T::FReturnType>(t.f()); } -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkvxQqIACgkQ5vihyNWuA4WqXACcCVjVolqyngjpqlZyIsuN6bTR XY4Anj5sz8ZrcZ7l/Eih+iPsKYa7nGyY =w5TK -----END PGP SIGNATURE-----

On 17/05/2010 11:20 PM, Frank Mori Hess wrote:
Are you saying you want to have a covariant return types with shared_ptr?
Among other things, yes. But the bulk of my message was effectively suggesting replacing enable_shared_from_this{2} with enable_shared_from_any to allow safe creation of shared_ptrs from any pointer to an object that inherits from enable_shared_from_any. I have not tested your specific example, but it looks a lot like one of the many attempts we have made in the past to return covariant pointers. AFAICT, for good reasons, it is just not possible.

On Monday 17 May 2010, Philip Warner wrote:
Among other things, yes. But the bulk of my message was effectively suggesting replacing enable_shared_from_this{2} with enable_shared_from_any to allow safe creation of shared_ptrs from any pointer to an object that inherits from enable_shared_from_any.
I have not tested your specific example, but it looks a lot like one of the many attempts we have made in the past to return covariant pointers. AFAICT, for good reasons, it is just not possible.
The idea does work. I've used it before and it provides the same functionality as covariant return types, although it's obviously more of a pain to implement. There are other variants of it: http://lists.boost.org/boost-users/2003/02/2996.php When I said it was untested, I only meant the specific illustrative code I put in the email was untested and may not even compile. It seems like you could use weak_from_raw() to get the behavior you want, by testing if the returned weak_ptr is expired or not, to decide if you should pass ownership of the object to a new shared_ptr.

On 18/05/2010 10:33 AM, Frank Mori Hess wrote:
The idea does work. I've used it before and it provides the same functionality as covariant return types, although it's obviously more of a pain to implement.
Sorry, I did not mean it "does not work as a concept", merely that it does not achieve the pain-free implementation what works with any "user-created" shared_ptr, which is what I was trying to achieve: from my original post:
Specifically, what I would like to see is a solution to the following code:
C1 *p = new C1(); shared_ptr<C1> ptr(p); ... ...some time later.... shared_ptr<C1> ptr2<p>;
and the resulting mess because of double-deallocation.
if I have this then I can return raw pointers from covariant functions then wrap the result in a shared pointer again and it won't matter of the library also has it wrapped in a shared pointer. The only requirement will be that they use a compatible version of boost.
It seems like you could use weak_from_raw() to get the behavior you want, by testing if the returned weak_ptr is expired or not, to decide if you should pass ownership of the object to a new shared_ptr.
Indeed, but it does not really solve the problem in the general case (without discipline and pitfalls). I am very happy to provide a patch that: - adds a class, enable_shared_from_any, that can be inherited from - updates shared_ptr to work in the above code, if C1 inherits from enable_shared_from_any What we have at the moment is something like 'SafeSharedPtr' that inherits from 'shared_ptr' which does what I want, but which is less efficient than a native implementation would be

Specifically, what I would like to see is a solution to the following code:
C1 *p = new C1(); shared_ptr<C1> ptr(p); ... ...some time later.... shared_ptr<C1> ptr2<p>;
and the resulting mess because of double-deallocation.
...
I am very happy to provide a patch that:
- adds a class, enable_shared_from_any, that can be inherited from - updates shared_ptr to work in the above code, if C1 inherits from enable_shared_from_any
If people are interested, the patch is attached. It includes a patch file as well as final versions of the files. 'sp_enable_shared_from_this' now returns a value; for old-style shared_ptr objects this is a constant 'false'. Allocation of the shared_counter object has been deferred until the 'sp_enable_shared_from_this'; if the result is true, it is assumed that the shared_counter has been allocated/copied. Otherwise, the shared_counter is allocated in the constructor. There used to be an extra 'sp_enable_shared_from_this'for each of the possible base classes (enable_shared_from_this and enable_shared_from_this2); now there are three for each possible base class because we have to pass the various possible allocator/deallocator parameters to the template functions. There is scope here to remove both enable_shared_from_this and enable_shared_from_this2 since, I believe, enable_shared_from_any is capable of performing both the tasks that these were intended for, but I have not done this. I will do so if people prefer. There are a number of different ways this could be done for backward compatibility. The patch is against 1.43.0.

Philip Warner wrote:
On 18/05/2010 10:33 AM, Frank Mori Hess wrote:
from my original post:
Specifically, what I would like to see is a solution to the following code:
C1 *p = new C1(); shared_ptr<C1> ptr(p); ... ...some time later.... shared_ptr<C1> ptr2<p>;
and the resulting mess because of double-deallocation.
I am very happy to provide a patch that:
- adds a class, enable_shared_from_any, that can be inherited from - updates shared_ptr to work in the above code, if C1 inherits from enable_shared_from_any
What we have at the moment is something like 'SafeSharedPtr' that inherits from 'shared_ptr' which does what I want, but which is less efficient than a native implementation would be
I don't see how inheriting from a special base class helps. Instantiating instances of a special type is no safer than calling a different function for this purpose. The issue is merely one of supplying external knowledge that the new shared_ptr should not assume sole ownership of the raw pointer. enable_shared_from_this was created to express a special use case of, as you point out, a more general need. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 18/05/2010 10:22 PM, Stewart, Robert wrote:
I don't see how inheriting from a special base class helps. Instantiating instances of a special type is no safer than calling a different function for this purpose. The issue is merely one of supplying external knowledge that the new shared_ptr should not assume sole ownership of the raw pointer. enable_shared_from_this was created to express a special use case of, as you point out, a more general need.
I have probably complicated matters by describing the second-best solution we have adopted, rather than just submitting the patch (which I have subsequently done in a later post). Please just ignore my unnecessary digression. The later post implements enabled_shared_from_any, which is even more general than enable_shared_from_this. It allows us to return raw pointers to variant types from a class defined in a library, that have been wrapped by shared pointers in the library, and further allows the caller to re-wrap these objects in shared pointers, all without risking double-deallocation. An example might help; we have class A; class B : class A; class SomeOtherBase { virtual A* getThing(); } class SomeOtherDerived : class SomeOtherBase { virtual B* getThing(); } internally, the library that provides SomeOtherBase and SomeOtherDerived can use shared_ptrs to wrap these objects internally, can also return covariant function results and the caller can then wrap the resulting pointer in a shared_ptr safely. Also, as noted in my later post, the implementation can easily be modified to provide the entire functionality of enable_shared_from_this[2]. In our application a very large chunk of the code only cares about the base classes (A and SomeOtherBase), but a small and important part wants to know about the derived types so returning variant function results is important to us. I hope I have not just made matters less clear!
participants (4)
-
Frank Mori Hess
-
Frank Mori Hess
-
Philip Warner
-
Stewart, Robert