[SmartPointers] Determine interest in a new pointer wrapper class "flex_ptr"
Hello Boost Community, In the last few weeks, I heavily felt the lack of boost not having a more lightweight solution to a "not-necessarily-owning" pointer-wrapper than shared_ptr. shared_ptr is quite slow in the context that I need it to work. Furthermore it always is owning, even though ownership is shared in between instances. This is unhelpful, if you have an std::list of both non-owning and owning pointers. Hence, if you do not want to use shared_ptr all over your project, or especially want to interact with C, you need a not-necessarily-owning pointer-wrapper. This is why I collected some ideas on how to improve the situation. I came up with a little class named 'flex_ptr', that can hold both strong and weak references (therefore the name 'flexible', but it can also be 'the name of whatever proves itself to be good ...'). It keeps track of whether the held pointer is strong or weak. The Class doesn't need reference counting, since an Instance either is owning, or non-owning. Ownership is transferred between Instances using the default Move/Copy behavior. By Default the Constructor takes a pointer and treats it as a weak one. This is required, since this class aims on being used only at places, that are ought to be able to but not necessarily own objects. (Excuse me for the long sentence) A flex_ptr instance holding a strong pointer is created using a static non-member method named 'make_owning_flex', which accepts an rvalue pointer. As a result, it is not required to use the class at every single pointer occurrences in the project, but only at places, where the programmer wants to allow but not force strong references. The flex_ptr class is easily integrated into existing code using unique_ and shared_ptr's: One could think of - a constructor accepting a const lvalue reference unique_ptr, creating a (flex_ptr-instance holding a) weak reference, - a constructor accepting an rvalue unique_ptr, creating a strong pointer, that is owning an object it received from unique_ptr, - as well as a constructor taking a const lvalue reference shared_ptr, creating a weak pointer. Résumé: This pattern allows outright memory management, it uses less memory than shared_ptr (because no reference counting object required), therefore is faster, allows well-defined interactions similar to unique_ptr (especially at interaction with C) and thus fills a yet unclosed gap in the boost library as well as the Standard. Since this is (only) my personal opinion, I'd like to see how people feel about this 'gap' and think about this addition in the library (however the pattern may be implemented in the end). Regards, Jakob Riedle
On 23/03/2015 11:04, Jakob Riedle wrote:
In the last few weeks, I heavily felt the lack of boost not having a more lightweight solution to a "not-necessarily-owning" pointer-wrapper than shared_ptr.
shared_ptr is quite slow in the context that I need it to work.
Furthermore it always is owning, even though ownership is shared in between instances.
This is unhelpful, if you have an std::list of both non-owning and owning pointers.
Hence, if you do not want to use shared_ptr all over your project, or especially want to interact with C, you need a not-necessarily- owning pointer-wrapper.
While I agree that shared_ptr is sometimes heavier than I would like, I'm having a hard time wrapping my head around potential use cases of such a pointer. I'm not saying there aren't any, but all cases that I can think of having a pointer collection would use either all-owned (unique or shared) or none-owned (weak). A heterogeneous collection feels weird. Can you explain your envisaged use cases in more detail perhaps, and why you think shared_ptr/weak_ptr is not the right choice for those cases?
Can you explain your envisaged use cases in more detail perhaps, and why you think shared_ptr/weak_ptr is not the right choice for those cases?
My use case is the following: I'm currently writing an Operating System. It offers a DOM-Like GUI-Tree. If, suppose you have an arbitrary program running, that program creates a textbox and appends it to its main program-window. To make sure, the textbox gets deleted, the program either has to hold a reference to it and delete the object as soon as the program terminates, or it tells the window to take care of the Textbox and delete it, as soon as the window closes. You can imagine, if there is big bunch of GUI elements, using shared_ptr for all children nodes in all parents, is serious memory overhead and slows down the whole program. [This is especially the case with my hardware only offering 4MB of RAM.] The flex_ptr class makes it very easy and comfortable for a programmer to indicate, whether they want to take care of an object, or transfer ownership to the DOM-like GUI-Tree. So, what this pointer wrapper really is good for: - It is for platforms with limited hardware (when shared_ptr is no option, that is with many embedded/restricted platforms) - When Ownership is not always intended - For platforms with C-integration, since often, C-Code owns an object and you cannot give that object to existing libraries that use shared_ptr or unique_ptr's, because they would require you to give up ownership, that your C++-Code never had. Additionally: Taking ownership again from the GUI-Tree is possible through the following pattern: class GUINode: flex_ptr<GUINode> removeChild( GUINode* node ){ Unbind the child from this node. return the reference that I had to the child. } If the returned value of the function is not used, the child gets deleted as the temporary value gets destroyed (in case it was owning). On the other hand, the returned value can be stored in a variable, when you want to unbind the child from the parent, without deleting it.
On Monday 23 March 2015 10:19:00 Jakob Riedle wrote:
Can you explain your envisaged use cases in more detail perhaps, and why you think shared_ptr/weak_ptr is not the right choice for those cases? My use case is the following:
I'm currently writing an Operating System. It offers a DOM-Like GUI-Tree. If, suppose you have an arbitrary program running, that program creates a textbox and appends it to its main program-window. To make sure, the textbox gets deleted, the program either has to hold a reference to it and delete the object as soon as the program terminates, or it tells the window to take care of the Textbox and delete it, as soon as the window closes.
You can imagine, if there is big bunch of GUI elements, using shared_ptr for all children nodes in all parents, is serious memory overhead and slows down the whole program.
[This is especially the case with my hardware only offering 4MB of RAM.]
The flex_ptr class makes it very easy and comfortable for a programmer to indicate, whether they want to take care of an object, or transfer ownership to the DOM-like GUI-Tree.
So, what this pointer wrapper really is good for: - It is for platforms with limited hardware (when shared_ptr is no option, that is with many embedded/restricted platforms) - When Ownership is not always intended - For platforms with C-integration, since often, C-Code owns an object and you cannot give that object to existing libraries that use shared_ptr or unique_ptr's, because they would require you to give up ownership, that your C++-Code never had.
Additionally:
Taking ownership again from the GUI-Tree is possible through the following pattern:
class GUINode:
flex_ptr<GUINode> removeChild( GUINode* node ){ Unbind the child from this node. return the reference that I had to the child. }
If the returned value of the function is not used, the child gets deleted as the temporary value gets destroyed (in case it was owning).
On the other hand, the returned value can be stored in a variable, when you want to unbind the child from the parent, without deleting it.
Perhaps I'm missing the point but to me it looks like a dangerous kind of pointer. You have to state clearly the ownership model for the elements of your DOM structure. Otherwise working with the structure becomes very error prone. What happens if one piece of code creates an object and puts it into the DOM structure? Who owns the object (i.e. responsible for destroying it)? What happens when another piece of code obtains a reference to the object in the DOM? And what happens when whoever owns the object destroys it (while other references are still present)? Are multiple owners possible? To my mind, if your data model allows multiple more or less persistent references to a single object you will have to go with shared ownership pointers - shared_ptr or intrusive_ptr, the latter possibly being more efficient. Otherwise you have to guarantee there's only one owner of the object and all other references never outlive the owner. Based on your description, I suspect, you can't give that guarantee.
On Mon, Mar 23, 2015 at 6:00 PM, Olaf van der Spek
On Sun, Mar 22, 2015 at 11:04 PM, Jakob Riedle
wrote: Furthermore it always is owning, even though ownership is shared in between instances.
A null-deleter would make it basically non-owning..
Right, and with a custom deleter, I believe, one can implement what flex_ptr provides. However, this looks like misuse to me since unique_ptr is supposed to imply unique ownership.
On Tue, Mar 24, 2015 at 8:12 AM, Andrey Semashev
On Mon, Mar 23, 2015 at 6:00 PM, Olaf van der Spek
wrote: On Sun, Mar 22, 2015 at 11:04 PM, Jakob Riedle
wrote: Furthermore it always is owning, even though ownership is shared in between instances.
A null-deleter would make it basically non-owning..
Right, and with a custom deleter, I believe, one can implement what flex_ptr provides. However, this looks like misuse to me since unique_ptr is supposed to imply unique ownership.
We're talking about shared_ptr. -- Olaf
On Tue, Mar 24, 2015 at 11:14 AM, Olaf van der Spek
On Tue, Mar 24, 2015 at 8:12 AM, Andrey Semashev
wrote: On Mon, Mar 23, 2015 at 6:00 PM, Olaf van der Spek
wrote: On Sun, Mar 22, 2015 at 11:04 PM, Jakob Riedle
wrote: Furthermore it always is owning, even though ownership is shared in between instances.
A null-deleter would make it basically non-owning..
Right, and with a custom deleter, I believe, one can implement what flex_ptr provides. However, this looks like misuse to me since unique_ptr is supposed to imply unique ownership.
We're talking about shared_ptr.
Oh, right, sorry.
On Wed, Mar 25, 2015 at 8:55 AM, Jakob Riedle
A null-deleter would make it basically non-owning..
That doesn't solve Efficiency, but it allows for non-owning shared_ptr's. Thank you!
It doesn't, however, solve the underlying safety issues. Smart Pointers tend to be smart in two ways: - track ownership to correctly delete object - be safe With flex_ptr (or shared_ptr with custom deleter), how do you know that a non-null flex_ptr is pointing to valid memory or not. Or if it is valid on this line, but not the next, due to threading or something. Tony
On Mar 27, 2015, at 1:16 PM, Gottlob Frege
On Wed, Mar 25, 2015 at 8:55 AM, Jakob Riedle
wrote: A null-deleter would make it basically non-owning..
That doesn't solve Efficiency, but it allows for non-owning shared_ptr's. Thank you!
It doesn't, however, solve the underlying safety issues. Smart Pointers tend to be smart in two ways:
- track ownership to correctly delete object - be safe
With flex_ptr (or shared_ptr with custom deleter), how do you know that a non-null flex_ptr is pointing to valid memory or not. Or if it is valid on this line, but not the next, due to threading or something.
Consider using boost::intrusive_ptr. You can remove the object from intrusive_ptr's management by calling intrusive_ptr_add_ref() on it manually, and return it by calling intrusive_ptr_release(). Josh
participants (6)
-
Andrey Semashev
-
Gavin Lambert
-
Gottlob Frege
-
Jakob Riedle
-
Josh Juran
-
Olaf van der Spek