1+1=10

扬长避短 vs 取长补短

"Qt Macro: Q_DECLARE_INTERFACE"

Q_DECLARE_INTERFACE(InterfaceClassName, InterfaceId)

This macro associate the given InterfaceId to the interface class called InterfaceClassName.The macro is normally used right after the interface definition:

class MyInterface {};
Q_DECLARE_INTERFACE(MyInterface, "me.debao.qt.myinterface")

Q_DECLARE_INTERFACE is a macro that defines helper function that make qobject_cast(QObject *object) return a IFace pointer.

#  define Q_DECLARE_INTERFACE(IFace, IId) \
    template <> inline const char *qobject_interface_iid<IFace *>() \
    { return IId; } \
    template <> inline IFace *qobject_cast<IFace *>(QObject *object) \
    { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : 0)); } \
    template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
    { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : 0)); }

InterfaceId is used in the function QObject::qt_metacast(InterfaceId). But, how does this function work?

Q_INTERFACES(...)

This macro tells Qt which interfaces the class implements.

class PluginObject : public QObject, public MyInterface
{
    Q_OBJECT
    Q_INTERFACES(MyInterface)

public:
    ...
};

When moc find Q_INTERFACES, it will generate a function called qt_metacast()

void *PluginObject::qt_metacast(const char *iname)
{
    if (strcmp(iname, "PluginObject")==0) return this;
    if (strcmp(iname, "MyInterface")==0) return static_cast<MyInterface *>(this);
    if (strcmp(iname, "me.debao.qt.myinterface")==0) return static_cast<MyInterface *>(this);
    //...
}

Q_PLUGIN_METADATA ()

This macro is being used to declare meta data which will be part of plugin. Then these data can be obtained without load the plugin.

NOTE: The plugin IID has nothing todo with the INTERFACES IID.

class PluginObject : public QObject, public MyInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "me.dabao.qt.myplugin" FILE "abc.json")
    Q_INTERFACES(MyInterface)

public:
    ...
};

When moc encounter the macro, it will generate char array to store the meta data(IID string, FILE contents and other infomation such as QTVERSION) and a macro to export the plugin.

static const unsigned char qt_pluginMetaData[] = {
    'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',
    0x71, 0x62, 0x6a, 0x73, 0x01, 0x00, 0x00, 0x00,
    0xe0, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
    0xcc, 0x00, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00,
//...
};
QT_MOC_EXPORT_PLUGIN(EchoPlugin, EchoPlugin)

In macro QT_MOC_EXPORT_PLUGIN, two C functions get exported.

#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
        { \
            static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
            if (!_instance)      \
                _instance = new IMPLEMENTATION; \
            return _instance; \
        }
#  define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME)      \
            Q_EXTERN_C Q_DECL_EXPORT \
            const char *qt_plugin_query_metadata() \
            { return (const char *)qt_pluginMetaData; } \
            Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
            Q_PLUGIN_INSTANCE(PLUGINCLASS)

Magic in moc

All the magic of this macro can be found in the source code of moc:

void Generator::generatePluginMetaData()
{
    if (cdef->pluginData.iid.isEmpty())
        return;

    // Write plugin meta data #ifdefed QT_NO_DEBUG with debug=false,
    // true, respectively.

    QJsonObject data;
    const QString debugKey = QStringLiteral("debug");
    data.insert(QStringLiteral("IID"), QLatin1String(cdef->pluginData.iid.constData()));
    data.insert(QStringLiteral("className"), QLatin1String(cdef->classname.constData()));
    data.insert(QStringLiteral("version"), (int)QT_VERSION);
    data.insert(debugKey, QJsonValue(false));
    data.insert(QStringLiteral("MetaData"), cdef->pluginData.metaData.object());

    fputs("\nQT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;\n\n"
          "#ifdef QT_NO_DEBUG\n", out);
    writePluginMetaData(out, data);

    fputs("\n#else // QT_NO_DEBUG\n", out);

    data.remove(debugKey);
    data.insert(debugKey, QJsonValue(true));
    writePluginMetaData(out, data);

    fputs("#endif // QT_NO_DEBUG\n\n", out);

    // 'Use' all namespaces.
    int pos = cdef->qualified.indexOf("::");
    for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
        fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
    fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n",
            cdef->qualified.constData(), cdef->classname.constData());
}

All the data can be retrieved through QPluginLoader before we really load the library.

QJsonObject QPluginLoader::metaData() const

For example:

        QPluginLoader pluginLoader(fileName);
        QString iid = pluginLoader.metaData().value("IID").toString();
        if (iid == "my_plugin_iid") {
            pluginLoader.load();
            //...
        }

Reference

  • http://qt-project.org/wiki/QtPlugins

Comments