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?

Community
  • 1
  • 1
xtofl
  • 40,723
  • 12
  • 105
  • 192
  • 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? – László Papp Apr 25 '14 at 07:25
  • @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. – xtofl Apr 25 '14 at 08:55
  • 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. :) – László Papp Apr 25 '14 at 09:35
  • One benefit of: Qt Creator there's code completion that helps with that. – Kuba hasn't forgotten Monica Apr 28 '14 at 15:58

5 Answers5

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__);
}
Nejat
  • 31,784
  • 12
  • 106
  • 138
  • 2
    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())` – László Papp Apr 25 '14 at 07:53
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)));
...
antonio
  • 31
  • 3
  • 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! – Jan Hošek May 26 '20 at 06:32
  • 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. – xtofl May 26 '20 at 07:56
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);
} );
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • 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. – xtofl Sep 27 '16 at 05:29
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.

handle
  • 5,859
  • 3
  • 54
  • 82
  • 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. – xtofl Sep 27 '16 at 05:26
  • 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. – handle Sep 27 '16 at 09:24
  • Also, my other idea is a [potential solution](http://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output) to your problem. – handle Sep 27 '16 at 09:26
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?)

Leherenn
  • 520
  • 5
  • 16