How can I be alerted when Qt signal/slot connection fails?
Asked Answered
G

5

6

We lose a lot of time when using a connect from/to a non-existing signal/slot, because Qt only warns us at runtime somewhere in the console logging.

Apart from evolving to Qt5, which uses the type system to report these problems, and from changing code for all connect calls in the system, is there another way to have the Qt runtime e.g. throw, or simply crash, or alert me loudly, when a wrong connection is made?

Georgiana answered 25/4, 2014 at 7:16 Comment(4)
Are you still really using Qt 4? Do you have C++11 support? Because, you could just use the proper SIGNAL/SLOT syntax with Qt 5 which generates a runtime error. Also, why would you crash when you can see the error message on the console?Lubra
@LaszloPapp: I have a 'large codebase' full of connect calls, but no time to adapt them all. The debug messages in the console are hidden between all other output. I'm looking for something very visual and disruptive that does not need code changes - only compiler change/runtime settings.Georgiana
You mean large codebase, but with Qt 4, and it would require quite a bit of porting to Qt 5 elsewhere? Because if you can switch to Qt 5, you would need to change the connect lines either way, so why not change it to the correct in that case? If you are still using Qt 4, I hear you. :)Lubra
One benefit of: Qt Creator there's code completion that helps with that.Pitiful
O
3

You can use a wrapper on connect which halts the program when some connection fails:

inline void CHECKED_CONNECT( const QObject * sender, const char * signal,
             const QObject * receiver,  const char * method,
             Qt::ConnectionType type = Qt::AutoConnection )
{
  if(!QObject::connect(sender, signal, receiver, method, type))
   qt_assert_x(Q_FUNC_INFO, "CHECKED_CONNECT failed", __FILE__, __LINE__);
}
Occasion answered 25/4, 2014 at 7:20 Comment(1)
It seems that qt_assert_x is not documented public API, so I assume it is safer to use Q_ASSERT or Q_ASSERT_X, and also: Q_ASSERT_X already solved you the _ _ FILE _ _ and _ _ LINE _ _ and noop boilerplate. That would also allow to avoid the extra function: #define Q_ASSERT_X(cond, where, what) ((!(cond)) ? qt_assert_x(where, what,__FILE__,__LINE__) : qt_noop())Lubra
U
3

My compact variant is as follows:

// BoolVerifier.h
#include <cassert>    

class BoolVerifier
{
public:
    BoolVerifier() = default;
    inline BoolVerifier(bool b) { assert(b); (void)(b); }
    inline BoolVerifier& operator=(bool b) { assert(b); (void)(b); return *this; }
};

And usage:

BoolVerifier b;
b = connect(objectFrom, SIGNAL(mySignal1(int)), objectTo, SLOT(mySlot1(int)));
b = connect(objectFrom, SIGNAL(mySignal2(int)), objectTo, SLOT(mySlot2(int)));
...
Urbano answered 25/5, 2020 at 23:4 Comment(2)
The inline keyword in the constructors is redundant by the way. But maybe explicit would be nice, if perhaps a bit less ergonomic. Nice application of C++ principles!Sym
I love this technique; it is barely intrusive and just works, and it expands to other return error codes. However, it can only work if you change every occurrence of the connect statement.Georgiana
K
1

The simplest solution is:

bool ok = QObject::connect(sender, SIGNAL(mySignal()), receiver, SLOT(mySlot());
Q_ASSERT_X(ok, Q_FUNC_INFO, "connect mySignal to mySlot");

Do not fall to the temptation of "shortening it". The variant below is a bug and becomes a no-op in release mode:

Q_ASSERT_X(QObject::connect(sender, SIGNAL(mySignal()),
                            receiver, SLOT(mySlot()),
                            Q_FUNC_INFO, "connect mySignal to mySlot");

This form would be entirely removed in release mode when not having the corresponding debug macro defined.

If you wish to throw, then you could start here:

try {
    if (!QObject::connect(sender, SIGNAL(mySignal()), receiver, SLOT(mySlot()))
        throw ...;
} catch ( .. )
    qDebug() << "Could not connect ...";
    qApp->exit(1);
}

You really should consider the new signal/slot syntax with Qt 5 and C++11 support which generates a compile-time warning.

That would result in something like:

connect(sender, &Sender::mySignal, mySlot);

You could even use a lambda to keep it short and easier to comprehend due to locality of related code:

connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
    receiver->updateValue("senderValue", newValue);
} );
Ketene answered 25/4, 2014 at 7:29 Comment(1)
Important note: with the lambda variant, it's mandatory that the receiver stays alive as long as the sender is. Therefore it's wise to include it in the call: connect(sender, &Sender::valueChanged, _receiver_, [=]()...). We caused many problems forgetting this.Georgiana
M
1

QObject::connect returns QMetaObject::Connection which can be tested via its bool operator:

Returns true if the connection is valid.

Another option might be to "reroute" and parse the auto-generated debug messages for connection errors.

Malita answered 26/9, 2016 at 15:32 Comment(3)
The Qt is strong with you. However, in the second paragraph of my question, I asked for a way without changing code. A runtime parameter of sorts that causes qt to crash loudly.Georgiana
It concisely answers the title question which is easily found and which specifies Qt5 as a tag. Plus it clarifies the usage of the return value which is implicitly used in the other answers. And I provide links to current documentation. This will be helpful to others.Malita
Also, my other idea is a potential solution to your problem.Malita
D
1

Qt sends a QWarning message when a connect fails.

You can catch this message using a helper class that uses qInstallMessageHandler and crash your application for QWarnings in dev mode, or parse the warning and only crash for connect errors.
(see How to redirect qDebug, qWarning, qCritical etc output?)

Discography answered 27/6, 2017 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.