Non-managed (owning) `gtkmm` wrappers, of widgets added to containers, do not release ('leak') GTK/C instance when destroyed/deleted
I feel like I've missed an obvious discussion, but I can't find it. If I have, then apologies, and please enlighten me!
I am aware of #115 (comment 1430114) where @kjellahl said:
delete
on a managed widget in a container deletes both the C++ wrapper and the underlying C instance in gtkmm3.
In gtkmm4 it deletes only the C++ wrapper. The underlying C instance remains a child of the container widget. Use remove() and similar methods, notdelete
, to remove child widgets in gtkmm4.
However, this seems also to be the case for non-managed widgets, which... seems really broken?
I store gtkmm
widgets in unique_ptr
s, add them to containers, and expect that - as in gtkmm3
- if I .clear()
that container or otherwise delete
a C++ wrapper... that widget will go away completely, i.e. the C instance will get unreferenced, too, and hence the widget will be unparented and disappear.
-
Is that really the intended behaviour now?
- IOW: Must we really ensure we
unparent()
orparent.remove(child)
every widget before deleting it, elsegtkmm
'leaks' the C instance? - If so, is this a(nother) problem introduced by GTK4, perhaps in removing
Container
and genericremove()
API? I don't think it should be, sincegtk_widget_unparent()
still seems to make an effort to tell the parent 'you do not have this child anymore'. Can we
- IOW: Must we really ensure we
-
Or is it still intended that non-
manage()
d widget wrappers will take care of nuking the GTK instance when destroyed?- If so, should
gtkmm
perhaps callunparent()
automatically upon destroying a wrapper, if it's not managed?
- If so, should
Here is a reproducer, based on my own code where I found this issue when migrating from gtkmm
3 to 4. Click Delete Widgets
and observe in the console that all the C instances still have ref_count == 1
. I expect, instead, that they would be fully deleted, removed from the parent, and obliterated from existence even in C
#include <gtkmm/application.h>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
using namespace Gtk;
using namespace std;
static auto widgets = vector< unique_ptr<Widget> >{};
static auto gwidgets = vector< GtkWidget* >{};
static void
push_back(unique_ptr<Widget> widget)
{
gwidgets.push_back( widget->gobj() );
widgets.push_back( move(widget ) );
}
static void
on_activate(Gtk::Application& application)
{
auto& box = *make_managed<Box>(Orientation::VERTICAL);
for (auto i = 10; i--;) {
auto button = make_unique<Button>( "Button #" + to_string(i) );
box.append(*button);
push_back( move(button) );
auto label = make_unique<Label>( "Label #" + to_string(i) );
box.append(*label);
push_back( move(label) );
}
auto& button = *make_managed<Button>("Delete Widgets");
button.signal_clicked().connect( [&]
{
widgets.clear();
for (auto const gbutton: gwidgets)
cerr << G_OBJECT(gbutton)->ref_count << '\n';
} );
box.append(button);
auto window = make_shared<Window>();
window->set_child(box);
window->set_visible(true);
application.add_window(*window);
application.signal_shutdown().connect(
[ window = move(window) ]() mutable { window.reset(); } );
}
auto
main() -> int
{
auto const application = Application::create("org.djb.test");
application->signal_activate().connect( bind_front( on_activate,
ref(*application) ) );
return application->run();
}