Qt MetaObject System詳解之二:meta資料和資料結構

來源:互聯網
上載者:User

如果一個類的聲明中包含Q_OBJECT宏,那麼qmake將為這個類產生meta資訊,這個資訊在前一篇中所提到的moc檔案中。這一篇通過解析這個一個樣本moc檔案來闡述這些meta資訊的儲存方式和格式;本篇先說明了一下QMetaObject的資料結構,然後呈現了一個簡單的類TestObject類及其產生的moc檔案,最後對這個moc檔案個內容進行了詳細解釋。

QMetaObject的資料定義:

QMetaObject包含唯一的資料成員如下(見標頭檔qobjectdefs.h)

struct QMetaObject
{
private:
struct { // private data
        const QMetaObject *superdata;  //父類QMetaObject執行個體的指標
        const char *stringdata;                //一段字串記憶體塊,包含MetaObject資訊之字串資訊
        const uint *data;                         //一段二級制記憶體塊,包含MetaObject資訊之二進位資訊
        const void *extradata;                //額外欄位,暫未使用
    } d;
}

 

QMetaObjectPrivate的資料定義:

QMetaObjectPrivate是QMetaObject的私人實作類別,其資料定義部分如下(見標頭檔qmetaobject_p.h)。該資料結構全是int類型,一些是直接的int型資訊,比如classInfoCount、methodCount等,還有一些是用於在QMetaObject的stringdata和data記憶體塊中定位資訊的索引值。下文結合這兩個記憶體塊的結構再分析個欄位的含義。

struct QMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData; //since revision 2
    int flags; //since revision 3
    int signalCount; //since revision
}

 

 

下文利用一個樣本QObject子類及其moc檔案,來分析QMetaObject的資訊結構。

樣本類TestObject:

TestObject類繼承自QObject,定義了兩個Property:propertyA,propertyB;兩個classinfo:Author,Version;一個枚舉:TestEnum。

#include <QObject><br />class TestObject : public QObject<br />{<br /> Q_OBJECT<br /> Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false)<br /> Q_PROPERTY(QString propertyB READ getPropertyB WRITE getPropertyB RESET resetPropertyB)<br /> Q_CLASSINFO("Author", "Long Huihu")<br /> Q_CLASSINFO("Version", "TestObjectV1.0")<br /> Q_ENUMS(TestEnum)<br />public:<br /> enum TestEnum {<br /> EnumValueA,<br /> EnumValueB<br /> };<br />public:<br /> TestObject();<br />signals:<br /> void clicked();<br /> void pressed();<br />public slots:<br /> void onEventA(const QString &);<br /> void onEventB(int );<br />}
 

 

樣本類TestObject的moc檔案:

#include "TestObject.h"<br />#if !defined(Q_MOC_OUTPUT_REVISION)<br />#error "The header file 'TestObject.h' doesn't include <QObject>."<br />#elif Q_MOC_OUTPUT_REVISION != 62<br />#error "This file was generated using the moc from 4.6.0. It"<br />#error "cannot be used with the include files from this version of Qt."<br />#error "(The moc has changed too much.)"<br />#endif<br />QT_BEGIN_MOC_NAMESPACE<br />static const uint qt_meta_data_TestObject[] = {<br /> // content:<br /> 4, // revision<br /> 0, // classname<br /> 2, 14, // classinfo<br /> 4, 18, // methods<br /> 2, 38, // properties<br /> 1, 44, // enums/sets<br /> 0, 0, // constructors<br /> 0, // flags<br /> 2, // signalCount<br /> // classinfo: key, value<br /> 22, 11,<br /> 44, 29,<br /> // signals: signature, parameters, type, tag, flags<br /> 53, 52, 52, 52, 0x05,<br /> 63, 52, 52, 52, 0x05,<br /> // slots: signature, parameters, type, tag, flags<br /> 73, 52, 52, 52, 0x0a,<br /> 91, 52, 52, 52, 0x0a,<br /> // properties: name, type, flags<br /> 113, 105, 0x0a095007,<br /> 123, 105, 0x0a095007,<br /> // enums: name, flags, count, data<br /> 133, 0x0, 2, 48,<br /> // enum data: key, value<br /> 142, uint(TestObject::EnumValueA),<br /> 153, uint(TestObject::EnumValueB),<br /> 0 // eod<br />};<br />static const char qt_meta_stringdata_TestObject[] = {<br /> "TestObject/0Long Huihu/0Author/0"<br /> "TestObjectV1.0/0Version/0/0clicked()/0"<br /> "pressed()/0onEventA(QString)/0onEventB(int)/0"<br /> "QString/0propertyA/0propertyB/0TestEnum/0"<br /> "EnumValueA/0EnumValueB/0"<br />};<br />const QMetaObject TestObject::staticMetaObject = {<br /> { &QObject::staticMetaObject, qt_meta_stringdata_TestObject,<br /> qt_meta_data_TestObject, 0 }<br />};<br />#ifdef Q_NO_DATA_RELOCATION<br />const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; }<br />#endif //Q_NO_DATA_RELOCATION<br />const QMetaObject *TestObject::metaObject() const<br />{<br /> return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;<br />}<br />void *TestObject::qt_metacast(const char *_clname)<br />{<br /> if (!_clname) return 0;<br /> if (!strcmp(_clname, qt_meta_stringdata_TestObject))<br /> return static_cast<void*>(const_cast< TestObject*>(this));<br /> return QObject::qt_metacast(_clname);<br />}<br />int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)<br />{<br /> _id = QObject::qt_metacall(_c, _id, _a);<br /> if (_id < 0)<br /> return _id;<br /> if (_c == QMetaObject::InvokeMetaMethod) {<br /> switch (_id) {<br /> case 0: clicked(); break;<br /> case 1: pressed(); break;<br /> case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;<br /> case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;<br /> default: ;<br /> }<br /> _id -= 4;<br /> }<br />#ifndef QT_NO_PROPERTIES<br /> else if (_c == QMetaObject::ReadProperty) {<br /> void *_v = _a[0];<br /> switch (_id) {<br /> case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;<br /> case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;<br /> }<br /> _id -= 2;<br /> } else if (_c == QMetaObject::WriteProperty) {<br /> void *_v = _a[0];<br /> switch (_id) {<br /> case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;<br /> case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;<br /> }<br /> _id -= 2;<br /> } else if (_c == QMetaObject::ResetProperty) {<br /> switch (_id) {<br /> case 0: resetPropertyA(); break;<br /> case 1: resetPropertyB(); break;<br /> }<br /> _id -= 2;<br /> } else if (_c == QMetaObject::QueryPropertyDesignable) {<br /> _id -= 2;<br /> } else if (_c == QMetaObject::QueryPropertyScriptable) {<br /> _id -= 2;<br /> } else if (_c == QMetaObject::QueryPropertyStored) {<br /> _id -= 2;<br /> } else if (_c == QMetaObject::QueryPropertyEditable) {<br /> _id -= 2;<br /> } else if (_c == QMetaObject::QueryPropertyUser) {<br /> _id -= 2;<br /> }<br />#endif // QT_NO_PROPERTIES<br /> return _id;<br />}<br />// SIGNAL 0<br />void TestObject::clicked()<br />{<br /> QMetaObject::activate(this, &staticMetaObject, 0, 0);<br />}<br />// SIGNAL 1<br />void TestObject::pressed()<br />{<br /> QMetaObject::activate(this, &staticMetaObject, 1, 0);<br />}<br />QT_END_MOC_NAMESPACE

 

qt_meta_data_TestObject::定義的正是QMetaObject::d.data指向的資訊塊;

qt_meta_stringdata_TestObject:定義的是QMetaObject::d.dataString指向的資訊塊;

const QMetaObject TestObject::staticMetaObject :定義TestObject類的MetaObject執行個體,從中可以看出QMetaObject各個欄位是如何被賦值的;

const QMetaObject *TestObject::metaObject() const:重寫了QObject::metaObject函數,返回上述的MetaObject執行個體指標。

TestObject::qt_metacall()是重寫QObject的方法,依據傳入的參數來調用signal&slot或訪問property,動態方法引動過程屬性訪問正是依賴於這個方法,在第四篇中會再講到該方法。

TestObject::clicked()和TestObject::pressed()正是對兩個signal的實現,可見,signal其實就是一種方法,只不過這種方法由qt meta system來實現,不用我們自己實現。

 

 

 TestObject類的所有meta資訊就儲存在qt_meta_data_TestObject和qt_meta_stringdata_TestObject這兩個待用資料中。QMetaObject的介面的實現正是基於這兩塊資料。下面就對這兩個資料進行分塊說明。

 

static const uint qt_meta_data_TestObject[] = { 

 

資料區塊一:
        // content:
       4,       // revision
       0,       // classname

       2,   14, // classinfo   

       4,   18, // methods

       2,   38, // properties
       1,   44, // enums/sets
       0,    0, // constructors
       0,       // flags
       2,       // signalCount

 

這塊資料可以被看做meta資訊的頭部,正好和QMetaObjectPrivate資料結構相對應,在QMetaObject的實現中,正是將這塊資料對應為QMetaObjectPrivate進行使用的。

第一行資料“4”:版本號碼;

第二行資料“0”:類型名,該值是qt_meta_stringdata_TestObject的索引,qt_meta_stringdata_TestObject[0]這個字串不正是類型名“TestObject”嗎。

第三行資料“2,14”,第一個表明有2個classinfo被定義,第二個是說具體的classinfo資訊在qt_meta_data_TestObject中的索引,qt_meta_data_TestObject[14]的位置兩個classinfo名值對的定義;

第四行資料“4,18”,指明method的資訊,模式同上;

第五行資料“2,38”,指明property的資訊,模式同上;
第六行資料“1,14”,指明enum的資訊,模式同上。 

 

資料區塊二:
 // classinfo: key, value
      22,   11,
      44,   29,

classinfo資訊塊。第一行“22,11”,22表明qt_meta_stringdata_TestObject[22]處定義的字串是classinfo的key,11表明qt_meta_stringdata_TestObject[11]處的字串就是value。第二行“44,29”定義第二個classinfo。

 

資料區塊三:
 // signals: signature, parameters, type, tag, flags
      53,   52,   52,   52, 0x05,
      63,   52,   52,   52, 0x05,

signal資訊塊。第一行“53,   52,   52,   52, 0x05”定義第一個signal clicked()。qt_meta_stringdata_TestObject[53]是signal名稱字串。parameters 52, type 52, tag 52, flags如何解釋暫未知。

 

資料區塊四:
 // slots: signature, parameters, type, tag, flags
      73,   52,   52,   52, 0x0a,
      91,   52,   52,   52, 0x0a,

slots資訊,模式類似signal。

 

資料區塊五:
 // properties: name, type, flags
     113,  105, 0x0a095007,
     123,  105, 0x0a095007,

property性資訊,模式類signal和slots,105如何和type對應暫未知。

 

資料區塊六:
 // enums: name, flags, count, data
     133, 0x0,    2,   48,
 // enum data: key, value
     142, uint(TestObject::EnumValueA),
     153, uint(TestObject::EnumValueB),

enum資訊,第一行定義的是枚舉名,flag,值的數目,data48不知是什麼。

幾行定義的是各枚舉項的名稱和值。名稱同上都是qt_meta_stringdata_TestObject的索引值。

       0        // eod
};

 

static const char qt_meta_stringdata_TestObject[] = {

這塊資料就是meta資訊所需的字串。是一個字串的序列。
    "TestObject/0Long Huihu/0Author/0"
    "TestObjectV1.0/0Version/0/0clicked()/0"
    "pressed()/0onEventA(QString)/0onEventB(int)/0"
    "QString/0propertyA/0propertyB/0TestEnum/0"
    "EnumValueA/0EnumValueB/0"
};

 

可以看出,meta資訊在moc檔案中以待用資料的形式被定義,其排列有點類似可執行檔中待用資料資訊的排布。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.