GNOME Bugzilla – Bug 788646
Mixing C & C++: Clarify ownership of wrap() result
Last modified: 2017-11-15 14:47:25 UTC
Maybe no one else found this as vague as I did, but I think it’d be much better to be clear how wrap() works and what users should (not) do about lifetime, etc.
Created attachment 361103 [details] [review] Mixing C & C++: Clarify ownership of wrap() result Make it clear that wrap() returns a pointer to an instance owned by glibmm, so users should not try to delete it. Also mention the effect on refcount, i.e. nothing if (!take_copy). Finally, show the C++ instance actually being used (though a better example would show more advantage). While here, I split the long paragraphs for readability & to help git, & add some missing <classname>/<function> tags for readability/semantics.
The ownership of the C++ wrapper is more complicated than you describe. Perhaps that's why it's not fully described in the tutorial. Perhaps it's better to say that the C++ instance is bound to the C instance. This is done with a GQuark in the C instance which contains the address of the C++ instance. And Glib::ObjectBase contains GObject* gobject_. When the C instance is finalized, the C++ wrapper instance is informed by a call to Glib::ObjectBase::destroy_notify_callback_(). Who shall delete the C++ wrapper? There are 3 cases. 1. The C++ class does not inherit from Gtk::Object. Its pointer is stored in a Glib::RefPtr. The C++ instance is deleted when the C instance loses its last reference. See Glib::ObjectBase::destroy_notify_(). 2. The C++ class inherits from Gtk::Object. It's not "managed". Typically this is a C++ instance which is a member of another C++ instance, such as a Gtk::Button which is a member of a class derived from Gtk::Box. The creator of the C++ instance is responsible for deleting it. But don't delete it before you want the C instance to die. Gtk::Object's destructor calls g_object_run_dispose(c_instance). 3. The C++ class inherits from Gtk::Object. It's "managed". Usually this means that Gtk::manage(cxx_instance) has been called. The C++ instance is deleted when the C instance loses its last reference. See Gtk::Object::destroy_notify_(). Consider a wrap() function that corresponds to a C++ class that inherits from Gtk::Object. When it creates a new C++ wrapper instance, Gtk::Object::Object(GObject* castitem) is called. Gtk::Object::_init_unmanage() then decides if the new C++ instance is managed or not managed based on the result of a call to g_object_is_floating(c_instance). Floating ref == not managed; no floating ref == managed. All this is too much for the gtkmm tutorial. I hope it can be explained in a reasonably short form without being misleading.
Thanks for the details! So, the ownership of the C instance dictates whether the user should free the result of wrap(), and they should only delete the wrapper if the C instance was floating at the time they wrap()ped it? If I understood it correctly, that seems like a succinct enough summary - which is why I suspect I didn't :)
Let me try a slightly less succinct summary: The wrapper shall be explicitly deleted if - it's a widget or other class that inherits from Gtk::Object, and - the C instance is floating when the wrapper is created, and - Gtk::manage() is not called. (Don't delete the wrapper before you want the C instance to die.) In all other cases the wrapper is automatically deleted when the last reference of the C instance is dropped.
Daniel, I have pushed your patch, followed by a patch based on comment 4, to the master branch and the gtkmm-3-22 branch of gtkmm-documentation.