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。
- #include
- class TestObject : public QObject
- {
- Q_OBJECT
- Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true
- Q_PROPERTY(QString propertyB READ getPropertyB WRITE getPropertyB RESET resetPropertyB)
- Q_CLASSINFO("Author", "Long Huihu")
- Q_CLASSINFO("Version", "TestObjectV1.0")
- Q_ENUMS(TestEnum)
- public:
- enum TestEnum {
- EnumValueA,
- EnumValueB
- };
- public:
- TestObject();
- signals:
- void clicked();
- void pressed();
- public slots:
- void onEventA(const QString &);
- void onEventB(int );
- }
示例類TestObject的moc檔案:
- #include "TestObject.h"
- #if !defined(Q_MOC_OUTPUT_REVISION)
- #error "The header file 'TestObject.h' doesn't include ."
- #elif Q_MOC_OUTPUT_REVISION != 62
- #error "This file was generated using the moc from 4.6.0. It"
- #error "cannot be used with the include files from this version of Qt."
- #error "(The moc has changed too much.)"
- #endif
- QT_BEGIN_MOC_NAMESPACE
- staticconst 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
- // classinfo: key, value
- 22, 11,
- 44, 29,
- // signals: signature, parameters, type, tag, flags
- 53, 52, 52, 52, 0x05,
- 63, 52, 52, 52, 0x05,
- // slots: signature, parameters, type, tag, flags
- 73, 52, 52, 52, 0x0a,
- 91, 52, 52, 52, 0x0a,
- // properties: name, type, flags
- 113, 105, 0x0a095007,
- 123, 105, 0x0a095007,
- // enums: name, flags, count, data
- 133, 0x0, 2, 48,
- // enum data: key, value
- 142, uint(TestObject::EnumValueA),
- 153, uint(TestObject::EnumValueB),
- 0 // eod
- };
- staticconstchar qt_meta_stringdata_TestObject[] = {
- "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"
- };
- const QMetaObject TestObject::staticMetaObject = {
- { &QObject::staticMetaObject, qt_meta_stringdata_TestObject,
- qt_meta_data_TestObject, 0 }
- };
- #ifdef Q_NO_DATA_RELOCATION
- const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; }
- #endif //Q_NO_DATA_RELOCATION
- const QMetaObject *TestObject::metaObject() const
- {
- return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
- }
- void *TestObject::qt_metacast(constchar *_clname)
- {
- if (!_clname) return 0;
- if (!strcmp(_clname, qt_meta_stringdata_TestObject))
- returnstatic_cast<void*>(const_cast< TestObject*>(this));
- return QObject::qt_metacast(_clname);
- }
- int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
- {
- _id = QObject::qt_metacall(_c, _id, _a);
- if (_id < 0)
- return _id;
- if (_c == QMetaObject::InvokeMetaMethod) {
- switch (_id) {
- case 0: clicked(); break;
- case 1: pressed(); break;
- case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;
- case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;
- default: ;
- }
- _id -= 4;
- }
- #ifndef QT_NO_PROPERTIES
- elseif (_c == QMetaObject::ReadProperty) {
- void *_v = _a[0];
- switch (_id) {
- case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;
- case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;
- }
- _id -= 2;
- } elseif (_c == QMetaObject::WriteProperty) {
- void *_v = _a[0];
- switch (_id) {
- case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;
- case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;
- }
- _id -= 2;
- } elseif (_c == QMetaObject::ResetProperty) {
- switch (_id) {
- case 0: resetPropertyA(); break;
- case 1: resetPropertyB(); break;
- }
- _id -= 2;
- } elseif (_c == QMetaObject::QueryPropertyDesignable) {
- _id -= 2;
- } elseif (_c == QMetaObject::QueryPropertyScriptable) {
- _id -= 2;
- } elseif (_c == QMetaObject::QueryPropertyStored) {
- _id -= 2;
- } elseif (_c == QMetaObject::QueryPropertyEditable) {
- _id -= 2;
- } elseif (_c == QMetaObject::QueryPropertyUser) {
- _id -= 2;
- }
- #endif // QT_NO_PROPERTIES
- return _id;
- }
- // SIGNAL 0
- void TestObject::clicked()
- {
- QMetaObject::activate(this, &staticMetaObject, 0, 0);
- }
- // SIGNAL 1
- void TestObject::pressed()
- {
- QMetaObject::activate(this, &staticMetaObject, 1, 0);
- }
- 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檔案中以靜態資料的形式被定義,其排列有點類似可執行檔案中靜態資料資訊的排布。