2

I created a sample GTKMM project on GNOME Builder. The great thing was that a sample hello world code was automatically generated for my sample project. Since C++ source files are organized into three parts:

  • Header file
  • Implementation file
  • Main file

I've modified my sample code in a single cpp file for demonstration:

#include <iostream>

#include <gtkmm.h>

using std::cout;

using Gtk::Application;
using Gtk::Window;
using Gtk::Box;
using Gtk::Button;
using Gtk::Label;

class HelloWindow : public Window
{
    Box    box;
    Button button;
    Label  label;

public:
    HelloWindow();
    ~HelloWindow();
};

HelloWindow::HelloWindow()
    : Glib::ObjectBase("HelloWindow")
    , Window()
    , box(Gtk::ORIENTATION_VERTICAL)
    , button("Clickable button")
    , label("Hello World!")
{
    set_default_size(320, 240);

    bool expand(true), fill(true);
    box.pack_start(label, expand, fill);
    box.pack_end(button, expand, fill);

    add(box);

    show_all();
}

HelloWindow::~HelloWindow()
{
    cout << "Object successfully destructed!\n";
}

static void
on_activate(Glib::RefPtr<Application> app)
{
    Window *window = app->get_active_window();

    if (not window) {
        window = new HelloWindow();
        window->set_application(app);
        app->add_window(*window);
    }

    window->present();
}

int main()
{
    auto app = Application::create("io.test.window-state-event");

    app->signal_activate().connect(sigc::bind(&on_activate, app));

    return app->run();
}

One interesting part about the above code is that app is connected to on_activate signal which means the user gets to run only one instance of this program. And if he tries to run another instance the previous still running window will instead be presented.

However, there is the use of new keyword on on_activate() and that confuses me a bit. Is the object really deleted when the user closes the HelloWorld window? What I've learnt about C++ new keyword is that one must remember to delete any object allocated with the former keyword.

Moreover, the destructor message "Object successfully destructed!" isn't printed when the window is closed.

2 Answers2

1

Chances are there is an intentional leak, but it's "controlled". The author knows that this method will be called only once. The author also knows the memory needs to be active the entire lifetime of the application. When the application closes, that memory will be freed one way or another (albeit the destructor will never be called, but in this case, there is nothing imperative that would need to be done)

It's perfectly fine in this scenario.

If you want to ensure the Window object gets deleted, you could keep a unique_ptr of the Window and it will dispose itself (thanks to @underscore_d comment):

#include <memory>

static std::unique_ptr<Window> window;

static void
on_activate(Glib::RefPtr<Application> app)
{
    if (!window) {
        window = std::make_unique<HelloWindow>();
        window->set_application(app);
        app->add_window(*window);
    }

    window->present();
}

int main()
{
    auto app = Application::create("io.test.window-state-event");
    app->signal_activate().connect(sigc::bind(&on_activate, app));
    return app->run();
}

At the end of the day, I am sure the author wanted to keep this "Hello, World" example simple and concise and didn't want to add in some code that doesn't really need to be there in order to keep it simple and concise.

Andy
  • 12,859
  • 5
  • 41
  • 56
  • 2
    A `static std::unique_ptr` (be that at namespace scope or simply in the function) should be an easier way to satisfy the desire to track whether a window exists yet _and_ run the destructor, once per process. Personally I have done more complicated things in less clear-cut situations, like making an `std::shared_ptr`, capturing that in a lambda that is the signal handler for `Application::shutdown`, and deleting it when said lambda is executed... but in this case, that may be overengineering. – underscore_d Aug 27 '20 at 15:21
  • @underscore_d -- *but in this case, that may be overengineering* -- exactly my thoughts which is probably why the author just said "Screw it! Just let it leak". I added your idea of using a `unique_ptr`. Thanks for the info! – Andy Aug 27 '20 at 15:45
  • Identifiers beginning with an `_` are reserved in the global namespace, by the implementation. Besides, you could just rename that `window` and get rid of the local `window` in `on_active()`, which doesn't add anything useful. You can just check `if (the_unique_ptr)` since if it has been set, it's also been added, only once, and is active. – underscore_d Aug 27 '20 at 15:48
  • 1
    Thanks guys. Learnt something new about C++14 too :) – Keyikedalube Ndang Aug 28 '20 at 02:12
0

Take a look at the section about managed widgets in the GTKMM documentation. You should use some variation of Gtk::make_managed:

if (!window) {
    window = Gtk::make_managed<HelloWindow>();
    window->set_application(app);
    app->add_window(*window);
}

This way, the window's lifetime will be managed by the application.

ptomato
  • 56,175
  • 13
  • 112
  • 165
  • I tested it. Builds and runs fine. However, on running it I got this error statement *Attempt to call Gtk::manage() on a Gtk::Window, but a Gtk::Window has no parent container to manage its lifetime.*. Reading the managed widgets doc, I'm thinking Gtk::Window class cannot be instantiated with make_managed because it's the root container itself not a child of another container. – Keyikedalube Ndang Aug 31 '20 at 01:54
  • 1
    Well, that's disappointing. The application should really be able to manage the window. – ptomato Aug 31 '20 at 04:38