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.
Related
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.
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 am actually facing a huge problem: I have a singleton class. My program is a multi-threaded MFC, so the singleton's constructor can be called from different threads. I surrounded the singleton instance-getter function with a mutex to avoid deadlocks and multiple constructions. The call of the below meant function fails directly at the first time.
The function looks like (LOG_MSG macro logs the given string into my log file):
MyClass& MyClass::singleton ()
{
LOG_MSG("MyClass::singleton -> jump in");
static CMutex oInitMutex;
try
{
CSingleLock oSingleLock((CSyncObject *) &oInitMutex, TRUE);
LOG_MSG("!!! SINGLETON LOCK OKAY !!!");
static MyClass oMyClassInstance;
LOG_MSG("!!! SINGLETON CONSTRUCTION OKAY !!!");
return oMyClassInstance;
}
catch(...)
{
CString excMsg("Unexpected exception by creating MyClass singleton instance!");
LOG_MSG(excMsg);
throw excMsg;
}
}
I've figured out, that the construction of the singleton object will NOT fail (since we get the "!!! SINGLETON CONSTRUCTION OKAY !!!" message).
Log output says:
09.04.2013 ;07:14:51:832;"MyClass::singleton -> jump in"
09.04.2013 ;07:14:51:841;"!!! SINGLETON LOCK OKAY !!!"
... (constructor logs => NOTHING unexpected in it!!! everything runs fine, as they must!!!)
09.04.2013 ;07:14:52:125;"!!! SINGLETON CONSTRUCTION OKAY !!!"
09.04.2013 ;07:14:52:170;"Unexpected exception by creating MyClass singleton instance!"
What does it means? When would a return statement throw exception(s)??? Please help me resolve this matter...
While this doesn't answer your particular question, it's still a solution to your overall problem: you don't need the mutex at all. The C++11 standard [stmt.dcl]§4 specifies (when talking about static variables local to functions):
If control enters the declaration concurrently while the variable is
being initialized, the concurrent execution shall wait for completion
of the initialization.88 If control re-enters the declaration
recursively while the variable is being initialized, the behavior is
undefined.
Where note 88 is:
Note 88: The implementation must not introduce any deadlock around
execution of the initializer.
In other words, the compiler introduces synchronisation for you; no need to do that manually.
SOLUTION:
I figured out the problem, it was not easy. To learn from my error, I
will share the code, caused the failure.
In the constructor I use an sscanf function call in improper way:
const char* sBuffer;
// some stuff here that fills the sBuffer up
sscanf(sBuffer, "%X %X %X %X", &tags_[0], &tags_[1], &tags_[2], &tags_[3]);
The array was defined in the singleton class as:
private:
char tags_[4];
Beware: the format codes awating specific type of variables to be able
to write the data in. For example: in my case the tags_ array should
be an array of integers instead of array of chars. Since the singleton class
should store the tags_, after indexing the 2nd, 3rd and 4th element,
the sscanf function will write into an undefined place of memory,
something will be overwritten about the static class's data, which
caused multiple instancing of it, however: this can be avoided by
mutexing, but in that case the mutex object will be overwritten.
More infos about required types for (s)scanf can be found here and here.
CONCLUSION:
Be smart and aware of using C-functions/low-level calls. Pay a lot of
attention on input parameters, not only their value, but their type
too. If you support other type as expected, the behaviour of your program will be
undefined, but you won't get compiler error: it is very hard to find out
that issue later on.
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.
Is it possible, and if so, how can I create a signal/slot in Qt that is a const reference to a shared_ptr? I want a signal that looks like this:
void signal( shared_ptr<SomeClass> const & )
I know how to do this without a constant reference, that is simply the type shared_ptr<SomeClass> but for efficiency* reasons I'd like to avoid the copying. The same syntax for reference type isn't working:
Q_DECLARE_METATYPE(shared_ptr<SomeClass> const &)
qRegisterMetaType<shared_ptr<SomeClass> const&>();
Many of the standard APIs have QString const & so I assume it is fundamentally possible and I just can't figure out the syntax.
**The biggest problem for performance is not the copying time, but the amount of mutex locking/unlocking as the object is copied to every receiver -- there are lots of them. As multiple threads use the object this introduces a noticeable slow-down/bottleneck. If the shared_ptr does in fact just use an atomic op this cost is also trivial, the general question about const reference in signals remains however.*
So far I have found that I can simply do this:
Q_DECLARE_METATYPE(shared_ptr<SomeClass>)
qRegisterMetaType<shared_ptr<SomeClass> >();
qRegisterMetaType<shared_ptr<SomeClass> >("std::shared_ptr<SomeClass>const&");
I'm trying to verify now whether this truly works correctly. The documentation here isn't clear on what actually happens. It appears that the const reference type in a signal/slot will just be marshalled as a normal shared_ptr<SomeClass>, which is totally okay here. Having some sort of guarantee that this is supposed to work as such would be nice however.
I have the feeling that the simple shared_ptr<SomeClass> version is all that is needed and it is the boost namespace that is interfering with the signals. The second version appears just to register the signal in the global namespace for easier use.
I can confirm, from testing, that the const & part is, as alluded to, completely ignored in queued connections. Each connected slot gets a new copy of the object. This is very unfortunate. :(
Further tests show that the & is used for the slot, but in an unusual fashion. The copy of the object is still created for the queued connection, but if you don't use a reference another copy will be created for the call.
Thus there although every connect will end up copying the data for a queued connection, the references still help a bit. And also if you do have a few signals sent locally (same thread) you may avoid even more copying.
According to one of the answer in this question Argument type for Qt signal and slot, does const reference qualifiers matters? , for a queued connection, the object is copied regardless of how you connect the signal and slot.
Since you are using multiple threads, the connection is queued. If you fear the mutex cost, try using SomeClass * directly.
For the sake of completeness, I believe it should be mentioned that Qt has its own shared pointer implementation, QSharedPointer, which behaves exactly like std::shared_ptr. Eventually you will still need to register your argument type though.