I am currently wondering how to reasonably use the QObject::destroyed(QObject*) signal.
An observation
I noticed that QWidget-derived objects are treated slightly different. Consider the following small self-contained and compiling example:
/* sscce.pro:
QT += core gui widgets
CONFIG += c++11
TARGET = sscce
TEMPLATE = app
SOURCES += main.cpp
*/
#include <QApplication>
#include <QPushButton>
#include <QTimer>
#include <QtDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::destroyed,
[=](QObject *o) { qDebug() << o; });
delete button;
QTimer *timer = new QTimer;
QObject::connect(timer, &QTimer::destroyed,
[=](QObject *o) { qDebug() << o; });
delete timer;
return app.exec();
}
This is its output:
QWidget(0x1e9e1e0)
QObject(0x1e5c530)
So presumably, the signal is emitted from QObject's d-tor, so only the QObject base remains when the slot is called for the QTimer. However, QWidget's d-tor seems to intercept as it still identifies itself as a QWidget from the slot.
And the problem
Let's assume we have a timer pool that organizes a couple of timers in a QList<QTimer *>:
struct Pool {
QTimer *getTimer() {
return timers.at(/* some clever logic here */);
}
QList<QTimer *> timers;
};
Now an incautious user might delete the timer that was borrowed to him/her. Well, we can react, and simply remove that timer from the list. A slot will do the trick:
Pool::Pool() {
/* for each timer created */
connect(theTimer, SIGNAL(destroyed(QObject*),
this, SLOT(timerDestroyed(QObject*));
}
void Pool::timerDeleted(QObject *object) {
QTimer *theTimer = /* hrm. */
timers.removeOne(theTimer);
}
But what now? Hrm. When the slot is called, the QTimer already is in destruction and partially destroyed - only its QObject base remains. So I objously cannot qobject_cast<QTimer *>(object).
To resolve this issue, I could think of the following tricks:
Store QObjects in the list. Then I'd have to downcast every time I use an item from the list. This could be done using static_cast, though, as I know there will only be QTimers in the list, so no need for dynamic_cast or qobject_cast.
Insteat of removeOne traverse the list using an iterator and then compare each QTimer item directly to the QObject. Then use QList::erase or such.
static_cast or even reinterpret_cast the QObject to a Qtimer nonetheless.
What should I do?
If you're looking for tricks, you could simply use the base QObject objectName and remove the destroyed timer based on that.
It seems clear that your problem is one of object ownership; in particular, how to convey who is responsible for destroying an object. If your Pool object owns the QTimer objects (and thus the user should not delete them), make it clear through the interface, for example returning a QTimer& instead of a QTimer* from your getTimer method. I'm not really well versed in Qt, but if you actually wanted to transmit ownership of the object returned from a method and thus make the user responsible of its deletion, you'd likely return a std::unique_ptr<QTimer>.
Just do a direct cast:
void Pool::timerDeleted(QObject *object) {
QTimer *theTimer = (QTimer*)object; //qobject_cast doesn't work here
//we are sure that only a timer can be a sender
timers.removeOne(theTimer);
}
You could base your list on QPointer instead of raw pointers. I.e. write
QList<QPointer<QTimer>> timers;
Now when one of the timers in the list goes away, the corresponding entry in the list will automagically be cleared. It will not be removed, though! But when you access the timer via your getTimer() method, an entry whose timer has been deleted will now return a nullptr (and not a dangling pointer).
And yes, QWidget emits destroyed() in its own destructor. This is why you see a real QWidget in that case. Everybody else uses QObject's implementation.
The other way around is safe. Cast QTimer * to QObject * instead:
void Pool::timerDeleted(QObject *object) {
const auto it = std::find_if(timers.begin(), timers.end(), [object](QTimer *timer) {
return static_cast<QObject *>(timer) == object;
});
Q_ASSERT(it != timers.end());
timers.erase(it);
}
Or use erase_if(QList &list, Predicate pred) introduced in Qt 6.1.
Related
I'm currently updating an existing codebase designed to be used with a GTK GUI to QT, so that it can implement multi threading, as the functions take hours to complete.
This codebase makes frequent calls to a function display(std::string), for the purpose of updating a text display widget. I redefined this function for the new QT version:
In Display.cpp:
void display(std::string output)
{
//
MainWindow * gui = MainWindow::getMainWinPtr(); //Gets instance of GUI
gui->DisplayInGUI(output); //Sends string to new QT display function
}
In MainWindow.cpp:
void MainWindow::DisplayInGUI(std::string output)
{
//converts output to qstring and displays in text edit widget
}
void MainWindow::mainFunction(){
//calls function in existing codebase, which itself is frequently calling display()
}
void MainWindow::on_mainFunctionButton_released()
{
QFuture<void> future = QtConcurrent::run(this,&MainWindow::mainFunction);
}
If I run the main function in a new thread, display(std::string) won't update the GUI until the thread completes. I understand why; the GUI can only be updated in the main thread. Everything else functions as intended.
What I want to implement, but I'm not sure how, is having display(std:string) send a signal back to the main thread to call MainWindow::DisplayInGUI(output_text) with the string that was passed to the display() function. I believe this is the correct way to do it, but correct me if I'm wrong. I want to avoid changing the existing codebase at all costs.
EDIT: I should add that for some dumb reasons entirely out of my control, I am forced to use C++98 (yeah, I know)
You must schedule the code that does UI calls to run in the main thread. I use a simple and easy to use wrapper for that:
#include <QApplication>
#include <QtGlobal>
#include <utility>
template<typename F>
void runInMainThread(F&& fun)
{
QObject tmp;
QObject::connect(&tmp, &QObject::destroyed, qApp, std::forward<F>(fun),
Qt::QueuedConnection);
}
You can now run code (using a lambda in this example, but any other callable will work) in the main thread like this:
runInMainThread([] { /* code */ });
In your case:
void display(std::string output)
{
runInMainThread([output = std::move(output)] {
MainWindow* gui = MainWindow::getMainWinPtr();
gui->DisplayInGUI(output);
});
}
Or you can leave display() as is and instead wrap the calls to it:
runInMainThread([str] { display(std::move(str)); );
The std::move is just an optimization to avoid another copy of the string since you should not pass the string by reference in this case (it would be a dangling reference once the string object goes out of scope.)
This is not a high performance inter-thread communication mechanism. Every call will result in the construction of a temporary QObject and a temporary signal/slot connection. For periodic UI updates, it's good enough and it allows you to run any code in the main thread without having to manually set up signal/slot connections for the various UI update operations. But for thousands of UI calls per second, it's probably not very efficient.
First of all: there's no way to make the getMainWinPtr method thread-safe, so this pseudo-singleton hack should probably go away. You can pass around some application-global context to all the objects that do application-global things like provide user feedback. Say, have a MyApplication : QObject (don't derive from QApplication, it's unnecessary). This can be passed around when new objects are created, and you'd then control the relative lifetime of the involved objects directly in the main() function:
void main(int argc, char **argv) {
QApplication app(argc, argv);
MainWindow win;
MyApplication foo;
win.setApplication(&foo);
// it is now guaranteed by the semantics of the language that
// the main window outlives `MyApplication`, and thus `MyApplication` is free to assume
// that the window exists and it's OK to call its methods
...
return app.exec();
}
Of course MyApplication must take care that the worker threads are stopped before its destructor returns.
To communicate asynchronous changes to QObject living in (non-overloaded) QThreads (including the main thread), leverage the built-in inter-thread communication inherent in Qt's design: the events, and the slot calls that traverse thread boundaries.
So, given the DisplayInGUI method, you need a thread-safe way of invoking it:
std::string newOutput = ...;
QMetaObject::invokeMethod(mainWindow, [mainWindow, newOutput]{
mainWindow->displayInGUI(newOutput);
});
This takes care of the thread-safety aspect. Now we have another problem: the main window can get hammered with those updates much faster than the screen refresh rate, so there's no point in the thread notifying the main window more often than some reasonable rate, it'll just waste resources.
This is best handled by making the DisplayInGUI method thread-safe, and leveraging the timing APIs in Qt:
class MainWindow : public QWidget {
Q_OBJECT
...
static constexpr m_updatePeriod = 1000/25; // in ms
QMutex m_displayMutex;
QBasicTimer m_displayRefreshTimer;
std::string m_newDisplayText;
bool m_pendingRefresh;
...
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == m_displayRefreshTimer.timerId()) {
QMutexLocker lock(&m_displayMutex);
std::string text = std::move(m_newDisplayText);
m_pendingRefresh = false;
lock.release();
widget->setText(QString::fromStdString(text));
}
QWidget::timerEvent(event);
}
void DisplayInGUI(const std::string &str) {
// Note pass-by-reference, not pass-by-value. Pass by value gives us no benefit here.
QMutexLocker lock(&m_displayMutex);
m_newDisplayText = str;
if (m_pendingRefresh) return;
m_pendingRefresh = true;
lock.release();
QMetaObject::invokeMethod(this, &MainWindow::DisplayInGui_impl);
}
private:
Q_SLOT void DisplayInGui_impl() {
if (!m_displayRefreshTimer.isActive())
m_displayRefreshTimer.start(this, m_updatePeriod);
}
};
In a more complex situation you'd likely want to factor out the cross-thread property setting to some "adjunct" class that would perform such operations without the boilerplate.
You could take advantage of the fact that QTimer::singleShot has an overload which, when called with a zero time interval, allows you to effectively schedule a task to be run on a specified thread during that thread's next idle slot...
void QTimer::singleShot(int msec, const QObject *context, Functor functor);
So your MainWindow::mainFunction could be something along the lines of...
void MainWindow::mainFunction ()
{
...
std::string output = get_ouput_from_somewhere();
QTimer::singleShot(0, QApplication::instance(),
[output]()
{
display(output);
});
...
}
the runnable project is here:
enter link description here
I sincerely glad to have your detail answers to solve this, but I am still confusing on this issue:
case 1: changing socket_session as a member variable of mainwindow
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
SocketThread* socket_session;
private:
...
But this is not the solution to access setFlag, even after I change the `Form1::on_qpushButton__set_white_level_0_clicked()' function like this:
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
socket_session->setThreadFlag(true);
}
Still it doesn't make sense because form1 instance doesn't have "the" instance of socket_thread which has been instantiated from mainwindow.
There's a solution I think is making another class that includes all instances that I want to use from inside of mainwindow but I don't think that is a good one because I am using thread and accessing a global big instance class that includes all of them to be "shared" is not a good idea for someone like me.
#include <form1.h>
#include <ui_form1.h>
#include "socketthread.h"
Form1::Form1(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form1) {
ui->setupUi(this);
}
Form1::~Form1() {
delete ui;
}
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
socket_session->setThreadFlag(true);
}
enter image description here
I know I am lack of understanding about this but, do I wanna make something nobody does...? I think everyone wants to separate all objects and their methods clearly and communicate via signals or calling functions from delivered object instances...
case 2: ... let me try how you suggested make possible first...
I can read C++ code and overall structure, but I don't know why I have to struggle with this, so please help me, dear Guru.
On socketthread.h :
class SocketThread : public QThread {
Q_OBJECT
public:
QTcpSocket *socket_session;
SocketThread();
~SocketThread(){}
bool connectToServer(QString, int);
void sendData(const char*, int, int);
void run(void);
private:
QString message;
volatile bool threadFlag;
signals:
void changedThreadFlag(void);
void changedMessageStr(void);
void setThreadFlag(bool);
void setMessageStr(QString);
private slots:
void setStr(QString);
void setFlag(bool);
void socketError(QAbstractSocket::SocketError);
};
And its implementation is...
SocketThread::SocketThread() {
socket_session = NULL;
threadFlag = false;
message = "NULL";
connect(this, SIGNAL(setThreadFlag(bool)), this, SLOT(setFlag(bool)));
}
...
void SocketThread::setStr(QString str) {
message = str;
}
void SocketThread::setFlag(bool flag) {
threadFlag = flag;
}
void SocketThread::run() {
while(true) {
if(threadFlag) {
QThread::msleep(100);
qDebug() << message;
} else
break;
}
qDebug() << "loop ended";
}
And I have one form which has a button, and I put a clicked() slot of it like this...
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
--how can I emit the signal of the one of socketthread from here??
}
Now, the mainwindow is like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
QString addr_server = "223.194.32.106";
int port = 11000;
SocketThread* socket_session = new SocketThread();
socket_session->connectToServer(addr_server, port);
ui->setupUi(this);
Form1* form1;
form1 = new Form1();
ui->stackedWidget_mainwindow->addWidget(form1);
ui->stackedWidget_mainwindow->setCurrentWidget(form1);
socket_session->run();
...
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
Once the socket_session->run() started, I need to change the threadFlag by clicking the button by emitting setThreadFlag() of one's from the running thread. And I just stuck in here.
Does it possible even?
Or am I doing this all wrong from the beginning?
As mentioned in this post:
"Emitting a signal" == "calling a function"
So all you really have to do is call the signal function, and all connected slots should be called.
This of course means that the Form1 object needs a pointer to the thread object, i.e. it needs a copy of socket_session. Then you can simply call the signal on the object
socket_session->setThreadFlag(your_flag);
Of course, if the Form1 have a copy of the socket_session pointer, it might as well call setFlag directly, if it was public.
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
No signal is needed – just call the function.
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
// --how can I emit the signal of the one of socketthread from here??
// E.g. this way:
socket_session->setThreadFlag(true);
}
To make this possible, another fix is needed:
socket_session is a local variable in OP's exposed code.
To make it "persistent", it has to become e.g. a member variable.
So, the constructor MainWindow::MainWindow() has to be changed:
// Nope: SocketThread* socket_session = new SocketThread();
// Instead:
socket_session = new SocketThread();
and SocketThread* socket_session; has to be added to member variables of class MainWindow.
To make it accessible in Form1, it has to be passed to Form1 as well.
This could be done e.g. by making it a member variable in Form1 also which is initialized with a constructor argument (or set from MainWindow afterwards).
(I must admit that I never have used the Qt UI builder QtDesigner but build all my UIs by C++ code exclusively.)
But, now, another fix is necessary:
volatile doesn't make a variable suitable for interthread communication.
(This was used in ancient times before multi-threading started to be supported by C++11.)
However, this is wrong: Is volatile useful with threads?
An appropriate fix would be to use std::atomic instead:
// Wrong for interthread-com.
//volatile bool threadFlag;
// Correct:
std::atomic<bool> threadFlag; // #include <atomic> needed
FYI: SO: Multithreading program stuck in optimized mode but runs normally in -O0
And, finally, in SocketThread::SocketThread():
connect(this, SIGNAL(setThreadFlag(bool)), this, SLOT(setFlag(bool)));
is not necessary in this case.
SocketThread::setThreadFlag() could call SocketThread::setFlag() directly, or even write threadFlag itself:
void setThreadFlag(bool flag) { threadFlag = flag; }
As I (recommended to) make threadFlag atomic, it can be accessed from any thread without causing a data race.
Update:
After OP has updated the question:
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
The button (created from UI Form1) can be connected in the MainWindow as well (without using any method of Form1):
QObject::connect(form1->button1, &QPushButton::clicked,
socket_session, &SocketThread::setThreadFlag,
Qt::QueuedConnection);
Notes:
About form1->button1, I'm not quite sure.
I noticed that widgets in UI generated forms can be accessed this way but I don't know the exact details (as I never used the Qt UI builder on my own).
I used the Qt5 style of QObject::connect().
This is what I would recommend in any case.
The Qt5 style is verified at compile time. –
Wrong connections are detected by the C++ type checking.
Additionally, any function with matching signature can be used – no explicit exposure of slots is anymore necessary.
Even conversion of non-matching signature or adding additional parameters becomes possible by using C++ lambdas which are supported as well.
Qt: Differences between String-Based and Functor-Based Connections
It is possible to connect signals and slots of distinct threads.
I used Qt::QueuedConnection to remark this as interthread communication.
(However, I roughly remember that Qt might be able to detect it itself.
See the doc. for Qt::AutoConnection which is the default.
Further reading: Qt: Signals & Slots
Btw. using the Qt signals for inter-thread communication would exclude the necissity to make SocketThread::threadFlag() atomic. It could become a simple plain bool threadFlag; instead. The slot SocketThread::setThreadFlag() is called in the Qt event loop of QThread, in this case.
I made a widget (QDataflowCanvas) based on QGraphicsView, where I connect the signal QGraphicsScene::selectionChanged() to the slot MainWindow::onSelectionChanged of my main window:
void MainWindow::onSelectionChanged()
{
// canvas is ptr to QDataflowCanvas, subclass of QGraphicsView
auto selNodes = canvas->selectedNodes();
auto selConns = canvas->selectedConnections();
...
}
The problem happens when I close my MainWindow and there is some item selected in the QGraphicsView.
I don't think I need to provide the complete code (although it can be found here), as I already isolated the cause of the crash.
This is what will happen (in order of causality):
destructor of MainWindow is called
destructor of QDataflowCanvas is called
destructor of QGraphicsView is called
destructor of QGraphicsScene is called, which triggers the removal of all items (with clear())
destructor of a QGraphicsItem is called
that will trigger a selectionChange event
the MainWindow::onSelectionChanged slot is called
method QDataflowCanvas::selectedNodes() is called, but the object is destroyed
crash!
which can be seen more in detail from the stack trace of the crash:
I found this workaround: if I disconnect the signal in MainWindow::~MainWindow, it will of course not crash:
MainWindow::~MainWindow()
{
QObject::disconnect(canvas->scene(), &QGraphicsScene::selectionChanged, this, &MainWindow::onSelectionChanged);
}
But this seems a rather atypical practice: I never found myself having to manually severe signal-slot connections because the program would otherwise crash.
There must be a more proper solution to this.
First of all, the name of your project is a mistake. The Q-prefixed namespace is taken. You should not be having any Q-prefixed classes in any project that uses Qt. You should rename the project to DataflowCanvas, for example.
There are three solutions:
Hold all children by value, order the children according to their dependencies. The QWidgetPrivate::deleteChildren called from QDataFlowCanvas will be a no-op, or at least it won't be touching objects you care about.
Use the old connect syntax when connecting to the MainWindow::onSelectionChanged slot. Note that when your slot was invoked, the mainwindow object was of the QWidget dynamic type, not MainWindow type. The connections made using the old connect syntax respect the dynamic type of the object, and a connection made to a slot of given class will guarantee that the object is of that class dynamically, i.e. at runtime.
Clear the selection in the destructor - then no futher selection changes will be handled.
The first solution makes everything explicit and is the one I'd use:
class DataFlowCanvas : public QGraphicsView {
...
private:
QDataflowModel *model_;
QDataflowTextCompletion *completion_;
QSet<QDataflowNode*> ownedNodes_;
QSet<QDataflowConnection*> ownedConnections_;
QMap<QDataflowModelNode*, QDataflowNode*> nodes_;
QMap<QDataflowModelConnection*, QDataflowConnection*> connections_;
bool showIOletsTooltips_;
bool showObjectHoverFeedback_;
bool showConnectionHoverFeedback_;
qreal gridSize_;
bool drawGrid_;
QGraphicsSecene scene_;
};
The scene is destructed before any other fields. Problem solved. You should hold everything else by value as well. E.g. completion_, etc. The pointer indirections are not useful.
The second solution highlights an unfortunate Qt bug. To wit -- in the code below, the old connect syntax will never invoke Derived2::aSlot2, because at the time the slot is invoked, the object isn't of the Derived2 type anymore:
#include <QtCore>
int ctr1, ctr2;
struct Derived1 : QObject {
Q_SLOT void aSlot1() { ctr1++; qDebug() << __FUNCTION__; }
Q_SIGNAL void aSignal();
~Derived1() { Q_EMIT aSignal(); }
Q_OBJECT
};
struct Derived2 : Derived1 {
Q_SLOT void aSlot2() { ctr2++; qDebug() << __FUNCTION__ << qobject_cast<Derived2*>(this); }
Q_OBJECT
};
int main() {
{
Derived2 d;
QObject::connect(&d, &Derived2::aSignal, &d, &Derived2::aSlot2);
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot2()));
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot1()));
}
Q_ASSERT(ctr1 == 1);
Q_ASSERT(ctr2 == 1);
}
#include "main.moc"
The output clearly demonstrates the problem:
aSlot2 QObject(0x0) <-- aSlot2 called but `this` is of `Derived1*` type!
aSlot1
I was taking it so simple :) What about just checking canvas pointer:
void MainWindow::onSelectionChanged()
{
if (!qobject_cast<QGraphicsScene*>(canvas))
return;
auto selNodes = canvas->selectedNodes();
auto selConns = canvas->selectedConnections();
...
}
I used qobject_cast to check if pointer canvas still exists. You can check in other (better) way. The code works.
So i am looking to make our own generic inherited checkbox class that will be able to take in some values in its constructor and pop out a widget that is fully connected to our model in the manner we need.
Currently we do something like this within our view
connect(checkboxWidget, &QCheckbox::Clicked, this, &VMyView::Signal);
Which emits the Signal from VMyView when the checkbox is clicked.
If i wanted to pass that signal as a parameter into my new inherited class to be hooked up in its own connect statement, how would I do so?
Research has shown me i can pass a const char* but i get compilation errors that the signal/slot do not match.
Example
CheckBox(View myView, const char* signal)
{
connect(this, &QCheckBox::Clicked, myView, signal);
}
Returns an error that Signal and slot arguments are not compatible. Ive also tried SIGNAL(signal) with the same result.
The solution ended up being fairly simple in the end
Instead of using this from within my View
connect(pCheckbox, &QCheckBox::clicked, this, &MyView::Signal);
I use
connect(this, &QCheckBox::clicked, View, signal);
Where signal and comes into my function via a function pointer
MyCheckBox::MyCheckBox(QWidget* parent, MyView* View, void(MyView::*signal)(bool))
The key takeaway is
void(MyView::*signal)(bool)
is equal too
&MyView::Signal
I think the major issue here is that signals are not static member functions. Thus they require a pointer to an instance of the class to be called correctly. So you cannot just pass in things like &VMyView::Signal, as there's no corresponding this pointer attached to the function. (This is why most of the QObject::connect() overloads require an instance to the sender/receiver objects.)
One way to solve this is to create a function object, which contains both the member function pointer and the pointer to the object on which to call it. This can be passed to the QObject::connect() function just fine.
Here's an example:
// objects.h
#include <QtCore>
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver( QObject *parent = nullptr)
: QObject(parent)
{
}
~Receiver() { }
signals:
void sig(void);
};
class Sender : public QObject
{
Q_OBJECT
public:
Sender(std::function<void(void)> &bound_signal, QObject *parent = nullptr)
: QObject(parent)
{
// automatically emit `Sender::sig` on a timer, for testing.
timer = new QTimer(this);
timer->setInterval(1000);
QObject::connect(timer, &QTimer::timeout, this, &Sender::sig);
QObject::connect(this, &Sender::sig, bound_signal);
timer->start();
}
~Sender() { }
signals:
void sig(void);
private:
QTimer *timer;
};
And then a main function:
// main.cc
#include <QtCore>
#include "objects.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Receiver receiver; // object to receive the signal
// Bind the receiver's signal to the instance of the class
std::function<void(void)> signal = std::bind(&Receiver::sig, &receiver);
// Create a Sender, which will connect its own signal to the
// given bound signal
Sender sender(signal);
QObject::connect(&receiver, &Receiver::sig,
[]() -> void { qDebug() << "received"; });
return app.exec();
}
So, in your case, the Receiver and its signal would be replaced by VMyView and the signals you want to chain, and Sender would be the custom checkbox class you've implemented. Then in the constructor of the checkbox class, connect whatever signals you want to the given bound signals. You can also pass in a list of bound signals, e.g., std::list<std::function<void(void)>> &bound_signals.
I have to say, though, I'm not sure what this buys you. You'll need to write the connection logic somewhere, and I don't see why it needs to be in the constructor of the checkbox class. Wherever the checkbox and the VMyView class are created and used, that seems like a better place to put the connection code. It's more obvious, less convoluted, and there's better separation of concerns. The checkbox class shouldn't have to know or care what signals/slots its connected to. The application logic (i.e., where the objects are used) should define how the objects interact with one another.
My situation is that I have a QWidget-derived class, MyWidget, that will create a QThread-derived class (WorkerThread) to do some uninterruptible, blocking work in its run() method. The results of this are a heap-allocated instance of a QObject-derived class (DataClass) which is then received and processed by MyWidget. MyWidget is a transitory widget, though, and may be deleted while WorkerThread is still running due to user action.
Here's some pseudo-code to illustrate this:
#include <QThread>
#include <QWidget>
class DataClass : public QObject {
Q_OBJECT
public:
// contains some complex data
};
class WorkerThread : public QThread {
Q_OBJECT
public:
virtual void run() {
DataClass *result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
emit workComplete(result);
}
signals:
void workComplete(DataClass *);
};
class MyWidget : public QWidget {
Q_OBJECT
public:
void doBlockingWork() {
WorkerThread *worker = new WorkerThread;
connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
worker->start();
}
public slots:
void processData(DataClass *result) {
// Do some stuff
delete result;
// Assuming MyWidget still exists when WorkerThread has finished, no memory has leaked
}
};
Normally the correct "Qt" way to return the results of a worker thread is to have it emit a signal with its arguments being the result of its work, as illustrated above. That's fine for data that can be copied, but since the result is a pointer to a heap-allocated object, I have to be careful to make sure that memory gets freed.
And normally that wouldn't be a problem, because since WorkerThread has finished, I can safely pass the pointer to DataClass to MyWidget, have it process DataClass, and then free it.
The problem is that, as I said earlier, MyWidget is transitory and may be destroyed before WorkerThread is finishing. In this scenario, how can I ensure that the instance of DataClass gets freed one way or the other?
In particular, I'm looking for solutions that have some elegance to them, meaning that it takes advantage of Qt's features and preferably makes it so that WorkerThread maintains its separation from MyWidget so that WorkerThread doesn't need to know anything about it or any other class that might create it. I'm also open to ideas that improve upon the pattern that I'm already using.
Use smart pointer (e.g., QSharedPointer) instead a normal pointer:
DataClass *result = new DataClass;
should be replaced with
QSharedPointer<DataClass> result = QSharedPointer<DataClass>(new DataClass);
Then, you could safely pass it somewhere and do not worry about deleting it. When it is out of the last scope where it can be used, the object will be automatically destroyed.
The worker should push the result to the main thread, to indicate that it's safe to use there (per QObject semantics). The result should be auto-deleted in the main thread after everyone interested has been notified of the completion of the work. It is a minimal change:
void run() override {
auto result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread()); // added
emit workComplete(result);
QObject::connect(this, &QThread::finished, result, &QObject::deleteLater); // added
}
You're guaranteed that deleteLater will be invoked after the last handler of workComplete has finished in the main thread.
A single object in the main thread might wish to retain the results longer. This can be indicated by setting the parent on the result object. The object shouldn't be deleted then:
...
QObject::connect(this, &QThread::finished, result, [result]{
if (!result->parent()) result->deleteLater();
});
If you intend that multiple objects in the main thread retain the results longer, you should be using a QSharedPointer in the workComplete's argument, and you must never set the parent of the results: a non-null parent and a QSharedPointer are mutually incompatible: the former indicates a unique ownership by a parent, the latter indicates a shared ownership.
It is necessary to move the DataClass object to the main thread to avoid a race on DataClass::thead() and to allow deleteLater to work:
Worker Thread: emit workComplete(result)
Main Thread: start using result, result.thread() is the worker instance.
Worker Thread: finishes
Main Thread: result.thread() is now nullptr while the main thread is using it.
This might not be a problem, but usually indicates poor design. As soon as you start using more QObject features of DataClass, it turns the latent bug into a real bug: e.g. deleteLater won't work, timers won't work, etc.
Furthermore, destructing a QObject in any thread other than its thread is not supported. Suppose that you had your original code. The following could happen and leads to undefined behavior:
Worker Thread: emit workComplete(result)
Main Thread: start using result, result.thread() is the worker instance.
Main Thread: delete result. QObject::~QObject is invoked in qApp->thread() but result->thread() is the different, still live instance of the worker thread.
If you wish to catch such issues, add:
DataClass::~DataClass() {
Q_ASSERT(thread() == nullptr || thread() == QThread::currentThread());
...
}
It's OK to destruct a threadless object, but such objects are not fully functional: you can't deleteLater them, their timers don't work, they don't receive events, etc.
The necessity of a parent check prior to deleteLater depends on whether you intend to prolong the existence of the result past the code connected to workComplete.
The "obvious" use of a shared pointer doesn't make it clear which thread can safely access the result iff the result isn't thread-safe. It also does nothing by itself to fix the fact that once the worker finishes, the QObject is half-functional as there's no event loop associated with it. I believe that your intent is that only one thread may own the result, so that its methods don't have to be thread-safe. Luckily, QObject's semantics already express this clearly: the object's thread() is the one authorized to act on the object.
Any recipients of workComplete in the main thread will get to process the results before they vanish. If any object in the main thread wants to take ownership of the result, it can - by setting the parent. Otherwise, as soon the workComplete handlers are done, if none have claimed ownership, the result will get deleted from the main event loop.
Change the QTimer::singleShot(1000, w.data(), [&]{ w.reset(); }) timer to 2500ms to have the widget outlive the worker thread and note the difference in behavior depending on whether it claimed ownership.
Complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/worker-shared-37956073
#include <QtCore>
struct DataClass : public QObject {
DataClass() { qDebug() << __FUNCTION__; }
~DataClass() { qDebug() << __FUNCTION__; }
};
void doSomeReallyLongUninterruptibleWork(DataClass*) { QThread::sleep(2); }
class WorkerThread : public QThread {
Q_OBJECT
public:
void run() override {
auto result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread());
emit workComplete(result);
QObject::connect(this, &QThread::finished, result, [result]{
if (!result->parent()) {
qDebug() << "DataClass is unclaimed and will deleteLater";
result->deleteLater();
}
});
}
Q_SIGNAL void workComplete(DataClass*);
};
class MyWidget : public QObject {
void processData(DataClass * result) {
// Do stuff with result
// Retain ownership (optional)
if (true) result->setParent(this);
}
public:
void doBlockingWork() {
auto worker = new WorkerThread;
connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
worker->start();
}
~MyWidget() { qDebug() << __FUNCTION__; }
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QScopedPointer<MyWidget> w{new MyWidget};
w->doBlockingWork();
QTimer::singleShot(1000, w.data(), [&]{ w.reset(); });
QTimer::singleShot(3000, qApp, &QCoreApplication::quit);
return app.exec();
}
#include "main.moc"
You could also forgo the use of an explicit thread, and use QtConcurrent::run instead. There's no clear advantage to that, I'm showing it here just to indicate that either approach is feasible.
#include <QtConcurrent>
struct DataClass : public QObject {
Q_SIGNAL void ready();
Q_OBJECT
};
// Let's not pollute the default pool with long-running stuff
Q_GLOBAL_STATIC(QThreadPool, longPool)
class MyWidget : public QObject {
void processData(DataClass * result) {
// Do stuff with result
// Retain ownership (optional)
if (true) result->setParent(this);
}
public:
void doBlockingWork() {
auto result = new DataClass;
connect(result, &DataClass::ready, this, [=]{ MyWidget::processData(result); });
result->moveToThread(nullptr);
QtConcurrent::run(longPool, [result]{
result->moveToThread(QThread::currentThread());
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread());
emit result->ready();
QTimer::singleShot(0, result, [result]{
if (!result->parent()) result->deleteLater();
});
});
}
};