1. 程式人生 > >Qt5的外掛機制(6)--開發Qt外掛時幾個重要的巨集

Qt5的外掛機制(6)--開發Qt外掛時幾個重要的巨集


如何開發Qt外掛,可以在Qt Assistant 中搜索"Qt Plugins"或"How to Create Qt Plugins",看看那篇manual中的介紹。
其中涉及到了幾個巨集


Q_DECLARE_INTERFACE( ClassName, Identifier)
This macro associates the given Identifier (a string literal) to the interface class called ClassName. The Identifier must be unique.
This macro is normally used right after the class definition for ClassName, in a header file.

Q_INTERFACES(...)
This macro tells Qt which interfaces the class implements. This is used when implementing plugins.

Q_PLUGIN_METADATA(...)
This macro is being used to declare meta data that is part of a plugin that instantiates this object.
The macro needs to declare the IID of the interface implemented through the object, and reference a file containing the meta data for the plugin.
There should be exactly one occurrence of this macro in the source code for a Qt plugin.

其中,Q_PLUGIN_METADATA(...)巨集在前面講“Qt外掛的元資訊”的那篇文章中已經介紹過了,它基本是這些巨集裡最重要的一個,因為
MOC會根據這個巨集生成很多跟該外掛相關的東西,包括元資訊、獲取外掛例項的函式等。可用它可以將外掛匯出,其作用類似於老版本
Qt中的 Q_EXPORT_PLUGIN2 巨集

Q_DECLARE_INTERFACE 巨集是與qobject_cast相關的,它為介面類定義了qobject_interface_iid和qobject_cast這兩個模板
Qt的原始碼中給出了巨集Q_DECLARE_INTERFACE的定義
#  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)); } \    
                    // qt_metacast通過外掛的IID來對映介面類的指標,一個IID繫結一個介面類
    template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
    { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : 0)); }



Q_INTERFACES巨集也是與qobject_cast相關,沒有Q_DECLARE_INTERFACE和Q_INTERFACES這兩個巨集,就無法對從外掛中獲取的例項指標進行qobject_cast對映。
不過,Q_INTERFACES巨集並沒有在Qt的原始碼中定義,他是MOC的菜,MOC會利用這個巨集生成一些程式碼。要注意一點,如果一個頭檔案或原始檔中用到了Q_INTERFACES巨集,
那麼在呼叫這個巨集之前,必須存在一個 Q_DECLARE_INTERFACE巨集宣告相應的介面(或者包含一個用Q_DECLARE_INTERFACE巨集聲明瞭該介面的標頭檔案),MOC會檢查這一點,因為它在為Q_INTERFACES巨集生成程式碼時要用到Q_DECLARE_INTERFACE巨集的IID引數。

舉例,
標頭檔案 MyPluginInterface.h 中虛擬介面類的定義如下

#include <QtPlugin>
#define QtPluginDemo_iid "org.qt-project.Qt.PluginDemo"    // 定義介面的IID
class MyPluginInterface
{
public:
        virtual ~MyPluginInterface(){}
        virtual void showPluginName();
};
Q_DECLARE_INTERFACE ( MyPluginInterface, QtPluginDemo_iid ) ;


標頭檔案MyPlugin.h中類的定義如下

class MyPlugin : public QObject, public MyPluginInterface
{
    Q_OBJECT
    //  Q_PLUGIN_METADATA ( IID QtPluginDemo_iid FILE "MyPlugin.json")
    Q_PLUGIN_METADATA ( IID QtPluginDemo_iid)
    Q_INTERFACES(MyPluginInterface)

public:
        void showPluginName();
};

將標頭檔案MyPlugin.h用MOC處理之後,生成的程式碼中有如下部分
(只列出了MOC為Q_INTERFACES巨集生成的程式碼,MOC為Q_PLUGIN_METADATA巨集生成的程式碼在前面講“Qt外掛的元資訊”的那篇文章中介紹過了):

    ...
    ...

static const qt_meta_stringdata_MyPlugin_t qt_meta_stringdata_MyPlugin = {
    {
QT_MOC_LITERAL(0, 0, 8)
    },
    "MyPlugin"
};
    ...
    ...

void *MyPlugin::qt_metacast(const char *_clname)    // Q_DECLARE_INTERFACE巨集就是利用這個函式實現的qobject_cast型別對映
{
    if (!_clname) return 0;
    if (!strcmp(_clname, qt_meta_stringdata_MyPlugin.stringdata))    // 如果_clname與類的名稱MyPlugin匹配,返回有效指標
        return static_cast<void*>(const_cast< MyPlugin*>(this));
    if (!strcmp(_clname, "MyPluginInterface"))        // 如果_clname與介面類的名稱MyPluginInterface匹配,返回有效指標
        return static_cast< MyPluginInterface*>(const_cast< MyPlugin*>(this));
    if (!strcmp(_clname, "org.qt-project.Qt.PluginDemo"))    // 如果_clname與介面類的IID匹配,返回有效指標。
                                // 這裡就用到了呼叫Q_DECLARE_INTERFACE巨集時使用的IID引數
                                // 而且,Q_DECLARE_INTERFACE巨集的程式碼中也是利用IID對映實現的qobject_cast
        return static_cast< MyPluginInterface*>(const_cast< MyPlugin*>(this));
    return QObject::qt_metacast(_clname);
}
    ...
    ...