gtkmm widgets - use smartpointers or pointers? - c++

I am trying to learn how to use gtkmm having got a basic grasp of C++ (I like a challenge!). I have been working my way through the tutorials (as well as other reading). I am trying to use the approach of using glade to design the UI and then write the code to do the work.
So I have built a very simple UI (window and button at the moment!). I am using the GTK::Builder to load the UI from file. I am dividing the code into classes and a main caller.
Here is the main.cpp
#include "hellowindow.h"
#include <gtkmm/application.h>
int main(int argc, char *argv[]) {
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); //creates a Gtk::Application object, stored in a Glib::RefPtr smartpointer, create() method for this object initializes gtkmm.
HelloWindow hw; // Create a HelloWindow object
return app->run(hw, argc, argv); // shows the HelloWindow object and enter the gtkmm main processing loop, will then return with an appropriate success or error code
}
here is the header for the HelloWindow class
#ifndef HELLOWINDOW_H
#define HELLOWINDOW_H
#include <gtkmm/application.h>
#include <gtkmm/applicationwindow.h>
#include <gtkmm/button.h>
#include <gtkmm/box.h>
#include <gtkmm/builder.h>
#include <glibmm/fileutils.h>
/* derive the class from Gtk::ApplicationWindow base class */
class HelloWindow : public Gtk::ApplicationWindow {
public:
/* Conctructor */
HelloWindow();
/* Destructor */
~HelloWindow() override;
protected:
/* Signal handlers: */
void on_button_clicked();
/* Member widgets: */
Gtk::Box *cont; // Container
Gtk::Button *pButton; // Pointer to a Button
Glib::RefPtr<Gtk::Button> display_btn; // Smart pointer to a Button
Glib::RefPtr<Gtk::Builder> builder; // Builder
};
#endif // HELLOWINDOW_H
and here is the class code:
#include "hellowindow.h"
#include <iostream>
HelloWindow::HelloWindow() : builder(Gtk::Builder::create()){
try {
/* load window from glade file */
builder->add_from_file("glade/simple.glade");
}
catch(const Glib::FileError& ex) {
/* catch file errors */
std::cerr << "FileError: " << ex.what() << std::endl;
return;
}
/* ui builder created successfully from file */
/* add a container to the builder */
builder->get_widget<Gtk::Box>("cont", cont);
builder->get_widget<Gtk::Button>("display_button", pButton);
pButton->signal_clicked().connect(
sigc::mem_fun(*this, &HelloWindow::on_button_clicked)
);
/* add the container to the application window */
add(*cont);
/* set some parameters for the window */
set_title("Simple Gtk::Builder Demo"); // set the window title
set_default_size(500, 500); // set the window size
show_all(); // show the window and all of the enclosed widgets
}
HelloWindow::~HelloWindow(){
}
void HelloWindow::on_button_clicked(){
std::cout << "Hello World" << std::endl;
}
This all works fine and I think I understand what is happening. However, I have seen a different approach to adding widgets at runtime (https://sodocumentation.net/gtk3/topic/5579/using-glade-with-builder-api). The difference is how the button object is declared. In the code above it is declared as a pointer to a button object in the line:
builder->get_widget<Gtk::Button>("display_button", pButton);
However, the website above uses an approach of a smartpointer to the button object:
display_btn = Glib::RefPtr<Gtk::Button>::cast_dynamic(builder->get_object("display_button"));
The second way of doing it seems less clear, specifically the cast_dynamic aspect, would someone please explain the difference between the two approaches?
I hope that I have included enough information.
Thank you
Martyn

First of all, to understand the difference, and since you are new to
C++, I would recommend reading on the following topics:
Dynamic memory allocation (new/delete)
Memory leaks
Casting
Templates
First approach: get_widget
With this approach, you are getting a raw pointer (as opposed to a smart
pointer) to a widget defined from the ui file. Example:
Gtk::Grid* pGrid = nullptr;
refXml->get_widget("mygrid", pGrid);
After this, pGrid points to a Gtk::Grid widget. Notice that no
casting is required, you immediately get a Gtk::Grid. This is because
the Gtk::Builder::get_widget method is a template method:
// T_Widget is like a placeholder for some widget type,
// like Gtk::Grid for example.
template <class T_Widget >
void Gtk::Builder::get_widget(const Glib::ustring& name,
T_Widget*& widget
)
Usually in C++, such
raw pointers may be dangerous because if they point to an object
allocated on the heap (usually using new), one must remember to use
delete on them when done, otherwise memory leaks will occur. In this
case, pGrid is indeed a raw pointer to an object allocated on the
heap, but the documentation states:
Note that you are responsible for deleting top-level widgets (windows
and dialogs) instantiated by the Builder object. Other widgets are
instantiated as managed so they will be deleted automatically if you
add them to a container widget.
so most of the time (i.e. if not a toplevel widget), you do not have
to call delete, but sometimes you do. This is because Gtkmm has facilities
to automatically delete destroyed objects. See Gtk::manage for
more information on this.
When or not to use delete may become hard as code grows, especially since
you do not have to always do it (it can become easy to forget).
Second approach: get_object
With this approach, you are getting a smart pointer (a Glib::RefPtr) to
an object and casting to the right type is required, hence the
cast_dynamic
// get_object returns a Glib::RefPtr<Glib::Object>, which is not a Glib::RefPtr<Gtk::Button>
// so a cast is performed. This works because Gtk::Button is a child class of
// Glib::Object.
display_btn = Glib::RefPtr<Gtk::Button>::cast_dynamic(builder->get_object("display_button"));
The advantage of this is that once the cast is performed,
you do not have to manage the object's memory. It is managed by the
smart pointer (i.e. the smart pointer will automatically call delete
for you. This is true even for "top level" widgets.
Note: the cast used here, cast_dynamic, is a Gtkmm specific cast which wraps a dynamic_cast.
My opinion
I would personally go with the second approach because you get automatic
memory management (even for top level widgets), and hence no memory leaks.
However, the code gets harder to read as you already noticed.

Related

Is HelloWindow object deleted?

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.
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.
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.

When should I use pointer or reference members, and when should I pass nullptr or this as parent pointer in Qt?

The Code
I've been writing this c++ with Qt knowing it works but not really understanding why I sometimes do things other than "I just know I should be doing this".
This is my startup class which initialises my classes:
namespace GUI
{
Startup::Startup() :
QObject(nullptr),
m_setupTab(*new SetupTab(nullptr)),
m_regTab(*new CbcRegistersTab(nullptr)),
m_dataTab(*new DataTestTab(nullptr)),
m_mainView(*new MainView(nullptr,
m_setupTab,
m_regTab,
m_dataTab)),
m_systemController(*new SystemController(nullptr,
Provider::getSettingsAsSingleton())),
m_dataTest(*new DataTest(nullptr,
m_systemController)),
m_setupTabVm(new SetupTabViewManager(this,
m_setupTab,
m_systemController,
Provider::getSettingsAsSingleton() ))
{
}
Then in my header file the member variables are described as such:
private:
SetupTab& m_setupTab;
CbcRegistersTab& m_regTab;
DataTestTab& m_dataTab;
MainView& m_mainView;
Settings* m_settings;
SystemController& m_systemController;
DataTest& m_dataTest;
SetupTabViewManager* m_setupTabVm;
The main difference between the view manager and everything else is that view manager passes signals between the tab classes and everything else.
Then to start this in my main, all I do is this:
GUI::Startup startup;
startup.show();
SetupTabViewManager.cpp:
#include "setuptabviewmanager.h"
#include "View/setuptab.h"
#include "Model/systemcontroller.h"
#include "Model/settings.h"
#include <QDebug>
namespace GUI
{
SetupTabViewManager::SetupTabViewManager(QObject *parent,
SetupTab& tab,
SystemController& sysCtrl,
Settings& config) :
QObject(parent),
m_setupTab(tab),
m_systemController(sysCtrl)
{
WireMessages(config);
WireSetupTabButtons(config);
}
void SetupTabViewManager::WireMessages(Settings& config)
{
connect(&config, SIGNAL(notifyStatusMessage(QString)), //for QT4
&m_setupTab, SLOT(onStatusUpdate(QString)) );
connect(&m_systemController, SIGNAL(notifyStatusMessage(QString)),
&m_setupTab, SLOT(onStatusUpdate(QString)));
}
void SetupTabViewManager::WireSetupTabButtons(Settings& config)
{
connect(&m_setupTab, SIGNAL(onBtnLoadSettingsClicked(bool)),
&config, SLOT(onLoadButtonClicked(bool)) );
connect(&config, SIGNAL(setHwTree(QStandardItemModel*)),
&m_setupTab, SLOT(setHwTreeView(QStandardItemModel*)));
connect(&m_setupTab, SIGNAL(onBtnInitClicked()),
&m_systemController, SLOT(startInitialiseHw()));
connect(&m_setupTab, SIGNAL(onBtnCfgClicked()),
&m_systemController, SLOT(startConfigureHw()));
}
}
Questions
What is the advantage of initialising a class in a member variable as a pointer or a reference? I just know when I make a "view manager", I should initialise the member variable as a pointer. Though I'm not sure why?
Also what is the advantage of "this" or "nullptr" as the parent to the class?
Qt objects are organized in object trees. It allows a programmer not to care about destructing the created objects: they will be deleted automatically when their respective parents are deleted.
If you take a look at almost any Qt GUI application, then you will see that all of its widgets (buttons, checkboxes, panels, etc.) have one single ancestor - the main window. When you delete the main window, all your application widgets are automatically deleted.
You can set up this parent-child relation by passing a parent object to a newly created child:
QWidget *mainWidget = ...
QPushButton *btn = new QPushButton(mainWidget);
In this case, btn object becomes a child of the mainWidget object, and from this moment you may not delete that child manually.
If you create an object without a parent, you should always delete it by yourself.
As thuga mentioned in the comments, you can transfer object ownership later. Widgets can be places inside layouts and it will make them children of the layout owner. Or you can even explicitly set parent with QObject::setParent.
And as for declaring class members as pointers or references - it does not really matter. It's just a question of convenience.
In Qt world all child objects are getting deleted when their parent object is deleting. If you do not define class members (QObjects) as pointers, they will be deleted twice: once by their parent, and second time when your class object is destructed by itself, and this can lead to undefined behavior.

Addressing segfaults and other problems due to unexpected library behavior?

I've been running into problems with a C++ program that I've been working on recently. Specifically, I've been working on a program that uses Qt's GUI framework and I've been running into errors that seem to be related to double-deletion of pointers and exception handling. The issue is that I feel like the API that I'm using works in a way that isn't exactly predictable and because of that, I'm running into a lot of errors that seem counter-intuitive. I'm not the most experienced C++ programmer in the world, so maybe there is some overall strategy for working with new APIs that I'm missing..
Here's an example: I typically always try to delete objects that I dynamically allocate with inside the same class. In other words, if I populate a pointer using the new keyword within a class' constructor or init function, then I usually make sure to delete the contents of that pointer in the class' destructor.
Here's an simplified example of the class definition for the class that was giving me problems [MyProject.h]:
#ifndef MYPROJECT_H
#define MYPROJECT_H
#include "QObject.h"
class QGuiApplication;
class QQmlApplicationEngine;
#define MYPROJECT MyProject::getInstance()
class MyProject : public QObject
{
Q_OBJECT
private:
explicit MyProject(QObject *parent = 0); //singleton..
MyProject(MyProject const&); //uncopyable..
void operator=(MyProject const&); //unassignable..
QGuiApplication * QtGUI;
QQmlApplicationEngine * QmlAppEngine;
public:
~MyProject(void);
/* Globally available function to get MyProject's singleton instance.
* You can use the "MYPROJECT" preprocessor macro for shorthand. */
static MyProject & getInstance(void)
{
static MyProject instance;
return instance;
}
int init(int argc, char * argv[]);
int exec(void);
signals:
public slots:
};
#endif
This is what my simplified main.cpp looks like:
#include "MyProject.h"
int main(int argc, char * argv[])
{
MYPROJECT.init(argc, argv);
return MYPROJECT.exec();
}
Here's the ctor and init() that I initially had for that class [MyProject.cpp]:
MyProject::MyProject(QObject *parent) :
QObject(parent) ,
QtGUI(NULL) ,
QmlAppEngine(NULL)
{
}
MyProject::~MyProject(void)
{
//segfault: debug points to both of these..
if (QtGUI) delete QtGUI;
if (QmlAppEngine) delete QmlAppEngine;
}
int MyProject::init(int argc, char * argv[])
{
QtGUI = new QGuiApplication(argc, argv);
QmlAppEngine = new QQmlApplicationEngine();
if(QtGUI && QmlAppEngine)
{
//segfault: debug points to this..
QmlAppEngine->load(QUrl( QStringLiteral("qrc:///MyProject.qml") ));
}
else return 1;
}
int MyProject::exec(void)
{
return QtGUI->exec();
}
So, my plan was: ctor initializes pointers to NULL, init() populates those pointers with new objects, and if those pointers are not null the dtor cleans them up with delete. But this code crashes with 2 segfaults, but even though I think I've narrowed it down, I'm not sure I understand why they're both happening.
(1) Segfault #1 is a crash on startup that points to the "QmlAppEngine->load()" call inside the init(). I was able to prevent the crash from occurring by wrapping that function call in exception handling code like this:
int MyProject::init(int argc, char * argv[])
{
QtGUI = new QGuiApplication(argc, argv);
QmlAppEngine = new QQmlApplicationEngine();
if(QtGUI && QmlAppEngine)
{
//exception handling prevents crash..
try
{
QmlAppEngine->load(QUrl( QStringLiteral("qrc:///MyProject.qml") ));
}
catch(int e)
{
std::cout << "Exception: " << e << std::endl;
return 1;
}
return 0;
}
else return 1;
}
I'm not very familiar with exception handling, as most of the code I've written so far has used int return-code style error handling. I'm guessing that the load function can throw an exception in certain situations, and not handling them can cause a crash. The program stopped crashing on start-up once I made this change, but strangely, it didn't seem to actually throw an exception as my 'cout' never output anything.. Something else that I don't understand is that this code is called without any exception handling code in the default setup for brand new Qt Projects that Qt Creator makes - for example, this is what you see when you start a new QtQuick2 project in QtCreator IDE:
#include "QGuiApplication"
#include "QQmlApplicationEngine"
int main(int argc, char * argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine();
//default Qt file calls this without issue though..
engine.load(QUrl( QStringLiteral("qrc:///MyQml.qml") );
return app.exec();
}
The only major different that I can see here is that the default code uses objects instead of pointers to objects. But, in this case, load runs fine without exception handling code and there is no segfault..
(2) The next issue is caused when my dtor calls the delete keyword on those two pointers. If I comment out those two lines, the program runs fine and closes without crashes or issues. This leads me to believe that the API has made these objects delete themselves later, which is causing a segfault due to double-deletion when I also explicitly call delete. But, in general, how can one know if the API that they're using is taking care of object deletion internally? And, if I can't tell whether or not an API specified object is being deleted automatically, should I take any extra measures (i.e.: use some kind of smart pointer, etc.)? Typically I make the assumption that I should delete any dynamically allocated objects in the destructor of the same class, but clearly that can backfire in situations like this.
So what steps can I take to work with the APIs that I use in a way that (a) prevents bugs and (b) allows me to make sure that resources are being freed correctly and exceptions are being handled?
It's hard to find the exact location of error by seeing the sample code you provided, your application must have large code base and does many things with memory. Qt is a well designed and fully documented framework (though some documentation are misleading or outdated), I suggest you to read properly the documentation about a specific item if you have confusion. Here are some general issues I guess you should know/consider when using Qt:
When creating an object on the heap of class that inherits QObject, if you pass a parent ( another QObject) in the constructor, then the child object is owned by parent and memory will be freed automatically by the parent object.
QObject is NO_COPYABLE, so if you inherit from it, you don't need to make copy ctor/assignment operator private. The compiler generated versions of these methods calls parent version (here QObject), hence your class is automatically un-copyable/assignable.
new by default throws bad_alloc exception if it fails instead of returning NULL. So either you use try-catch or change default behavior by using no_throw version of new (new(std::nothrow)), it will return nullptr on failure.
deleteing a NULL pointer will not cause any problem. However, if the pointer points to arbitrary location / contain garbage value, deleteing it will result in segfault.
By default engine.load is not used with exception handler, so there is a high chance it does not raise exception. Look closely in other areas of your code.

gtkmm and Gtk::Builder: Lifetime of objects after calling get_widget

I think I must be "doing something wrong" here. I have a program using gtkmm. The main window for the program is a subclass of Gtk::Window and I have the bare bones of the application drawn out in Glade.
Here's an edited version of the main window's constructor:
template<class T>
static T*
get_widget (Glib::RefPtr<Gtk::Builder> builder, const Glib::ustring& name)
{
T* widget;
builder->get_widget (name, widget);
if (! widget) no_such_widget (name);
return widget;
}
app_window::app_window ()
{
Glib::RefPtr<Gtk::Builder> builder;
try {
builder = Gtk::Builder::create_from_file (installed_ui_name);
}
catch (const Glib::FileError& err) {
g_error ("Couldn't open UI definition file at %s",
installed_ui_name);
throw;
}
// main_box is a std::unique_ptr<Gtk::Widget>, a field of app_window.
//
// This works fine: the Gtk::Box gets added as the main child of this
// app_window and survives until the app_window is destroyed.
main_box.reset (get_widget <Gtk::Box> (builder, "main-box"));
add (* main_box);
auto accel_group = get_accel_group ();
// This doesn't work so well
//
// menu_quit is a Gtk::MenuItem*. The object seems to be actually
// constructed for the call to builder->get_widget, because its existence
// stops the menu item from being finalized at the end of the application.
auto menu_quit (get_widget<Gtk::MenuItem> (builder, "menu-quit"));
menu_quit->add_accelerator ("activate", accel_group,
GDK_KEY_q, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
using std::bind;
menu_quit->signal_activate ().connect (bind (& app_window::on_close, this));
show ();
}
When setting up the window, I want to register various signal handlers for menu items etc. (the menu_quit widget in the example is just one of them). To do so, I think I need to use builder->get_widget() to get hold of an object to talk about.
The problem is that I've now got an instance of (a subclass of) Gtk::Widget and I don't know what to do with it. If I call delete on the pointer, the widget doesn't appear in the application, but no other problems happen. That's a bit confusing: I would expect either no effect or a segmentation fault (depending on whether something else thought it owned the object or not).
If, on the other hand, I leak the pointer, assuming that object will be owned by the container into which main_box has been added, I get a memory leak. In particular, menu_quit's underlying gtk_image_menu_item doesn't get finalised because the reference count is one too high. (I can check easily enough that with GDB)
A "solution" is to store a pointer to each object that I get with builder->get_widget as a field in the app_window and then delete it in the destructor (automated with std::auto_ptr or std::unique_ptr). But that's really horrible: I don't want to have to write out a field for every single menu item! Avoiding that sort of nonsense was the whole point of using GtkBuilder in the first place!
Assuming that this isn't a bug (gtkmm 3.12.0, if that's relevant), I assume I'm just doing it wrong. How is an application supposed to do this?

GTKMM Error: Void Value Not Ignored As it Ought to Be

I am trying to create a simple window in GTKMM that contains a box. I've got the window part working, but I can't get my box code to work. I am following along with this tutorial
I think the tutorial is a little bit dated because Anjuta (the IDE I'm using) generated some different code. Here is my code that should add a box:
#include <gtkmm.h>
#include <iostream>
#include "config.h"
using namespace Gtk;
int main (int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
main_win->set_title ("Image-Viewer");
Gtk::Box *box = Gtk::manage (new Gtk::Box());
box ->set_orientation (ORIENTATION_VERTICAL);
box->set_spacing(6);
*main_win -> add(*box);
if (main_win)
{
kit.run(*main_win);
}
return 0;
}
In the code on the tutorial the window is not created in the same way. As you can see below, the window in my code is being created so that it is in the heap, rather than the stack. (or at least I think [I am new to C++]). I know that items in the heap should be used like a pointer, so for the add function I did that, (rather than using the dot notation described in the tutorial). When I run this code, I get an error stating the following:
error:void value not ignored as it out to be
The error is pertaining to the add method being called on the window. Can somone tell me what I'm doing incorrectly? Thanks
This instruction:
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
Declares a pointer to Gtk::Window. Later, you do:
*main_win -> add(*box);
This is incorrect, because you basically try to apply operator -> after you already dereferenced the main_win pointer - and the result of this dereferencing is not a pointer itself, but a reference to an object of type Gtk::Window.
To fix the problem, remove the extra dereferencing:
main_win -> add(*box);
NOTE:
I do not know Gtk::Window and its member function add(), but if it is the case that add() accepts a pointer as its argument, then you also shouldn't dereference box.