What are some best practices for debugging Qt sign

2019-03-16 20:30发布

问题:

Debugging signals and slots can be hard, because the debugger does not jump to the slots of a signal when it is emitted. What are some best practices for debugging Qt signals and slots?

In particular

  1. How do I make sure connections are set up successfully?
  2. When should I use signals and slots, when should I avoid them?
  3. What are the most efficient debugging techniques from your experience?

回答1:

There was a blog post written a while back called 20 ways to debug Qt signals and slots
It addresses I think #1 and #3 of your questions.

For #2, I don't think there is really a hard and fast reason to use or not use signals/slots, as they are a pretty core concept of the GUI framework. Signals are a perfect way to decouple the knowledge of one component to another, allowing you to design reusable widgets that simply declare state changes or notifications. It is also a very good way to communicate GUI changes from non-GUI thread loops, by emitting a signal that the main thread can see.

There might be times where what you really want instead of a signal/slot is to use events, such as when a parent widget should become the event filter for a number of child widgets. The children still do not need to know about the parent, and the parent gets a more direct event as opposed to a signal connection.

On the same topic of events, there are times where what you really want is a bubbling up of an event from child -> parent -> grandparent -> etc. Signals would make less sense here because they are not meant as a way to determine whether the proposed event should result in an action (they could be used this way obviously). Events allow you to check the current state, decide whether this widget should do anything, or otherwise bubble them up the chain for someone else to inspect.

There is a really fantastic answer about The Difference Between Signals/Slots and Events. Here is a good snippet:

  • You "handle" events
  • You "get notified of" signal emissions

What I like about that quote is that it describes the different need case. If you need to handle an action in a widget, then you probable want an event. If you want to be notified of things happening, then you probably want a signal.



回答2:

How do I make sure connections are set up successfully?

You will see warnings in the console output from your application for each failed connection.

When should I use signals and slots, when should I avoid them?

In my opinion, it's fine to use them any time you want to maintain separation of concerns in your class design. Your class can emit a signal that may or may not be answered by another class (or classes) completely unknown to your class. This keeps your coupling down.

What are the most efficient debugging techniques from your experience?

I can't really add anything more than what is said on this blog post. 20 ways to debug Qt signals and slots



回答3:

Regarding #1, I'll just add another piece of information that I didn't see mentioned above or in the referenced blog post.

From the documentation on QObject::connect():

Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns true if the connection succeeds; otherwise returns false.

I prefer asserting the return value of my connection to make sure the connection succeeded, especially because not all Qt programs will have console output. This also leads to more easily maintainable code, as it will catch changes made at a later date to either the signal or the slot, and force the programmer who made the changes to update the connections as well.



回答4:

In addition to what have been said, here are additional tricks.

If you use QTest for your unit tests, then you can pass -vs argument to the executable and all signals will be shown in the console.

I looked at how QTest works, and it registers callbacks that is triggered when signal and slots are executed using QSignalDumper class. However, this API is not exported and might break any time. Here is how I was able to hook on all signals and slots on Linux with Qt 5.10 using GCC.

// QSignalSpyCallbackSet is defined in qt5/qtbase/src/corelib/kernel/qobject_p.h

struct QSignalSpyCallbackSet
{
    typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);
    typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);
    BeginCallback signal_begin_callback,
                    slot_begin_callback;
    EndCallback signal_end_callback,
                slot_end_callback;
};
typedef void (*register_spy_callbacks)(const QSignalSpyCallbackSet &callback_set);

static void showObject(QObject *caller, int signal_index, const QString &msg)
{
   const QMetaObject *metaObject = caller->metaObject();
   QMetaMethod member = metaObject->method(signal_index);
   qDebug() << msg << metaObject->className() << qPrintable(member.name());
}

static void onSignalBegin(QObject *caller, int signal_index, void **argv)
{
   showObject(caller, signal_index, "onSignalBegin");
}

static void onSlotBegin(QObject *caller, int signal_index, void **argv)
{
   showObject(caller, signal_index, "onSlotBegin");
}

int main(int argc, char *argv[])
{
   static QSignalSpyCallbackSet set = { onSignalBegin, onSlotBegin, 0, 0 };
   QLibrary qtcore("libQt5Core");
   register_spy_callbacks reg = (register_spy_callbacks)qtcore.resolve("_Z32qt_register_signal_spy_callbacksRK21QSignalSpyCallbackSet");

   if (reg) {
      reg(set);
   }
   ...
}

I believe that Qt should expose that API, because we could use it for so many things beyond debugging, such as monitoring the time spent in a slot, get statistics, etc.