接上篇文章https://blog.csdn.net/Master_Cui/article/details/109007448
moc文件开始是个结构体
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_moctest_t {
QByteArrayData data[7];
char stringdata0[41];
};
里面有两个数组,其中QByteArrayData在qarraydata.h中,结构如下
struct Q_CORE_EXPORT QArrayData
{
QtPrivate::RefCount ref;
int size;
uint alloc : 31;
uint capacityReserved : 1;
qptrdiff offset; // in bytes from beginning of header
void *data()
{
Q_ASSERT(size == 0
|| offset < 0 || size_t(offset) >= sizeof(QArrayData));
return reinterpret_cast<char *>(this) + offset;
}
const void *data() const
{
Q_ASSERT(size == 0
|| offset < 0 || size_t(offset) >= sizeof(QArrayData));
return reinterpret_cast<const char *>(this) + offset;
}
//以下省略......
};
size是指针指向数据的大小,offset是数据指针和当前对象指针的距离(偏移量),如果数据大小不是0,data函数就根据偏移量offset和当前对象的地址返回当前数据的指针
接下来是这两部分QT_MOC_LITERAL和qt_meta_stringdata_moctest_t
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_moctest_t qt_meta_stringdata_moctest = {
{
QT_MOC_LITERAL(0, 0, 7), // "moctest"
QT_MOC_LITERAL(1, 8, 5), // "sigf1"
QT_MOC_LITERAL(2, 14, 0), // ""
QT_MOC_LITERAL(3, 15, 5), // "sigf2"
QT_MOC_LITERAL(4, 21, 5), // "slotf"
QT_MOC_LITERAL(5, 27, 6), // "slotf2"
QT_MOC_LITERAL(6, 34, 6) // "slotf3"
},
"moctest\0sigf1\0\0sigf2\0slotf\0slotf2\0"
"slotf3"
};
先是一个复杂的宏QT_MOC_LITERAL,然后通过该宏初始化结构体qt_meta_stringdata_moctest_t,而结构体的第一个数组就是QByteArrayData,所以宏QT_MOC_LITERAL返回的一定是QByteArrayData数据
七个QT_MOC_LITERAL宏后面的一堆字符串对应结构体qt_meta_stringdata_moctest_t中的第二个数组,字符串的内容分别是类名,信号和槽
先看QT_MOC_LITERAL的实现,开始又是一个大宏Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET,但是最终的展开如下
#define Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset)
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET的展开如下
#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
{ Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } \
大括号里面的内容正好用来初始化QArrayData中的成员变量,Q_REFCOUNT_INITIALIZE_STATIC是-1,alloc和capacityReserved都是0
之后大宏里面是
(len, qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs - idx * sizeof(QByteArrayData)))
len对应size,后面的其实就是一个数qptrdiff就是int,offsetof用于求结构体中一个成员在该结构体中的偏移量,在stddef.h头文件中。qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs - idx * sizeof(QByteArrayData))对应offset,算出来这两个数据的值,然后初始化QByteArrayData数组
通过上面的一通计算,就能通过QByteArrayData中的data函数计算出下面字符串的地址
for (int i=0;i<7;++i) {
cout<<(char *)(qt_meta_stringdata_moctest.data[i].data())<<endl;
}
接着,再看一下数组qt_meta_data_moctest
static const uint qt_meta_data_moctest[] = {
// content:
8, // revision 版本号,没啥用
0, // classname
0, 0, // classinfo
5, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount
// signals: name, argc, parameters, tag, flags
1, 1, 39, 2, 0x06 /* Public */,
3, 2, 42, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
4, 1, 47, 2, 0x09 /* Protected */,
5, 1, 50, 2, 0x09 /* Protected */,
6, 1, 53, 2, 0x09 /* Protected */,
// signals: parameters
QMetaType::Void, QMetaType::Double, 2,
QMetaType::Int, QMetaType::Char, QMetaType::Int, 2, 2,
// slots: parameters
QMetaType::Void, QMetaType::Double, 2,
QMetaType::Int, QMetaType::Char, 2,
QMetaType::Bool, QMetaType::Char, 2,
0 // eod
};
下面拆解该数组
1.目录数据content
8, // revision 版本号,不用管
0, // classname 类名在stringdata中的索引,总是0,qt_meta_stringdata_moctest.data[0].data()
0, 0, // classinfo//不用管
5, 14, // methods//信号槽的总数,3个槽函数,2个信号函数,从该数组中的第15个元素(qt_meta_data_moctest[14]])开始描述
0, 0, // properties 属性数量和在该数组中的起始位置,这里为0
0, 0, // enums/sets //不用管
0, 0, // constructors //不用管
0, // flags //不用管
2, // signalCount //信号个数2个
这些数据对应一个结构QMetaObjectPrivate
struct QMetaObjectPrivate
{
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
int signalCount;
//以下省略......
};
2.信号槽参数数据
// signals: name, argc, parameters, tag, flags
1, 1, 39, 2, 0x06 /* Public */,
3, 2, 42, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
4, 1, 47, 2, 0x09 /* Protected */,
5, 1, 50, 2, 0x09 /* Protected */,
6, 1, 53, 2, 0x09 /* Protected */,
第一列数据表示信号槽的索引,以 1, 1, 39, 2, 0x06 /* Public */为例,第一个1和QT_MOC_LITERAL(1, 8, 5)中的表示的都是同一个意思
第二列数据表示信号槽函数的参数个数,以以 1, 1, 39, 2, 0x06 /* Public */为例,第二个1表示信号函数sigf1有一个形参,参数类型是double
第三列数据表示参数数据的描述是从数组qt_meta_data_moctest[39]开始描述的,比如这里面的39,开头14个content数据,然后又有25个数字,到第11行的 QMetaType::Void时,正好是qt_meta_data_moctest[39]
第四列数据表示tag信息,是 qt_meta_stringdata_Widget.data[2].data() 指向的空字符串。好像一般的MOC文件的tag标志都是2,没啥特殊意义
第五列数据表示信号槽函数标志位,在qmetaobject_p.h 里声明,声明如下
enum MethodFlags {
AccessPrivate = 0x00,
AccessProtected = 0x01,
AccessPublic = 0x02,
AccessMask = 0x03, //mask
MethodMethod = 0x00,
MethodSignal = 0x04,
MethodSlot = 0x08,
MethodConstructor = 0x0c,
MethodTypeMask = 0x0c,
MethodCompatibility = 0x10,
MethodCloned = 0x20,
MethodScriptable = 0x40,
MethodRevisioned = 0x80
};
将上述枚举按位或,就能得到第五列数据以 1, 1, 39, 2, 0x06 /* Public */为例,0x06=0x02 | 0x04,表示public signal
// signals: parameters
QMetaType::Void, QMetaType::Double, 2,
QMetaType::Int, QMetaType::Char, QMetaType::Int, 2, 2,
// slots: parameters
QMetaType::Void, QMetaType::Double, 2,
QMetaType::Int, QMetaType::Char, 2,
QMetaType::Bool, QMetaType::Char, 2,
第10行到第17行表示信号槽函数的返回值和形参类型,以QMetaType::Int, QMetaType::Char, QMetaType::Int, 2, 2,为例,QMetaType::Int是信号的返回值,QMetaType::Char, QMetaType::Int是信号函数的形参
最后一块数据部分就是moctest::staticMetaObject
QT_INIT_METAOBJECT const QMetaObject moctest::staticMetaObject = { {
QMetaObject::SuperData::link<QObject::staticMetaObject>(),
qt_meta_stringdata_moctest.data,
qt_meta_data_moctest,
qt_static_metacall,
nullptr,
nullptr
} };
moctest::staticMetaObject的类型是QMetaObject,QMetaObject的结构如下(位于qobjectdefs.h中)
struct Q_CORE_EXPORT QMetaObject
{
struct { // private data
SuperData superdata;
const QByteArrayData *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const SuperData *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
};
里面的数据d的格式和moctest::staticMetaObject的初始值对应
SuperData superdata的初始值为QMetaObject::SuperData::link<QObject::staticMetaObject>(),表示基类的元对象数据
stringdata的初始值为qt_meta_stringdata_moctest.data,指向的是qt_meta_stringdata_moctest_t中的data数组(也就是那八个字符串的指针)
data的初始值为qt_meta_data_moctest,该指针指向数组qt_meta_data_moctest
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);声明了一个函数指针,指针的类型是void (*)(QObject *, QMetaObject::Call, int, void **);
接着,通过函数指针的类型定义了一个函数指针static_metacall,static_metacall的初始值为qt_static_metacall
最后两个数据relatedMetaObjects和extradata分别表示相关元对象指针和额外数据,一般都都指向空,不是很重要
参考
Qt5.14源码
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出