1. 程式人生 > >Qt MetaObject System詳解之二:meta資料和資料結構

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

如果一個類的宣告中包含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。

  1. #include 
  2. class TestObject : public QObject  
  3. {  
  4.     Q_OBJECT  
  5.     Q_PROPERTY(QString propertyA  READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true
     SCRIPTABLE true STORED true USER false)  
  6.     Q_PROPERTY(QString propertyB  READ getPropertyB WRITE getPropertyB RESET resetPropertyB)  
  7.     Q_CLASSINFO("Author""Long Huihu")  
  8.     Q_CLASSINFO("Version""TestObjectV1.0")  
  9.     Q_ENUMS(TestEnum)  
  10. public:  
  11.     enum TestEnum {  
  12.         EnumValueA,  
  13.         EnumValueB  
  14.     };  
  15. public:  
  16.     TestObject();  
  17. signals:  
  18.     void clicked();  
  19.     void pressed();  
  20. public slots:  
  21.     void onEventA(const QString &);  
  22.     void onEventB(int );  
  23. }  

示例類TestObject的moc檔案:

  1. #include "TestObject.h"
  2. #if !defined(Q_MOC_OUTPUT_REVISION)
  3. #error "The header file 'TestObject.h' doesn't include ."
  4. #elif Q_MOC_OUTPUT_REVISION != 62
  5. #error "This file was generated using the moc from 4.6.0. It"
  6. #error "cannot be used with the include files from this version of Qt."
  7. #error "(The moc has changed too much.)"
  8. #endif
  9. QT_BEGIN_MOC_NAMESPACE  
  10. staticconst uint qt_meta_data_TestObject[] = {  
  11.  // content:
  12.        4,       // revision
  13.        0,       // classname
  14.        2,   14, // classinfo
  15.        4,   18, // methods
  16.        2,   38, // properties
  17.        1,   44, // enums/sets
  18.        0,    0, // constructors
  19.        0,       // flags
  20.        2,       // signalCount
  21.  // classinfo: key, value
  22.       22,   11,  
  23.       44,   29,  
  24.  // signals: signature, parameters, type, tag, flags
  25.       53,   52,   52,   52, 0x05,  
  26.       63,   52,   52,   52, 0x05,  
  27.  // slots: signature, parameters, type, tag, flags
  28.       73,   52,   52,   52, 0x0a,  
  29.       91,   52,   52,   52, 0x0a,  
  30.  // properties: name, type, flags
  31.      113,  105, 0x0a095007,  
  32.      123,  105, 0x0a095007,  
  33.  // enums: name, flags, count, data
  34.      133, 0x0,    2,   48,  
  35.  // enum data: key, value
  36.      142, uint(TestObject::EnumValueA),  
  37.      153, uint(TestObject::EnumValueB),  
  38.        0        // eod
  39. };  
  40. staticconstchar qt_meta_stringdata_TestObject[] = {  
  41.     "TestObject\0Long Huihu\0Author\0"
  42.     "TestObjectV1.0\0Version\0\0clicked()\0"
  43.     "pressed()\0onEventA(QString)\0onEventB(int)\0"
  44.     "QString\0propertyA\0propertyB\0TestEnum\0"
  45.     "EnumValueA\0EnumValueB\0"
  46. };  
  47. const QMetaObject TestObject::staticMetaObject = {  
  48.     { &QObject::staticMetaObject, qt_meta_stringdata_TestObject,  
  49.       qt_meta_data_TestObject, 0 }  
  50. };  
  51. #ifdef Q_NO_DATA_RELOCATION
  52. const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; }  
  53. #endif //Q_NO_DATA_RELOCATION
  54. const QMetaObject *TestObject::metaObject() const
  55. {  
  56.     return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;  
  57. }  
  58. void *TestObject::qt_metacast(constchar *_clname)  
  59. {  
  60.     if (!_clname) return 0;  
  61.     if (!strcmp(_clname, qt_meta_stringdata_TestObject))  
  62.         returnstatic_cast<void*>(const_cast< TestObject*>(this));  
  63.     return QObject::qt_metacast(_clname);  
  64. }  
  65. int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
  66. {  
  67.     _id = QObject::qt_metacall(_c, _id, _a);  
  68.     if (_id < 0)  
  69.         return _id;  
  70.     if (_c == QMetaObject::InvokeMetaMethod) {  
  71.         switch (_id) {  
  72.         case 0: clicked(); break;  
  73.         case 1: pressed(); break;  
  74.         case 2: onEventA((*reinterpret_castconst QString(*)>(_a[1]))); break;  
  75.         case 3: onEventB((*reinterpret_castint(*)>(_a[1]))); break;  
  76.         default: ;  
  77.         }  
  78.         _id -= 4;  
  79.     }  
  80. #ifndef QT_NO_PROPERTIES
  81.       elseif (_c == QMetaObject::ReadProperty) {  
  82.         void *_v = _a[0];  
  83.         switch (_id) {  
  84.         case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;  
  85.         case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;  
  86.         }  
  87.         _id -= 2;  
  88.     } elseif (_c == QMetaObject::WriteProperty) {  
  89.         void *_v = _a[0];  
  90.         switch (_id) {  
  91.         case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;  
  92.         case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;  
  93.         }  
  94.         _id -= 2;  
  95.     } elseif (_c == QMetaObject::ResetProperty) {  
  96.         switch (_id) {  
  97.         case 0: resetPropertyA(); break;  
  98.         case 1: resetPropertyB(); break;  
  99.         }  
  100.         _id -= 2;  
  101.     } elseif (_c == QMetaObject::QueryPropertyDesignable) {  
  102.         _id -= 2;  
  103.     } elseif (_c == QMetaObject::QueryPropertyScriptable) {  
  104.         _id -= 2;  
  105.     } elseif (_c == QMetaObject::QueryPropertyStored) {  
  106.         _id -= 2;  
  107.     } elseif (_c == QMetaObject::QueryPropertyEditable) {  
  108.         _id -= 2;  
  109.     } elseif (_c == QMetaObject::QueryPropertyUser) {  
  110.         _id -= 2;  
  111.     }  
  112. #endif // QT_NO_PROPERTIES
  113.     return _id;  
  114. }  
  115. // SIGNAL 0
  116. void TestObject::clicked()  
  117. {  
  118.     QMetaObject::activate(this, &staticMetaObject, 0, 0);  
  119. }  
  120. // SIGNAL 1
  121. void TestObject::pressed()  
  122. {  
  123.     QMetaObject::activate(this, &staticMetaObject, 1, 0);  
  124. }  
  125. 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檔案中以靜態資料的形式被定義,其排列有點類似可執行檔案中靜態資料資訊的排布。