I need to connect binaryMessageReceived signal of QWebSocket to my slot which modifies the QByteData
The QByteData may be large so it might be really costly to copy it again in mutable variable each time. I want to reuse the existing QByteData
when I try to compile with following slot
void route(QByteArray& msg);
I get compilation error
/usr/include/qt/QtCore/qobject.h:255:9: error: static assertion failed: Signal and slot arguments are not compatible.
255 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
| ^~~~~~~~~~~~~~~~~
/usr/include/qt/QtCore/qobject.h:255:9: note: ‘(((int)QtPrivate::CheckCompatibleArguments<QtPrivate::List<const QByteArray&>, QtPrivate::List<QByteArray&> >::value) != 0)’ evaluates to false
but if I change the slot to
void route(const QByteArray& msg);
it compiles just fine
I'm connecting slot like this:-
connect(this, &QWebSocket::binaryMessageReceived, this, &WSManager::route);
You probably don't want to do that.
The signal argument is not meant to be modified if passed as const &. You are not even sure of the lifetime of the binary data in the emitter object (QWebSocket).
The QByteData is emitted from here : https://code.woboq.org/qt5/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp.html#181
Nested in multiple classes hidden from the API, it's very dangerous to rely on this kind of attribute/data.
QByteArray uses the implicit-sharing Qt mecanism which avoid deep copy.
https://doc.qt.io/qt-5/implicit-sharing.html
That means you can pass the object around without concern about the performances. And if at some point you need to modify it, you might end up working on the only actual instance of the data thanks to the move operator.
I think you could connect to lambda and use const_cast inside the lambda function:
connect(this, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray &message) {
this->route(const_cast<QByteArray &>(message));
});
Update: even you can cast constness away this way it doesn't mean it's a good idea to do that :) As it was pointed out in the other answer no data copying is done for unchanged QByteArray because of Qt implicit sharing mechanism.
Related
I am working on some code where I see the following:
In header file:
private slot:
void OnNotifySomeSlot(const QList<bool>& someList); // Note the argument is pass by const ref
In implementation file:
connect(&m_serviceObj,SIGNAL(NotifySomeSlot(QList<bool>)), this, SLOT(OnNotifySomeSlot(QList<bool>)); // Note that arguments are pass by value
Slot function definition:
void OnNotifySomeSlot(const QList<bool>& someList)
{
m_list = someList;
} // Note that argument is passed by const ref as in the header
Now my doubt is that:
1: Why is such a code getting compiled as there is difference in signature between connect statement and slot definition?
2: During runtime if the value is getting passed a const ref , will it still not cause problems in a multithreaded environment. Because the reference to the list (be it const) can still be modified by some other code referring the same list in a multithreaded environment.
3: Can we just "pass by value" everywhere to avoid any issue in a multi threaded environment. Will it make the application slower?
Answering your questions in order:
The Qt macro machinery canonicalizes signal and slot names so they "fit". If you use the modern connection approach you do not have to worry about this:
QObject::connect(m_serviceObj, &SomeServiceObjectClass::NotifySomeSlot, this, &ThisObjectClassOnNotifySomeSlot)
Yes, even though you pass the list as a const ref, some other thread that has a non-const ref can change it behind your back.
Yes, passing it by value makes this behave properly. It is also efficient, as QList is an "implicitly shared" datastructure. This means that a copy is only made when a mutation happens, by the thread doing the mutation.
In qt framework, most library signals and slots use pointers as parameters. I was wondering, If I create a signal-slot "structure" that takes a reference as the parameter instead of the pointer, will the whole parameter be copied, or just 4 bytes (32-bit system) like in a regular c++ reference?
I am asking this because I noticed something when I create a signal/ slot methods with the reference parameter. When I then connect them, the autocomplete mechanism in QTCreator doesn't hint me with reference parameters like he would do with pointer parameters. He hints me with the regular parameter. For example:
I create a signal and slot:
...
signals:
void mySignal(int& parameter);
private slots:
void on_mySignal(int& parameter);
I then attempt to connected them and Qt doesnt add & for reference in parameter:
...
connect(this, SIGNAL(mySignal(int)), this, SLOT(on_mySignal(int)));
I have to manually change to:
connect(this, SIGNAL(mySignal(int&)), this, SLOT(on_mySignal(int&)));
Thus I am wondering, does reference even work with signal/slot? I would appreciate all help.
If you send and receive a reference, on the same thread, per default no copy will be made. If you do anything else, including sending/receiving a value or sending a reference to another thread, one, two or even three copies will be made.
What happens depends on the connection type and the assurances QT needs to know that references remain valid through the call. A direct connection on the same thread resolves to a simple function call, so very little can happen to the underlying data. A queued connection, however, offers no guarantees for when the call will actually happen, therefore QT will make copies to preserve data integrity. QT implicitly queues signals crossing thread boundaries.
If either side is pass-by-value then QT copies the data to not affect the underlying object's state.
For more information, have a look at this blog post.
I'v read the documentation for QObject::connect (for Qt 5.4), but I have a question about the overload
QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
What exactly is the context parameter? What is its purpose? Can it be used to build connections in local event loops in threads?
Can someone provide examples of how/when to use this overload (when the context is not this)?
The context object is used in two scenarios.
Automatic disconnection
Let's first do a step back and ask ourselves: when does Qt break a connection?
With the usual connect(sender, signal, receiver, slot) connect, there are three possibilities:
When someone explicitely calls disconnect;
When sender is deleted;
When receiver is deleted.
Especially in cases #2 and #3, it just makes sense for Qt to behave that way (actually, it must behave that way, otherwise you'd have resource leaks and/or crashes).
Now: when using the connect overload taking a functor, when does Qt break a connection?
Note that without the context parameter, there's only one QObject involved: the sender. Hence the answer is:
When someone explicitely calls disconnect;
When sender is deleted.
Of course, there's no receiver object here! So only the sender automatically controls the lifetime of a connection.
Now, the problem is that the functor may capture some extra state that can become invalid, in which case is desirable that the connection gets broken automatically. The typical case is with lambdas:
connect(sender, &Sender::signal,
[&object1, &object2](Param p)
{
use(object1, object2, p);
}
);
What happens if object1 or object2 get deleted? The connection will still be alive, therefore emitting the signal will still invoke the lambda, which in turn will access destroyed objects. And that's kind of bad...
For this reason, when it comes to functors, a connect overload taking a context object has been introduced. A connection established using that overload will be disconnected automatically also
when the context object is deleted.
You're probably right when you say that a good number of times you're going to see there the very same "main" object used in the functor, for instance
connect(button,
&QPushButton::clicked,
otherWidget,
[otherWidget]()
{
otherWidget->doThis(); otherWidget->doThat();
}
);
That's just a pattern in Qt -- when setting up connections for sub-objects, you typically connect them to slots on this object, hence this is probably the most common context. However, in general, you may also end up with something like
// manages the lifetime of the resources; they will never outlive this object
struct ResourceManager : QObject
{
Resource res1; // non-QObjects
OtherResource res2;
};
ResourceManager manager;
connect(sender, signal, manager, [&manager](){ use(manager.res1, ...); });
// or, directly capture the resources, not the handle
So, you're capturing part of the state of manager.
In the most general case, when no context object is available, if there's the chance that the objects captured by the lambda survive the connection, then you must capture them by weak pointers, and try to lock those pointers inside the lambda before trying to access them.
Running a functor in a specific thread/event loop
Very shortly: when specifying a context object, the functor will be run into the context's thread, just like normal connections employing a receiver object. Indeed, note that the connect overload that takes a context also takes a connection type (while the one without context doesn't take one -- connection is always direct).
Again, this is useful because QObject is not reentrant or thread safe, and you must use a QObject only in the thread it lives in. If your functor accesses an object living in another thread, it must be executed in that thread; specifying that object as the context solves the issue.
I read that QT applies signature normalization process on the signal/slot mechanism. MOC generator basically removes the const reference qualifiers from signals/slots and just pass them by value.
I have a class which generates a big data structure called BIG_DATA so frequently and some other classes need to catch this data structure every time it is emitted.
struct BIG_DATA
{
// very big data
};
class DataGenerator
{
// some methods which generate BIG_DATA
signals:
void data_updated(const BIG_DATA &);
};
What i do :
connect(&data_generator_object, SIGNAL(data_updated(const BIG_DATA &)), this, SLOT(catch_new_data(const BIG_DATA &)));
What QT does :
connect(&data_generator_object, SIGNAL(data_updated(BIG_DATA)), this, SLOT(catch_new_data(BIG_DATA)));
So, what is the benefit of removing const reference qualifiers here ? What am i going to do with the overhead of copying the whole BIG_DATA to many clients of data_updated signal ?
Seems like the best way is to use a pointer to the generated BIG_DATA object, if QT doesn't attempt to remove the pointer signature too.
Signature normalization is used only to identify signals and slots. That is, if you want to tell connect() which signal or slot to use, you need to pass normalized signature there. But your signal's and slot's signatures remain untouched. If you use direct connection (which is default for single-threaded program), your object will not be copied.
If you are using queued connections, your structure is copied anyway (see this).
Now, if you use use normalized signals, you can minimize performance hit when you are using connect (see this) :
Lookup is first attempted with the signature as-is, and only if that fails is QMetaObject::normalizedSignature() called.
That means, when using non-normalised signal/slot signatures, you not only pay for a strcpy(), but also for a doomed-to-fail first lookup attempt. Sure, connects are usually done during startup, and a profiler won’t show you, but using non-normalised signatures is hereby firmly put into the realm of premature pessimisation.
However, the performance hit is only when using connect, not when sending signals. And the connections are made usually only once. Therefore I wouldn't worry too much.
In order to avoid the structure copy, use references.
This is a thing that I never quite got with const-ref and I really hope that someone could explain it to me.
When calling a function inside of another function, I get that const-ref is the best way when passing stack objects that I don't plan to tamper with. For example:
void someInnerFunction(const QString& text) {
qDebug() << text;
}
void someFunction() {
QString test = "lala";
....
someInnerFunction(test);
}
So far so good, I guess. But what about signals? Isn't there any risk that comes with passing a reference? Even though it's const. It feels like I've been reading all the docs about const-ref but I still find a bit risky since I understand it as "sending a reference to an object and keeping it const". What if the object it's referring to goes out of scope?
For example:
void someFunction() {
connect(this, SIGNAL(someSignal(const QString&)), this, SLOT(someSlot(const QString&)));
QString test = "lala";
emit someSignal(test);
// doesnt test go out of scope here? and since im not using queued connection the QString object doesnt get copied.
}
void someSlot(const QString& test) {
qDebug() << test; // will this work?
}
What is really happening here? I frequently use const-ref on function calls where I just want to access the object but not change it. But what about signals? Most signals seems to have const-ref parm in the Qt doc, but how does it work?
According to this answer, Qt just replaces const references with copies.
EDIT: Apparently not always the case... I just did a basic test program with one thread, and the reference was passed correctly. Its const-ness remained intact, as well. Anyways, yes, you do need to be wary of the variable going out of scope, plus you can't send references across threads this way. If you do, only copies will be passed.
To answer the questions in the comments of your example, yes, it will work regardless of whether it's a direct or queued connection. If it's a direct connection, it will work because someSlot() will be executed before someFunction() finishes; if it's a queued connection, it will work because test will be copied instead of passed by reference.
Here is a good demonstration showing how Qt signals/slots manage copying: http://www.embeddeduse.com/2013/06/29/copied-or-not-copied-arguments-signals-slots/
In Qt, when emitting a signal that is connected to a slot or slots, it equates to a synchronous function call... unless you've configured your signals and slots to use queued connections, then it is an asynchronous call and you should be careful when passing stack data and should pass a copy as if passing data to another thread.