Qt MetaObject 详解之二:QMeta数据以及数据结构信息
2016-11-02 11:09
537 查看
如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成 meta信息,这个信息在前一篇中所提到的moc文件中。这一篇通过解析这个一个示例moc文件来阐述这些meta信息的存储方式和格式;本篇先说明了一 下QMetaObject的数据结构,然后呈现了一个简单的类TestObject类及其生成的moc文件,最后对这个moc文件个内容进行了详细解释。
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是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类继承自QObject,定义了两个Property:propertyA,propertyB;两个classinfo:Author,Version;一个枚举:TestEnum。
view plaincopy
to clipboardprint?
#include
class TestObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false)
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 );
}
view plaincopy
to clipboardprint?
#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
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
// 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
};
static const char 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(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_TestObject))
return static_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
else if (_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;
} else if (_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;
} else if (_c == QMetaObject::ResetProperty) {
switch (_id) {
case 0: resetPropertyA(); break;
case 1: resetPropertyB(); break;
}
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyDesignable) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyScriptable) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyStored) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyEditable) {
_id -= 2;
} else if (_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文件中以静态数据的形式被定义,其排列有点类似可执行文件中静态数据信息的排布。
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。view plaincopy
to clipboardprint?
#include
class TestObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false)
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文件:
view plaincopyto clipboardprint?
#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
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
// 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
};
static const char 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(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_TestObject))
return static_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
else if (_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;
} else if (_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;
} else if (_c == QMetaObject::ResetProperty) {
switch (_id) {
case 0: resetPropertyA(); break;
case 1: resetPropertyB(); break;
}
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyDesignable) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyScriptable) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyStored) {
_id -= 2;
} else if (_c == QMetaObject::QueryPropertyEditable) {
_id -= 2;
} else if (_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文件中以静态数据的形式被定义,其排列有点类似可执行文件中静态数据信息的排布。
相关文章推荐
- Qt MetaObject System详解之二:meta数据和数据结构
- Qt MetaObject 详解之一:简单介绍
- Qt MetaObject System详解之四:meta call
- QtMetaObjectsysmtem详解之三:QMetaObject接口实现
- Qt MetaObject System详解之五:signal&slot
- Qt MetaObject System详解
- 迭代网络数据头信息 以及获取客户端真实IP
- Inside Qt Series (八):Meta Object Class overview
- html meta信息详解
- Inside QT Series (六):元对象编译器 - Meta Object Compiler (moc)
- 转载: 对GPS全球定位系统定位信息的接收以及对各定位参数数据的提取方法(VC++)
- QT的 Meta-Object系统
- PUSH模式动态水晶报表的实现-动态数据以及汇总信息的的实现
- Inside Qt Series (六):元对象编译器 – Meta Object Compiler (moc)
- Qt的Meta-Object系统
- Qt meta object compiler moc:Invalid argument错误
- Qt的元对象(Meta-Object)系统简介
- Inside Qt Series (九):QMetaObject class data members
- ASP.NET数据传递:获取META信息
- Inside QT Series (八):Meta Object Class