2.MOC文件解读(上)——MOC文件中的数据

接上篇文章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源码

https://qtguide.ustclug.org/

Inside Qt Series (全集)

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值