[SmartPtr] enable_shared_from_raw
Hi all, Firstly, any chance this could get some documentation? Is the author still around? I'm thinking about using this at the moment for two separate reasons (and ways in which enable_shared_from_this seems to fall short): 1. shared_from_this can't be used from the constructor (requiring factory-method-init shenanigans); reportedly (https://groups.google.com/forum/#!topic/boost-list/8JWFZ56ry6k) shared_from_raw can, although I can't find specific details on the caveats mentioned. 2. What I *really* want to be able to do is to create a child object (most commonly a callback functor) that contains a weak_ptr, and it seems silly (and badly performant) to use the usual factory-init pattern to call shared_from_this just to degrade it back to a weak_ptr. And it should be possible to obtain that weak_ptr before any shared_ptr exists, and "fix" it later to point at the real shared_ptr, especially if it is known that the weak_ptr will never actually be used until that real shared_ptr exists. enable_shared_from_raw almost seems to fit the bill (according to http://stackoverflow.com/questions/22184772/why-is-boostenable-shared-from-r...), but I encountered the same problem as in this post (http://lists.boost.org/boost-users/2013/05/78833.php); it works with shared_from_raw but not weak_from_raw. However this change: https://github.com/boostorg/smart_ptr/pull/8 fixes that. It looks like the two calls went down different code paths for some reason I can't fathom. Making this change (or the similar-but-better-style one mentioned in the PR discussion) makes the above scenario work as expected. I'm not sure if there was some specific reason to not do this before or if it was just an oversight. Any chance this can make it into 1.56? Or is it too late for that? Regards, Gavin Lambert
On Thu, Jul 24, 2014 at 12:53 AM, Gavin Lambert
Hi all,
Firstly, any chance this could get some documentation? Is the author still around?
I'm still around, although not super-active. I'm personally neutral towards the code and whether its use should be encouraged. It has some pitfalls for the unwary, but there also seems to be recurring desire for the functionality. Also, it doesn't need CRTP they way enable_shared_from_this does.
I'm thinking about using this at the moment for two separate reasons (and ways in which enable_shared_from_this seems to fall short):
1. shared_from_this can't be used from the constructor (requiring factory-method-init shenanigans); reportedly (https://groups.google.com/forum/#!topic/boost-list/8JWFZ56ry6k) shared_from_raw can, although I can't find specific details on the caveats mentioned.
The two dangers that come to mind are: 1) you throw out of your constructor after handing out shared_ptrs/weak_ptrs, which reference an object which ultimately was never constructed. 2) you call shared_from_raw, but never actually give ownership of the object to an external shared_ptr. This results in the object holding a shared_ptr to itself, potentially indefinitely.
2. What I *really* want to be able to do is to create a child object (most commonly a callback functor) that contains a weak_ptr, and it seems silly (and badly performant) to use the usual factory-init pattern to call shared_from_this just to degrade it back to a weak_ptr.
And it should be possible to obtain that weak_ptr before any shared_ptr exists, and "fix" it later to point at the real shared_ptr, especially if it is known that the weak_ptr will never actually be used until that real shared_ptr exists.
enable_shared_from_raw almost seems to fit the bill (according to http://stackoverflow.com/questions/22184772/why-is-boostenable-shared-from-r...), but I encountered the same problem as in this post (http://lists.boost.org/boost-users/2013/05/78833.php); it works with shared_from_raw but not weak_from_raw.
However this change: https://github.com/boostorg/smart_ptr/pull/8 fixes that. It looks like the two calls went down different code paths for some reason I can't fathom.
I don't remember what the rationale was for the current behavior of weak_from_raw, or if I was the one who decided on that behavior. However, it seems like the current behavior could be useful in that it provides a way to query if an external shared_ptr ever actually took ownership of the object (or at least shared_from_raw has been used, which commits the coder to giving ownership to an external shared_ptr at some point).
On 25/07/2014 10:50, Frank Mori Hess wrote:
Also, it doesn't need CRTP they way enable_shared_from_this does.
I was wondering about that. Does it handle the case where a base class calls shared_from_raw with its own "this" pointer but the derived pointer is the one that ends up actually being externally owned? I think that (or something multi-inheritance-related) was the reason ESFT required CRTP? On a basic read-through of the code I *think* it's fine, but it never hurts to get confirmation. :)
enable_shared_from_raw almost seems to fit the bill (according to http://stackoverflow.com/questions/22184772/why-is-boostenable-shared-from-r...), but I encountered the same problem as in this post (http://lists.boost.org/boost-users/2013/05/78833.php); it works with shared_from_raw but not weak_from_raw.
However this change: https://github.com/boostorg/smart_ptr/pull/8 fixes that. It looks like the two calls went down different code paths for some reason I can't fathom.
I don't remember what the rationale was for the current behavior of weak_from_raw, or if I was the one who decided on that behavior. However, it seems like the current behavior could be useful in that it provides a way to query if an external shared_ptr ever actually took ownership of the object (or at least shared_from_raw has been used, which commits the coder to giving ownership to an external shared_ptr at some point).
I don't think there's any difference in that commitment with shared_from_raw vs. weak_from_raw. Both of them are insufficient to give a "real" owning pointer by themselves -- according to the stackoverflow answer, any pointer obtained from either of these methods cannot "actually" own the pointer, and instead the real raw pointer has to be externally passed to a separate shared_ptr instance. So calling shared_from_raw from within the constructor gives you no additional guarantees (except that it actually works, at the moment, while weak_from_raw does not until this patch is applied). Neither provide a way to tell from the outside whether that second "real" shared_ptr was ever constructed -- to do that, you'd have to check that weak_this_ was *not* expired and shared_this_ *was* empty, if I'm following the code correctly. (Or conversely, you're in the danger state whenever shared_this_ is non-empty.) Maybe it would be useful to explicitly expose a way to query for that? Though to be honest other than unit tests and debugging I doubt anything would actually use it (but the same could be said of shared_ptr's use_count). If you think it will help, I could post a separate PR with the other style that I mentioned, which would make it more obvious that there's little difference between shared_from_raw and weak_from_raw, and then you (or whoever is in charge of SharedPtr at the moment) can choose which you prefer. I just initially went with the one-liner in the hopes that it'd be easier to get that into 1.56.
On 07/24/2014 06:53 AM, Gavin Lambert wrote:
1. shared_from_this can't be used from the constructor (requiring factory-method-init shenanigans); reportedly
If you are using C++11, then your factory can be made generic by using
perfect forwarding:
class my_class : enable_shared_from_this
And it should be possible to obtain that weak_ptr before any shared_ptr exists, and "fix" it later to point at the real shared_ptr, especially if it is known that the weak_ptr will never actually be used until that real shared_ptr exists.
This sounds a bit risky if the last assumption does not hold.
On 28/07/2014 01:03, Bjorn Reese wrote:
On 07/24/2014 06:53 AM, Gavin Lambert wrote:
1. shared_from_this can't be used from the constructor (requiring factory-method-init shenanigans); reportedly
If you are using C++11, then your factory can be made generic by using perfect forwarding:
While that's somewhat an improvement on manually specifying parameters, it still counts as factory-method-init shenanigans. (Only somewhat, because this sort of thing tends to confuse IDE-provided parameter-info hints, which makes it harder to call.)
And it should be possible to obtain that weak_ptr before any shared_ptr exists, and "fix" it later to point at the real shared_ptr, especially if it is known that the weak_ptr will never actually be used until that real shared_ptr exists.
This sounds a bit risky if the last assumption does not hold.
It's not any more risky than the current behaviour of shared_from_raw. There's nothing incomplete or invalid about the weak_ptr thus obtained, and there's no reason why other code couldn't actually use it or even lock it back to a shared_ptr and save that somewhere. It just points at the internal self-cyclic pointer, which will create a memory leak if it's not cleared by having code outside of the constructor assign the raw pointer to a "real" shared_ptr. As soon as that occurs, everything is happy again. But at no point is anything invalid or unusable.
participants (3)
-
Bjorn Reese
-
Frank Mori Hess
-
Gavin Lambert