Clone wiki

sigfwd / sig_checking

Signal/slot call signature compatibility checking

By default, sigfwd::connect and sigfwd::connectx will attempt to compare the signature of the Qt signal you specify to that of the function or functor given for the slot.

Most of the time the compatibility check works correctly. Failures are always conservative, meaning that sigfwd will never connect a signal to a functor when their signatures are incompatible.

The worst that can happen is that two signatures that are actually compatible are thought to be incompatible by sigfwd and the connection will fail.

This page will explore what it means for signatures to be compatible, the reasons for the occasional failure in compatibility checking and what we can do about those failures.

Definition of "compatibility"

So what does it mean for a signal and slot to be compatible? The answer is that QMetaObject::checkConnectArgs( ... ) returns true when applied to the normalized signatures of the signal and slot.

See the Qt documentation for more information. The compatibility checking is well defined and safe, but a big problem with it is that it take strings, rather than operating on types. In order to use it we therefore need to construct a string containing the call signature of the function or functor we pass to the slot.

Constructing a call signature

The call signature of the slot is either provided though a template argument to sigfwd::connectx or through the functor_traits mechanism when sigfwd::connect is used.

In either case, we will end up with a signature type such as void (double, int, const QString &). We need to turn this in to a string in order to use QMetaObject's checking facilities.

To do this, sigfwd tries a number of things for the type of each argument:

  1. looks for any meta-data provided at compile time by Q_DECLARE_METATYPE, else:
  2. looks for any meta-data provided at run-time by sigfwd::register_type_name (more on this in a moment), else:
  3. uses C++'s RTTI machinery (std::type_info::name()) to provide a name for the type

The last method, RTTI name lookup, works well on both the g++ and Microsoft Visual C++ platforms. The g++ implementation uses functionality from the <cxxabi.h> header in order to provide a demangled name for each argument's type. Visual C++'s std::type_info::name() returns a demangled name by default.

Signature comparison failures

Even with this multi-layered lookup, signature comparison may still occasionally fail. This is because a particular type may have multiple typedefs. So a connection attempt such as the following will not work:

sigfwd::connect(widget, SIGNAL(moreInput(int32_t)), bind(&add_to_count, ref(count), _1));

In this case, the bind expression may result in a functor with signature void (int), which matches that of the signal if int32_t is a typedef for int, but Qt's string-based matching mechanism will see "int" and "int32_t" as different types, even though they're the same in reality.

This particular case is really a problem with Qt; the following Qt-native connection code would also fail:

QObject::connect(widget, SIGNAL(moreInput(int32_t)), counter, SLOT(addToCount(int)));

To fix the problem, you can change the signal argument to say that moreInput takes an int. This workaround assumes that you can be sure int32_t is always an int on your code's target platform(s).

The other workaround is to set the check_sigs argument to false when calling sigfwd::connect or sigfwd::connectx.

Another example:

sigfwd::connect(addresslist, SIGNAL(void nameAdded(const std::string &)), add_address);

In this situation, the name look-up for the type std::string in the signature of add_address might result in a string such as "std::basic_string<char, std::char_traits<char>, std::allocator<char>", which is obviously different to "std::string", specified in the signal.

To fix this problem we can tell sigfwd that the name of std::string really is "std::string". Here's how:

#include <sigfwd/type_registry.hpp>

int main()
    // ...

The type registry header also contains a function sigfwd::register_meta_type which does the same as register_type_name but also calls Qt's qRegisterMetaType with the same arguments. Another function register_streamable_meta_type does the same but also calls Qt's qRegisterMetaTypeStreamOperators.

Of course the check_args = false fix also works in this case too, but is less robust to code changes.