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.

12 Responses to Prefer to use normalised signal/slot signatures

  1. Pingback: Effective Qt: Prefer to use normalised signal/slot signatures « -Wmarc

  2. Sérgio Martins says:

    IMHO, it’s also more pleasant to read a huge amount of normalized connects than non-normalized ones.

  3. Giuseppe D'Angelo says:

    Since you’re there, add a link to the https://qt.gitorious.org/qt/qt/trees/master/util/normalize/ tool shipped with Qt.

    • marcmutz says:

      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.

  4. Michael says:

    Sounds like something that should be made a krazy2 warning ;-)

  5. Peter says:

    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.

  6. Wladimir says:

    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?

  7. 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.

  8. 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?

    • marcmutz says:

      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:

      void setText( const QString & );
      void setText(const QString&);
      void setText(const QString &);
      void setText(const
        QString
        &
        );
      void setText( QString const & );
      //...
      

      or

      void setValue( int );
      void setValue( const int );
      void setValue( int const );
      

      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 overload

      void setText( const QString & );
      void setText( QString );
      

      anyway (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-&.

  9. frank says:

    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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: