从源码角度分析Qt元对象系统1

简介: 从源码角度分析Qt元对象系统

一、演示代码

test.h

#ifndef TEST_H
#define TEST_H
 
#include <QObject>
 
class test : public QObject {
  Q_OBJECT
public:
  Q_INVOKABLE explicit test(QObject *parent = nullptr);
 
  Q_PROPERTY(int a READ f WRITE g)
 
  Q_INVOKABLE void t1();
  Q_INVOKABLE int t2(const QString &name, QString mark);
 
signals:
  void sgn1();
  int sgn2(int);
 
public slots:
  void slt1();
  int slt2(int);
 
private slots:
  void slt3();
  int slt4(int);
 
private:
  int f() { return ccc; }
  void g(int i) { ccc = i; }
  int ccc;
};
 
#endif // TEST_H

test.cpp

#include "test.h"
#include <QDebug>
 
test::test(QObject *parent) : QObject(parent) {}
 
void test::t1() {}
 
int test::t2(const QString &name, QString mark) {
  qDebug() << name << mark;
  return 0;
}
 
void test::slt1() {
  qDebug() << "slt1()";
  emit sgn1();
}
 
int test::slt2(int) {
  qDebug() << "slt2()";
  return 1;
}
 
void test::slt3() {
  qDebug() << "slt3()";
  emit sgn2(3);
}
 
int test::slt4(int) {
  qDebug() << "slt4()";
  return 1;
}

头文件展开,以及编译后生成:

Q_OBJECT 宏展开

#define Q_OBJECT \
public: \
    ...
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    ...

moc_test.cpp

/****************************************************************************
** Meta object code from reading C++ file 'test.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.9)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
 
#include "../../TestListWidget/test.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'test.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.9. 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
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_test_t {
    QByteArrayData data[14];
    char stringdata0[61];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_test_t qt_meta_stringdata_test = {
    {
QT_MOC_LITERAL(0, 0, 4), // "test"
QT_MOC_LITERAL(1, 5, 4), // "sgn1"
QT_MOC_LITERAL(2, 10, 0), // ""
QT_MOC_LITERAL(3, 11, 4), // "sgn2"
QT_MOC_LITERAL(4, 16, 4), // "slt1"
QT_MOC_LITERAL(5, 21, 4), // "slt2"
QT_MOC_LITERAL(6, 26, 4), // "slt3"
QT_MOC_LITERAL(7, 31, 4), // "slt4"
QT_MOC_LITERAL(8, 36, 2), // "t1"
QT_MOC_LITERAL(9, 39, 2), // "t2"
QT_MOC_LITERAL(10, 42, 4), // "name"
QT_MOC_LITERAL(11, 47, 4), // "mark"
QT_MOC_LITERAL(12, 52, 6), // "parent"
QT_MOC_LITERAL(13, 59, 1) // "a"
 
    },
    "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0"
    "t1\0t2\0name\0mark\0parent\0a"
};
#undef QT_MOC_LITERAL
 
static const uint qt_meta_data_test[] = {
 
 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       8,   14, // methods
       1,   76, // properties
       0,    0, // enums/sets
       2,   79, // constructors
       0,       // flags
       2,       // signalCount
 
 // signals: name, argc, parameters, tag, flags
       1,    0,   54,    2, 0x06 /* Public */,
       3,    1,   55,    2, 0x06 /* Public */,
 
 // slots: name, argc, parameters, tag, flags
       4,    0,   58,    2, 0x0a /* Public */,
       5,    1,   59,    2, 0x0a /* Public */,
       6,    0,   62,    2, 0x08 /* Private */,
       7,    1,   63,    2, 0x08 /* Private */,
 
 // methods: name, argc, parameters, tag, flags
       8,    0,   66,    2, 0x02 /* Public */,
       9,    2,   67,    2, 0x02 /* Public */,
 
 // signals: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
 
 // slots: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
 
 // methods: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::QString, QMetaType::QString,   10,   11,
 
 // constructors: parameters
    0x80000000 | 2, QMetaType::QObjectStar,   12,
    0x80000000 | 2,
 
 // properties: name, type, flags
      13, QMetaType::Int, 0x00095003,
 
 // constructors: name, argc, parameters, tag, flags
       0,    1,   72,    2, 0x0e /* Public */,
       0,    0,   75,    2, 0x2e /* Public | MethodCloned */,
 
       0        // eod
};
 
void test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::CreateInstance) {
        switch (_id) {
        case 0: { test *_r = new test((*reinterpret_cast< QObject*(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
        case 1: { test *_r = new test();
            if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
        default: break;
        }
    } else if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<test *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->sgn1(); break;
        case 1: { int _r = _t->sgn2((*reinterpret_cast< int(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;
        case 2: _t->slt1(); break;
        case 3: { int _r = _t->slt2((*reinterpret_cast< int(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;
        case 4: _t->slt3(); break;
        case 5: { int _r = _t->slt4((*reinterpret_cast< int(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;
        case 6: _t->t1(); break;
        case 7: { int _r = _t->t2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< QString(*)>(_a[2])));
            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (test::*)();
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::sgn1)) {
                *result = 0;
                return;
            }
        }
        {
            using _t = int (test::*)(int );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::sgn2)) {
                *result = 1;
                return;
            }
        }
    }
#ifndef QT_NO_PROPERTIES
    else if (_c == QMetaObject::ReadProperty) {
        auto *_t = static_cast<test *>(_o);
        Q_UNUSED(_t)
        void *_v = _a[0];
        switch (_id) {
        case 0: *reinterpret_cast< int*>(_v) = _t->f(); break;
        default: break;
        }
    } else if (_c == QMetaObject::WriteProperty) {
        auto *_t = static_cast<test *>(_o);
        Q_UNUSED(_t)
        void *_v = _a[0];
        switch (_id) {
        case 0: _t->g(*reinterpret_cast< int*>(_v)); break;
        default: break;
        }
    } else if (_c == QMetaObject::ResetProperty) {
    }
#endif // QT_NO_PROPERTIES
}
 
QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { {
    &QObject::staticMetaObject,
    qt_meta_stringdata_test.data,
    qt_meta_data_test,
    qt_static_metacall,
    nullptr,
    nullptr
} };
 
 
const QMetaObject *test::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
 
void *test::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_test.stringdata0))
        return static_cast<void*>(this);
    return QObject::qt_metacast(_clname);
}
 
int test::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) {
        if (_id < 8)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 8;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 8)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 8;
    }
#ifndef QT_NO_PROPERTIES
    else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
            || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {
        qt_static_metacall(this, _c, _id, _a);
        _id -= 1;
    } else if (_c == QMetaObject::QueryPropertyDesignable) {
        _id -= 1;
    } else if (_c == QMetaObject::QueryPropertyScriptable) {
        _id -= 1;
    } else if (_c == QMetaObject::QueryPropertyStored) {
        _id -= 1;
    } else if (_c == QMetaObject::QueryPropertyEditable) {
        _id -= 1;
    } else if (_c == QMetaObject::QueryPropertyUser) {
        _id -= 1;
    }
#endif // QT_NO_PROPERTIES
    return _id;
}
 
// SIGNAL 0
void test::sgn1()
{
    QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
 
// SIGNAL 1
int test::sgn2(int _t1)
{
    int _t0{};
    void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)), const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
    return _t0;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

二、QMetaObject

QMetaObject 类描述了 QObject 及其派生类对象的所有元信息,该类是 Qt 元对象系统的核心类,通过该类的成员函数可以获取 QObject 及其派生类对象的所有元信息,

因此可以说 QMetaObject 类的对象是 Qt 中的元对象。

1、获取QMetaObject

QObject中定义了一个虚函数:

virtual const QMetaObject *metaObject() const;

在moc_test.cpp代码,有相应的实现:

const QMetaObject *test::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

2、staticMetaObject

moc_test.cpp中相应代码为:

QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { {
    &QObject::staticMetaObject,
    qt_meta_stringdata_test.data,
    qt_meta_data_test,
    qt_static_metacall,
    nullptr,
    nullptr
} };

QMetaObject结构体定义如下:

struct Q_CORE_EXPORT QMetaObject
{
...
    struct { // private data
        const QMetaObject *superdata;
        const QByteArrayData *stringdata;
        const uint *data;
        typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
        StaticMetacallFunction static_metacall;
        const QMetaObject * const *relatedMetaObjects;
        void *extradata; //reserved for future use
    } d;
};

因此可知:superdata指向父类的staticMetaObject,从而形成了对象间的层级链。stringdata指向了qt_meta_stringdata_test.datadata指向了qt_meta_data_teststatic_metacall指向了qt_static_metacall

3、qt_meta_stringdata_test

struct qt_meta_stringdata_test_t {
    QByteArrayData data[14];
    char stringdata0[61];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_test_t qt_meta_stringdata_test = {
    {
QT_MOC_LITERAL(0, 0, 4), // "test"
QT_MOC_LITERAL(1, 5, 4), // "sgn1"
QT_MOC_LITERAL(2, 10, 0), // ""
QT_MOC_LITERAL(3, 11, 4), // "sgn2"
QT_MOC_LITERAL(4, 16, 4), // "slt1"
QT_MOC_LITERAL(5, 21, 4), // "slt2"
QT_MOC_LITERAL(6, 26, 4), // "slt3"
QT_MOC_LITERAL(7, 31, 4), // "slt4"
QT_MOC_LITERAL(8, 36, 2), // "t1"
QT_MOC_LITERAL(9, 39, 2), // "t2"
QT_MOC_LITERAL(10, 42, 4), // "name"
QT_MOC_LITERAL(11, 47, 4), // "mark"
QT_MOC_LITERAL(12, 52, 6), // "parent"
QT_MOC_LITERAL(13, 59, 1) // "a"
 
    },
    "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0"
    "t1\0t2\0name\0mark\0parent\0a"
};

由上面可知,其保存了元对象的类名、方法名、信号名、槽名、属性名以及各个参数的名字,这些名字主要用来通过名字反射相应的类、方法、信号、槽以及属性等。

4、qt_meta_data_test

static const uint qt_meta_data_test[] = {
 
 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       8,   14, // methods
       1,   76, // properties
       0,    0, // enums/sets
       2,   79, // constructors
       0,       // flags
       2,       // signalCount
 
 // signals: name, argc, parameters, tag, flags
       1,    0,   54,    2, 0x06 /* Public */,
       3,    1,   55,    2, 0x06 /* Public */,
 
 // slots: name, argc, parameters, tag, flags
       4,    0,   58,    2, 0x0a /* Public */,
       5,    1,   59,    2, 0x0a /* Public */,
       6,    0,   62,    2, 0x08 /* Private */,
       7,    1,   63,    2, 0x08 /* Private */,
 
 // methods: name, argc, parameters, tag, flags
       8,    0,   66,    2, 0x02 /* Public */,
       9,    2,   67,    2, 0x02 /* Public */,
 
 // signals: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
 
 // slots: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
    QMetaType::Void,
    QMetaType::Int, QMetaType::Int,    2,
 
 // methods: parameters
    QMetaType::Void,
    QMetaType::Int, QMetaType::QString, QMetaType::QString,   10,   11,
 
 // constructors: parameters
    0x80000000 | 2, QMetaType::QObjectStar,   12,
    0x80000000 | 2,
 
 // properties: name, type, flags
      13, QMetaType::Int, 0x00095003,
 
 // constructors: name, argc, parameters, tag, flags
       0,    1,   72,    2, 0x0e /* Public */,
       0,    0,   75,    2, 0x2e /* Public | MethodCloned */,
 
       0        // eod
};

以上描述了类、方法、信号、槽以及属性的一些信息。其前面几个字节对应QMetaObjectPrivate结构体:

struct QMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData;
    int flags;
    int signalCount;
...
}

qt_meta_stringdata_testqt_meta_data_test基本存储元对象信息。

三、通过QMetaObject获取classInfo

1、classInfo函数

QMetaClassInfo QMetaObject::classInfo(int index) const
{
    int i = index;
    // classInfoOffset 求父类的偏移
    i -= classInfoOffset();
    if (i < 0 && d.superdata)
        return d.superdata->classInfo(index);
 
    QMetaClassInfo result;
    // priv 把d.data转换成QMetaObjectPrivate对象
    if (i >= 0 && i < priv(d.data)->classInfoCount) {
        result.mobj = this;
        result.handle = priv(d.data)->classInfoData + 2*i;
    }
    return result;
}

可见其主要根据QMetaObjectPrivate中信息,来初始化QMetaClassInfo类。

2、QMetaClassInfo

函数classInfo中根据QMetaObjectPrivate信息,初始了QMetaClassInfomobjhandle

const char *QMetaClassInfo::name() const
{
    if (!mobj)
        return 0;
    // mobj->d.data[handle] 获取qt_meta_data_test中的数据
    // rawStringData 根据index获取qt_meta_stringdata_test.data中字符串
    return rawStringData(mobj, mobj->d.data[handle]);
}
 
const char* QMetaClassInfo::value() const
{
    if (!mobj)
        return 0;
    // mobj->d.data[handle] 获取qt_meta_data_test中的数据
    return rawStringData(mobj, mobj->d.data[handle + 1]);
}
 
static inline const QByteArray stringData(const QMetaObject *mo, int index)
{
    Q_ASSERT(priv(mo->d.data)->revision >= 7);
    const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
    Q_ASSERT(data.ptr->ref.isStatic());
    Q_ASSERT(data.ptr->alloc == 0);
    Q_ASSERT(data.ptr->capacityReserved == 0);
    Q_ASSERT(data.ptr->size >= 0);
    return data;
}
 
static inline const char *rawStringData(const QMetaObject *mo, int index)
{
    return stringData(mo, index).data();
}

四、通过QMetaObject获取constructor

1、constructor函数

QMetaMethod QMetaObject::constructor(int index) const
{
    int i = index;
    QMetaMethod result;
    Q_ASSERT(priv(d.data)->revision >= 2);
    // priv 把d.data转换成QMetaObjectPrivate对象
    // constructorCount 值为2
    if (i >= 0 && i < priv(d.data)->constructorCount) {
        result.mobj = this;
        // constructorData 值为 79 + 5*i
        // 当index=0时,其对应qt_meta_data_test中第79个偏移的位置,即
        // constructors: name, argc, parameters, tag, flags
        // 0,    1,   72,    2, 0x0e /* Public */,
        // 0,    0,   75,    2, 0x2e /* Public | MethodCloned */,
        result.handle = priv(d.data)->constructorData + 5*i;
    }
    return result;
}

可见其主要根据QMetaObjectPrivate中信息,来初始化QMetaMethod类。由上分析可知handle指向qt_meta_data_test的偏移地址,其存储了QMetaMethod中所需要的信息。信息为:

 // constructors: name, argc, parameters, tag, flags
       0,    1,   72,    2, 0x0e /* Public */,
       0,    0,   75,    2, 0x2e /* Public | MethodCloned */,

分别对应构造函数的name, argc, parameters, tag, flags信息。

flags值含义如下:

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
};

2、QMetaMethod

函数classInfo中根据QMetaObjectPrivate信息,初始了QMetaMethodmobjhandle。首先我们看下QMetaMethod中的name()函数

2.1 name()

QByteArray QMetaMethod::name() const
{
    if (!mobj)
        return QByteArray();
    // get() 强制转换为QMetaMethodPrivate对象
    return QMetaMethodPrivate::get(this)->name();
}
 
QByteArray QMetaMethodPrivate::name() const
{
    return stringData(mobj, mobj->d.data[handle]);
}
 
static inline const QByteArray stringData(const QMetaObject *mo, int index)
{
    // d.stringdata指向qt_meta_stringdata_test.data,其是QByteArrayData数组
    // QByteArrayDataPtr data中ptr初始化为QByteArrayData数组地址
    const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
    return data;
}

stringData函数分析:mo->d.stringdata指向qt_meta_stringdata_test.data,其值为:

struct qt_meta_stringdata_test_t {
    QByteArrayData data[14];
    char stringdata0[61];
};
 
static const qt_meta_stringdata_test_t qt_meta_stringdata_test = {
    {
QT_MOC_LITERAL(0, 0, 4), // "test"
QT_MOC_LITERAL(1, 5, 4), // "sgn1"
QT_MOC_LITERAL(2, 10, 0), // ""
QT_MOC_LITERAL(3, 11, 4), // "sgn2"
QT_MOC_LITERAL(4, 16, 4), // "slt1"
QT_MOC_LITERAL(5, 21, 4), // "slt2"
QT_MOC_LITERAL(6, 26, 4), // "slt3"
QT_MOC_LITERAL(7, 31, 4), // "slt4"
QT_MOC_LITERAL(8, 36, 2), // "t1"
QT_MOC_LITERAL(9, 39, 2), // "t2"
QT_MOC_LITERAL(10, 42, 4), // "name"
QT_MOC_LITERAL(11, 47, 4), // "mark"
QT_MOC_LITERAL(12, 52, 6), // "parent"
QT_MOC_LITERAL(13, 59, 1) // "a"
 
    },
    "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0"
    "t1\0t2\0name\0mark\0parent\0a"
};

mo->d.stringdata[0]返回QT_MOC_LITERAL(0, 0, 4), // "test",类型为QByteArrayData。

#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
 
#define Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
    Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset)
 
#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
    { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } 

QT_MOC_LITERAL构成了一个QByteArrayData数组,而QByteArrayData被重定义为了QArrayData结构体

typedef QArrayData QByteArrayData;
struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;
 
    qptrdiff offset; // in bytes from beginning of header
...
}

/即QT_MOC_LITERAL(0, 0, 4), // "test"展开后变成{ Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } ,相当于初始化QArrayData中的ref,size,alloc,capacityReserved,offset几个成员。

回过头来接着看static inline const QByteArray stringData(const QMetaObject *mo, int index)函数:

static inline const QByteArray stringData(const QMetaObject *mo, int index)
{
    // d.stringdata指向qt_meta_stringdata_test.data,其是QByteArrayData数组
    // QByteArrayDataPtr data中ptr初始化为QByteArrayData数组地址
    const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
    return data;
}
 
struct QByteArrayDataPtr
{
    QByteArrayData *ptr;
};

从上可知:QByteArrayDataPtr data中的ptr被初始化成了qt_meta_stringdata_test.data数组地址。并且在返回时转换成了QByteArray对象。那么QByteArrayDataPtr又是如何转换成QByteArray的呢

我们跟踪QByteArray代码,其提供了一个构造函数:

typedef QTypedArrayData<char> Data;
inline QByteArray(QByteArrayDataPtr dd)
        : d(static_cast<Data *>(dd.ptr))
{
}
 
template <class T>
struct QTypedArrayData
    : QArrayData
{
...
}

其调用此构造函数转换成QByteArray,从代码中可知其把dd.ptr转换成了Data结构体,而Data是QTypedArrayData重定义。QTypedArrayData是一个继承QArrayData的模板。QByteArrayDataPtr dd成员变量ptr类型正好是QArrayData。从而整个对应起来了。QArrayData和QTypedArrayData的代码如下:

typedef QArrayData QByteArrayData;
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;
    }
...
}
 
typedef QTypedArrayData<char> Data;
inline QByteArray(QByteArrayDataPtr dd)
        : d(static_cast<Data *>(dd.ptr))
{
}
 
template <class T>
struct QTypedArrayData
    : QArrayData
{
...
}

2.2  QMetaMethod::access()

QMetaMethod::Access QMetaMethod::access() const
{
    if (!mobj)
        return Private;
    // constructorData 值为 79 + 5*i
    // 当index=0时,其对应qt_meta_data_test中第79个偏移的位置,即
    // constructors: name, argc, parameters, tag, flags
    // 0,    1,   72,    2, 0x0e /* Public */,
    // 0,    0,   75,    2, 0x2e /* Public | MethodCloned */
    // 即mobj->d.data[handle + 4]值为0x0e
    return (QMetaMethod::Access)(mobj->d.data[handle + 4] & AccessMask);
}

(mobj->d.data[handle + 4] 值为0x0e,flags代表的含义如下:

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
};


0x0e为0b0000,1110,可知其访问权限为public,方法类型为constructor。依次类推可以获取访问权限与方法类型,以及方法修订版本(对应方法QMetaMethod::revision(),QMetaMethod::methodType())。

2.3 QMetaMethod::tag()

const char *QMetaMethod::tag() const
{
    if (!mobj)
        return 0;
    return QMetaMethodPrivate::get(this)->tag().constData();
}
 
QByteArray QMetaMethodPrivate::tag() const
{
    Q_ASSERT(priv(mobj->d.data)->revision >= 7);
    // constructors: name, argc, parameters, tag, flags
    // 0,    1,   72,    2, 0x0e /* Public */,
    // 对应值为2,指向第2个字符串,值为空
    return stringData(mobj, mobj->d.data[handle + 3]);
}

这个函数可用于注解。其实现示例如:

定义tag,用于注解

     // In the class MainWindow declaration
     #ifndef Q_MOC_RUN
     // define the tag text as empty, so the compiler doesn't see it
     #  define MY_CUSTOM_TAG
     #endif
     ...
     private slots:
         MY_CUSTOM_TAG void testFunc();

获取定义的tag,并可解释成特殊用途:

     MainWindow win;
     win.show();
 
     int functionIndex = win.metaObject()->indexOfSlot("testFunc()");
     QMetaMethod mm = win.metaObject()->method(functionIndex);
     qDebug() << mm.tag(); // prints MY_CUSTOM_TAG

从源码角度分析Qt元对象系统2:https://developer.aliyun.com/article/1597143

目录
相关文章
|
1月前
|
存储 Windows
(13) Qt事件系统(two)
文章详细介绍了Qt事件系统,包括事件分发、自定义事件、事件传播机制、事件过滤以及事件与信号的区别。
84 3
(13) Qt事件系统(two)
|
1月前
|
编解码 程序员
(12)Qt事件系统(one)
本文详细介绍了Qt事件系统,包括各种系统事件、鼠标事件、键盘事件、定时器等的处理方法和示例代码。
76 0
(12)Qt事件系统(one)
|
2月前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
170 12
|
2月前
|
Windows
QT源码拾贝6-11(qwindowswindow)
这篇文章深入探讨了Qt源码中与窗口激活相关的函数,QDebug运算符重载,vscode的变量提示,Windows常用类型名,获取所有窗体的方法,以及QSharedPointer智能指针的使用。
QT源码拾贝6-11(qwindowswindow)
|
2月前
|
存储 Java C++
QT源码拾贝0-5(qimage和qpainter)
这篇文章介绍了在Qt源码中qimage和qpainter的使用,包括线程池的使用、智能指针的存储、std::exchange函数的应用、获取类对象的方法以及QChar字节操作。
QT源码拾贝0-5(qimage和qpainter)
|
3月前
从源码角度分析Qt元对象系统2
从源码角度分析Qt元对象系统
55 0
|
4月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
183 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
3月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
90 0
|
2月前
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。
|
2月前