Prefer to use normalised signal/slot signatures
Some time ago I’ve talked with my Berlin office mates about the benefits of using normalised signal signatures in connect statements. That is, instead of
SIGNAL( rowsInserted( const QModelIndex &, int, const int ) )
write
SIGNAL(rowsInserted(QModelIndex,int,int))
That is, remove all dispensable whitespace, const-&
, and top-level const
. If you’re in doubt about how the normalised form of your signal or slot looks like, take a look at the string data in the moc-generated file. The signals and slots are listed with their normalised signatures there.
What was clear already then is that you’ll save a few bytes of string data if you use the shorter form. I was also expecting a zero-copy fast-path in QMetaObject::normalizedSignature()
for the case of an already-normalised argument. I didn’t find it back then, and I put the issue on my to-do list to implement in Qt.
But I’ve just stumbled over the code of QObject::connect()
. If you look there, you’ll see that not using the normalised form has a rather severe performance penalty:
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; smeta = sender->metaObject(); signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); }
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.
On the positive side, that means we already have the zero-copy fast-path for the case of normalised signatures, and I can strike one more thing from my to-do list 🙂
For all of us, this gives us the following
Guideline: Prefer normalised signal/slot signatures in connect statements.
Luckily, we don’t have to wade through our source code by hand, since the Trolls provide a tool in $QTDIR/util/normalize
that you can run on a project to fix the signal/slot signatures up. It doesn’t seem to be shipped in the releases, but you can download it directly from Gitorious: https://qt.gitorious.org/qt/qt/trees/v4.7.3/util/normalize
Note, however, that at the time of writing, it creates sporadic false positives when it hits Q_PRIVATE_SLOT()
macros.
Pingback: Effective Qt: Prefer to use normalised signal/slot signatures « -Wmarc
IMHO, it’s also more pleasant to read a huge amount of normalized connects than non-normalized ones.
Since you’re there, add a link to the https://qt.gitorious.org/qt/qt/trees/master/util/normalize/ tool shipped with Qt.
The
util/
directory doesn’t seem to be shipped in releases, and the tool fails on (some, I haven’t found out why not on all)Q_PRIVATE_SLOT
macros, but it’s of course a huge time-saver, and trivial to build even when downloaded from Gitorious.Great find! Added.
Sounds like something that should be made a krazy2 warning 😉
I guess that is like opening the box of pandora 😉
The effect of using the normalized signature is maybe big when QStateMachine is used: on each state entry all connections are created and when the state is left all are dicsonnected.
But I haven’t checked the code if the normalized signatures are cached somehow.
Thanks for the tip, I never even thought about this. Would it maybe be a good idea to add runtime warnings when non-normalized names are used?
Also, how does Qt creator handle this? Does it always autocomplete the normalized names?
Your girlfriend doesn’t like premature optimization, neither does your code maintainer. Only follow this advice if your profiler says your code is bogged down in the connect() statement. Sheesh.
I’m a little confused — does that mean that you can’t pass const reference parameters to a slot? If not, doesn’t it make debugging a lot more confusing when the connect signature is different from the function prototype of the slot?
Regarding the need to normalise and possible confusion from different signatures:
The signature is normalised because there are so many ways in C++ to write the same signature:
or
but connect() does essentially a simple string comparison. You could normalise to a form that preserves the
const-&
, but there’s no point because you can’t overloadanyway (they will always be ambiguous), so you might as well use the shorter form, and because it doesn’t matter if arguments are passed by value or by constant reference, because the code that moc has to generate for either would be identical. Since the normalised form is identical if the source signatures would be either equivalent or else always ambiguous, I don’t think there’s any confusion.
Regarding passing const reference parameters to slots:
The normalised signature is but a name for the slot, used in the meta object system, e.g. to look up a slot by name, and as such doesn’t influence the way a direct call to the slot is compiled by the compiler, not even the one that appears in the moc-generated code. Therefore, the slot signature should follow standard C++ rules as to when to pass by value and when to pass by
const-&
.David Johnson: Using “QString” instead of “const QString &” in the signature is not premature optimization, it doesn’t add any complexity, it even makes the connect statement more concise and readable. And if something doesn’t make a difference from a complexity or readability point of view, like, say i++ vs. ++i, I’d just go for the more performant solution.
Pingback: Qt: normalized signal-slot-connections | solutions not code