Lifting some of the mystery around QT_MOC_COMPAT

((Dumping here the info collected as reminder to self, but also everyone who might wonder and search the internet. If you know a proper place to put it, please copy it there.))

When working on adding macros to control warnings by & visibility to the compiler for deprecated API in the KDE Frameworks modules, a certain C++ preprocessor macro has been found in some places in the code: QT_MOC_COMPAT. This macro is found as annotation to signals or slots which are otherwise tagged as deprecated.

Yet, searching in the Qt documentation (both website & local docs) has not yield any hits. More, grepping the headers of Qt libraries itself does not yield any hits, besides a blank definition of the macro in qglobal.h:

/* moc compats (signals/slots) */
#ifndef QT_MOC_COMPAT
#  define QT_MOC_COMPAT
#else
#  undef QT_MOC_COMPAT
#  define QT_MOC_COMPAT
#endif

So, what has it been there for?

Looking at the code generated by moc, one can see that QT_MOC_COMPAT gets reflected in some flags being set on the metadata about the signals and slots methods. See by the example of KActionCollection (as found in the build directory src/KF5XmlGui_autogen/include/moc_kactioncollection.cpp, note the MethodCompatibility comments):

// ...
static const uint qt_meta_data_KActionCollection[] = {

 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
      12,   14, // methods
       2,  108, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       5,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   74,    2, 0x06 /* Public */,
       5,    1,   77,    2, 0x16 /* Public | MethodCompatibility */,
       6,    1,   80,    2, 0x16 /* Public | MethodCompatibility */,
       7,    1,   83,    2, 0x06 /* Public */,
       8,    1,   86,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       9,    0,   89,    2, 0x09 /* Protected */,
      10,    0,   90,    2, 0x19 /* Protected | MethodCompatibility */,
// ...

Those flags reflect enums defined in qmetaobject_p.h:

enum MethodFlags  {
    // ...
    MethodCompatibility = 0x10,
    MethodCloned = 0x20,
    MethodScriptable = 0x40,
    MethodRevisioned = 0x80
};

So, what makes use of this flag?

At first it seems nothing does. Looking some more, one can though discover that the flag is being brought back into the game via some bitshifting and being mapped onto another of set of flags (qmetaobject.h & qmetaobject.cpp):

class Q_CORE_EXPORT QMetaMethod
{
public:
    // ...
    enum Attributes { Compatibility = 0x1, Cloned = 0x2, Scriptable = 0x4 };
    int attributes() const;
    // ...
};

int QMetaMethod::attributes() const
{
    if (!mobj)
        return false;
    return ((mobj->d.data[handle + 4])>>4);
}

The very flag QMetaMethod::Compatibility is then checked for in debug builds of Qt during QObject::connect(...) calls, which invokes in such builds the following code to generate runtime warnings in the log:

#ifndef QT_NO_DEBUG
static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal,
                                         const QMetaObject *receiver, const QMetaMethod &method)
{
    if (signal.attributes() & QMetaMethod::Compatibility) {
        if (!(method.attributes() & QMetaMethod::Compatibility))
            qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)",
                     sender->className(), signal.methodSignature().constData());
    } else if ((method.attributes() & QMetaMethod::Compatibility) &&
               method.methodType() == QMetaMethod::Signal) {
        qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
                 sender->className(), signal.methodSignature().constData(),
                 receiver->className(), method.methodSignature().constData());
    }
}
#endif

Chance is that QT_MOC_COMPAT is a left-over from string-based signal/slot connection times. Where now using the method-function-pointer-based signal/slot connects catches connections with deprecated signals or slots at build time, as opposed to the runtime-only approach of the earlier, which also comes at runtime costs and thus is only available in debug builds of Qt.

Perhaps some Qt contributor reading this can shed more light on this and about its future, presence & past 🙂