Casting Gtk::Widget to a custom derived type fails in gtkmm

I have a custom class called Page which is derived from Gtk::Paned. I add multiple instances of Page to Gtk::Stack using add(). Now when I retrieve a child by name and cast the pointer to Page* it fails. Casting it to Gtk::Paned works though.

Any idea what’s wrong? How can I get the pointer to my custom Page object after adding it to a stack?

Page p;
stack.add(p, id);
Page* ptr = dynamic_cast<Page*>(stack.get_child_by_name(id));    // fails
Gtk::Paned* ptr2 = dynamic_cast<Gtk::Paned*>(stack.get_child_by_name(id));    // works

Do you really call stack.get_child_by_name() before Page p has been deleted?

If Page p in stack.add(p,id) is deleted before stack.get_child_by_name() is called,
the result is that you get back the same C widget, which is a GtkPaned, with a newly
created C++ widget, which will be a Gtk::Paned.

Try this:

Page* p = Gtk::make_managed<Page>();
stack.add(*p, id);
Page* ptr = dynamic_cast<Page*>(stack.get_child_by_name(id));

This worked but I’m struggling to understand why my original code doesn’t. The Page object I was passing to the add() function was local and not being stored as a class member. But wouldn’t passing it to the add function extend it’s life?

A similar piece of code works when assigning a Gtk::Image as a child for the Gtk::Button. The Image craeted was local i.e. inside a function and not stored as a member variable. However the even after the function ends, the image sticks and the button works like normal. This led me to believe that when widgets are passed to other widgets as child, Gtk keeps track of it somehow. Perhaps I was wrong.

But wouldn’t passing it to the add function extend it’s life?

No, a local object allocated on the stack will be deleted when the function
ends. There is no way to prevent that. That’s how C and C++ work.

The C++ widgets are wrappers around C widgets. For instance, the constructor
of your Page widget (derived from Gtk::Paned) will create a GtkPaned object
on the heap, and store a pointer to it. It also stores a pointer to the C++
wrapper in the GtkPaned object. Gtk::Stack::add() calls gtk_stack_add_child(),
which only cares about the GtkPaned. It knows nothing about any C++ class.
When the Page object is deleted, it’s destructor (actually the destructor in one
of its base classes) notices that it has been added to a container widget, and it
does not delete the C object (GtkPaned). It just deletes the pointer to the C++ object.
The GtkPaned continues to work normally. GtkPaned still exists, but Page is gone.
Gtk::Stack::get_child_by_name() calls gtk_stack_get_child_by_name(),
which returns a pointer to the GtkPaned. That GtkPaned now does not have a C++
wrapper, so one is created. That will be a Gtk::Paned.

In the case with Gtk::Image and Gtk::Button, the Gtk::Image will be deleted
when the function ends, but the GtkImage (the corresponding C widget) will
remain. It’s allocated on the heap.

1 Like

Thanks, this explained everything