Null pattern with QObject - qt

(C++/Qt) I have a smart pointer to a QObject. Let's say a QWeakPointer. For some external reason (something that might happen in another object or due to an event), it is possible that the pointed object gets destroyed. Since I have a smart pointer there will be no dangling reference, so there's no problem. But I always have to check if the pointer is null or not.
I'm thinking of using the null pattern in order to avoid checking this all the time but I'm not sure if this is possible or convenient with a QObject. The idea would be that the pointer points to the object and in case it gets destroyed, the smart pointer changes its pointed object to a null object. Is this a good idea or should I forget it and just check if the pointer is NULL all the time?
Let's show you an example. We have a worker who uses a tool to do its work:
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = 0);
void work()
{
if(m_tool)
m_tool->use();
emit workCompleted();
};
signals:
workCompleted();
public slots:
void setTool(QWeakPointer<Tool> tool);
private:
QWeakPointer<Tool> m_tool;
};
class Tool : public QObject
{
Q_OBJECT
public:
Tool();
public slots:
void use() =0;
};
class Screwdriver : public Tool
{
Q_OBJECT
public:
Screwdriver() : Tool();
public slots:
void use()
{
// do something
};
};
class Hammer : public Tool;
class Saw : public Tool;
...
In this case, the Tool is a public domain object of a library, which is used by the Worker. I'm developing such library. So the worker is using a screwdriver but it gets broken and gets destroyed. No problem:
if(m_tool)
m_tool->use();
emit workCompleted();
m_tool is 0 so it simply does nothing. But we have to check that it's not null everytime.
Now let's say we had a NullTool object:
class NullTool : public Tool
{
Q_OBJECT
public:
NullTool() : Tool();
public slots:
void use()
{
// does nothing
};
};
When the tool was destroyed, our pointer would be smart and would know it should point to a NullTool instance. So Worker::work() could be implemented like this:
void Worker::work()
{
m_tool->use();
emit workCompleted();
};
m_tool->use() would then get called on the NullTool which does nothing, so there would be no need to check the pointer is not null.
Is this a good idea? Is it possible with the smart pointer classes Qt provides or should I subclass QWeakPointer?

I think the null object pattern makes most sense for value-like classes. Examples are QString or QVariant, were you don't want to have code like if ( str && !str->isEmpty() ) but just do if ( !str.isEmpty() ). For QObjects, which are not values but have "an identity", I never found this useful.

I don't understand clearly your use case, but your program can be signaled when the object has been destroy by connecting the following signal from QObject:
void destroyed ( QObject * obj = 0 );

I don't see any problem in your idea. You just have to compare the work that it takes to implement it compared to the work for checking the pointer every time. Let's your checking the pointer 10.000 times it's a good idea to use your approach. Side note: Your null object pattern rely on the fact that Tool::use() has no side effects whatsoever.
Take care that possible side affects in Tool::use() don't get in the way when you replace it polymorphically with NullTool::use(). In other words: Be sure you don't break the Liskov Substitution Principle.

Related

QObject connect fails in Qt

everyone!
I am having a problem using QObject::connect with some custom classes I've created. First of all, I have created 2 classes that inherit from QObject, they are called: Valve and PushButton. They are instantiated inside controllers called PanelController and SynopticController, which are also QObjects. And these controllers are instantiated inside another class called MasterController, also a QObject. I find this information useful since I think it is a problem of referencing the classes or the way I might be instantiating my classes inside these controllers. I strongly think this, because in my main method, when I do the following snippet of code, the connection works:
...
avionics::synoptic::Valve valveTest(nullptr, avionics::synoptic::ValveName::ABV);
avionics::panel::PushButton pushButtonTest(nullptr, avionics::panel::PanelNames::RECIRC);
QObject::connect(&pushButtonTest, &avionics::panel::PushButton::onStateColorChanged, &valveTest, &avionics::synoptic::Valve::updateState);
...
Basically, the controller classes are:
// MasterController
class MasterController : public QObject {
...
private:
panel::PanelController* panelController{nullptr};
synoptic::SynopticController* synopticController{nullptr};
}
// Panel Controller
class PanelController : public QObject {
...
explicit PanelController(QObject *parent = nulptr){
this->pushButtons.append(new avionics::panel::PushButton(_panelController, avionics::panel::PanelNames::RECIRC));
}
private:
QList<avionics::panel::PushButton*> pushButtons{};
}
// SynopticController
class SynopticController : public QObject {
private:
QList<avionics::synoptic::Valve*> iceValves{};
explicit SynopticController(QObject *parent = nullptr) {
antiIcePneumaticLines.append(new avionics::synoptic::PneumaticLine(_synopticController, avionics::synoptic::PneumaticLineName::APU_2_ABV));
}
}
My problem is that when I do the same call for the QObject::connect either from my MasterController constructor or my main method, the signal doesn't call the slot function. I want to connect pushButtons to valves, and to do this I am using getters from my controllers. The call to QObject::connect that doesn't work is:
QObject::connect(panelController->getpushButtons().at(1), &avionics::panel::PushButton::onStateColorChanged, synopticController->getValves().at(1), &avionics::synoptic::Valve::updateState);
// Example of getter
QList<avionics::panel::PushButton*> PanelController::getPushButtons(){
return pushButtons;
}
I've put some prints inside the method that emits the signal and tried debugging it, but the signal is emitted and the slot isn't called. The classes return from the getters are not undefined or null, I've checked it. Let me know if something wasn't clear. Thanks in advance!

Signals emitted from a QThread worker class do not arrive

I have this simplified code:
class MyCustomObject {
};
class DeviceConnection : public QObject {
Q_OBJECT
public:
explicit DeviceConnection(QObject* const parent = nullptr);
signals:
void readFinished(MyCustomObject result);
public slots:
void readFromDevice();
};
DeviceConnection::readFromDevice() {
/* ... */
emit readFinished(MyCustomObject());
}
void MainWindow::on_actionRead_triggered() {
QThread* const thread = new QThread(this);
DeviceConnection* const connection = new DeviceConnection();
connection->moveToThread(thread);
thread->start();
connect(connection, &DeviceConnection::readFinished, this, [=](MyCustomObject data) {
/* This never runs. */
connection->deleteLater();
thread->quit();
});
QTimer::singleShot(0, connection, &DeviceConnection::readFromDevice);
}
This starts reading just fine. I can see in the debugger that I am getting to the emit line, and I am getting there in the thread. But I can also see in the debugger, and in the behavior of the code, that the readFinished lambda is never called. This is also true with slots that aren't lambdas. What's the problem?
Edit: This code runs fine when I don't use an extra thread, but of course it blocks the main thread while readFromDevice() runs.
I figured it out. Unfortunately I simplified the important bit away when I first asked the question, but I just edited it back in.
The problem is that MyCustomObject cannot be enqueued in the Qt message queue. To do that, you need to run this:
qRegisterMetaType<MyCustomObject>("MyCustomObject");
or
// ideally just after the definition for MyCustomObject
Q_DECLARE_METATYPE(MyCustomObject);
// any time before you want to enqueue one of these objects
qRegisterMetaType<MyCustomObject>();
your defined signal should take an argument of QString type.

Q_GADGET has value type semantic?

From the docs (https://doc.qt.io/qt-5/qtqml-cppintegration-data.html#value-types) and other posts I got the impression, that Q_GADGET can be used as a value type with copy semantics.
Let's take the Actor from the docs:
class Actor
{
Q_GADGET
Q_PROPERTY(QString name READ name WRITE setName)
public:
QString name() const { return m_name; }
void setName(const QString &name) { m_name = name; }
private:
QString m_name;
};
Q_DECLARE_METATYPE(Actor)
Now I expose an Actor with Q_PROPERTY on some C++ object like this:
Q_PROPERTY(Actor actor READ actor WRITE setActor NOTIFY actorChanged)
So far, everything is fine.
Now I have some javascript code in QML (context is the C++ object on which the Q_PROPERTY actor is exposed:
{
var actorCopy = context.actor
actorCopy.name = "Tom"
}
I always assumed that the Actor-Gadget will be copied, when assigning to the javascript variable actorCopy and I change the name on a copy.
Now I set a breakpoint in the setActor function of the defined Q_PROPERTY.
What happens is, when assigning the name of the actor in javascript, the setActor method will be called.
I would not expect that, since I work on a copy.
My question is, do I have a wrong understanding of Q_GADGET or is this a bug?
I mean, if I would have a O_OBJECT instead of Q_GADGET I would not expect the setActor function being called either, since I don't assign the property.
So Q_GADGET is neither a lightweight Q_OBJECT nor a value type.
Try something like this:
{
var actorCopy = context.actor
var otherCopy = actorCopy
actorCopy.name = "Tom"
otherCopy.name = "Gatis"
}
Then in your debugger you will see that setName() is called on two different instances. You can print "this" pointer to confirm.
I did not run this code, but I think that is how it should work.

How to use protected function setLocalPort?

I should use setlocalport for my socket connection but the property is protected and i have an error of compilation.
This is in qt application.
m_pSocket = new QTcpSocket();
m_pSocket->setLocalPort(m_iLocalPort);
error: ‘void QAbstractSocket::setLocalPort(quint16)’ is protected
If you want to use protected member like a public one, then you should provide a custom class that is the child of the class whose protected method you intend to use. There is nothing that would forbid you to create a child class inheriting QTcpSocket, and then using the protected method you want. Example for the QTcpSocket case that has been described here can be the following.
// Let us define CustomTcpSocket, i.e. the class inheriting QTcpSocket
#pragma once
#include <QTcpSocket>
class CustomTcpSocket
: public QTcpSocket
{
Q_OBJECT
public:
CustomTcpSocket(QObject* parent = nullptr);
virtual ~CustomTcpSocket();
// This method will be used to call QTcpSocket::setLocalPort which is protected.
void SetLocalPort(quint16 port);
};
Then, we provide the implementation itself.
#include "CustomTcpSocket.h"
CustomTcpSocket::CustomTcpSocket(QObject* parent)
: QTcpSocket(parent)
{
}
CustomTcpSocket::~CustomTcpSocket()
{
}
void CustomTcpSocket::SetLocalPort(quint16 port)
{
// Since method is protected, and scope is the child one, we can easily call this method here.
QAbstractSocket::setLocalPort(port);
}
Now we can easily use this newly created class in the following way.
auto customTcpSocketInstance = new CustomTcpSocket();
customTcpSocketInstance->SetLocalPort(123456);
Through usage of polymorphism, instances of CustomTcpSocket should be accepted by other Qt's APIs. However, there is no guarantee it will work as you would expect it to. Qt developers wanted this method to be protected for some of the reasons. So, use it with caution.

Qt, "no such signal" runtime error from child class, having Q_OBJECT macro

Using Qt 5.4, I'm having trouble with a signal emitted from a child class. This is the situation:
There is a
class Player : public QDialog{
Q_OBJECT
/*...*/
public slots:
void ReactToAdapter(Adapter::state_t newState);
private:
Adapter* adapter;
}
The
class Adapter : public QObject{
Q_OBJECT
public:
enum state_t {/*...*/}
signals:
void StateChagned(state_t newState);
}
is purely virtual. One of its Implementations is given as
class AdapterCAN : public Adapter{
O_OBJECT
/*...*/
}
AdapterCAN is emitting the StateChagnedwithin one of its methodes.
state_t is visible everywhere through includes. All three classes have the Q_OBJECT macro.
When I try to do
Player::ConnectToCANBus(/*...*/){
adapter = new AdapterCAN(/*...*/);
connect(adapter, SIGNAL(StateChanged(Adapter::state_t),
this, SLOT(ReactToAdapter(Adapter::state_t));
}
This creates the error message
QObject::connect: No such signal AdapterCAN::StateChanged(Adapter::state_t) in ..\player.cpp:90 during runtime.
It looks like the signal is not inherited!? Anyone having any ideas what I might be doing wrong? Thanks!
Changing the definition of the signal to
class Adapter : public QObject{
/*...*/
void StateChagned(state_t);
}
solved it. The qualified name has to be left out.
Thanks to #vahancho for pointing this out!
If anyone finds here, please check your connect function for errors. I once wrote such a stupid error, but the compiler did not give any error report.
connect(m_pInitEncFromDvd1, SIGNAL(signalFinish(int,QStirng)),
this, SLOT(slotDVD1InitFinish(int,QString)),
(Qt::ConnectionType)(Qt::ConnectionType::AutoConnection | Qt::ConnectionType::UniqueConnection));
Pay attention to
"SIGNAL(signalFinish(int,QStirng))"
, I accidentally write QString as QStirng, and it works perfectly after correction.

Resources