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

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

从源码角度分析Qt元对象系统 一、演示代码 test.h
#ifndef TEST_H
#define TEST_H

#include 

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 

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

#include "../../TestListWidget/test.h"
#include 
#include 
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'test.h' doesn't include ."
#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"

    },
    "testsgn1sgn2slt1slt2slt3slt4"
    "t1t2namemarkparenta"
};
#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 ,
       3,    1,   55,    2, 0x06 ,

 // slots: name, argc, parameters, tag, flags
       4,    0,   58,    2, 0x0a ,
       5,    1,   59,    2, 0x0a ,
       6,    0,   62,    2, 0x08 ,
       7,    1,   63,    2, 0x08 ,

 // methods: name, argc, parameters, tag, flags
       8,    0,   66,    2, 0x02 ,
       9,    2,   67,    2, 0x02 ,

 // 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 ,
       0,    0,   75,    2, 0x2e ,

       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(_a[0]) = _r; } break;
        case 1: { test *_r = new test();
            if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break;
        default: break;
        }
    } else if (_c == QmetaObject::InvokemetaMethod) {
        auto *_t = static_cast(_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(_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(_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(_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(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(_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(reinterpret_cast(&_t0)), const_cast(reinterpret_cast(&_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.data,data指向了qt_meta_data_test,static_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"

    },
    "testsgn1sgn2slt1slt2slt3slt4"
    "t1t2namemarkparenta"
};

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

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 ,
       3,    1,   55,    2, 0x06 ,

 // slots: name, argc, parameters, tag, flags
       4,    0,   58,    2, 0x0a ,
       5,    1,   59,    2, 0x0a ,
       6,    0,   62,    2, 0x08 ,
       7,    1,   63,    2, 0x08 ,

 // methods: name, argc, parameters, tag, flags
       8,    0,   66,    2, 0x02 ,
       9,    2,   67,    2, 0x02 ,

 // 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 ,
       0,    0,   75,    2, 0x2e ,

       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_test和qt_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信息,初始了QmetaClassInfo中mobj和handle。

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(&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 ,
        // 0,    0,   75,    2, 0x2e ,
        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 ,
       0,    0,   75,    2, 0x2e ,

分别对应构造函数的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信息,初始了QmetaMethod中mobj和handle。首先我们看下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(&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"

    },
    "testsgn1sgn2slt1slt2slt3slt4"
    "t1t2namemarkparenta"
};

即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(&mo->d.stringdata[index]) };
    return data;
}

struct QByteArrayDataPtr
{
    QByteArrayData *ptr;
};

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

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

typedef QTypedArrayData Data;
inline QByteArray(QByteArrayDataPtr dd)
        : d(static_cast(dd.ptr))
{
}

template 
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(this) + offset;
    }
...
}

typedef QTypedArrayData Data;
inline QByteArray(QByteArrayDataPtr dd)
        : d(static_cast(dd.ptr))
{
}

template 
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 ,
    // 0,    0,   75,    2, 0x2e 
    // 即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 ,
    // 对应值为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
2.4 QmetaMethod::typeName 获取函数返回名称
const char *QmetaMethod::typeName() const
{
    if (!mobj)
        return 0;
    return QmetaMethodPrivate::get(this)->rawReturnTypeName();
}

const char *QmetaMethodPrivate::rawReturnTypeName() const
{
    Q_ASSERT(priv(mobj->d.data)->revision >= 7);
    // constructors: parameters
    // 0x80000000 | 2, QmetaType::QObjectStar,   12,
    // 0x80000000 | 2,
    // typesDataIndex() 为72,即typeInfo值为 0x80000000 | 2
    // IsUnresolvedType值为0x80000000,所以rawStringData指向第二个字符串
    uint typeInfo = mobj->d.data[typesDataIndex()];
    if (typeInfo & IsUnresolvedType)
        return rawStringData(mobj, typeInfo & TypeNameIndexMask);
    else
    // methods: parameters
    // QmetaType::Void,
    // QmetaType::Int, QmetaType::QString, QmetaType::QString,   10,   11,
    // 即直接通过QmetaType::QString获取相应的名字类型
        return QmetaType::typeName(typeInfo);
}

int QmetaMethodPrivate::typesDataIndex() const
{
    Q_ASSERT(priv(mobj->d.data)->revision >= 7);
    // constructors: name, argc, parameters, tag, flags
    // 0,    1,   72,    2, 0x0e ,
    // 对应值为72
    return mobj->d.data[handle + 2];
}

从上面分析可知,其获取流程大致是先通过QmetaObjectPrivate找到构造函数信息,然后依据其信息获取构造函数参数信息,然后根据参数信息获取名称。

2.5 QmetaMethod::parameterNames
QList QmetaMethod::parameterNames() const
{
    if (!mobj)
        return QList();
    return QmetaMethodPrivate::get(this)->parameterNames();
}

QList QmetaMethodPrivate::parameterNames() const
{
    // constructors: name, argc, parameters, tag, flags
    //    0,    1,   72,    2, 0x0e ,
    //    0,    0,   75,    2, 0x2e ,
    // 获取参数个数,即argc
    int argc = parameterCount();
    QList list;
    list.reserve(argc);
    // constructors: parameters
    // 0x80000000 | 2, QmetaType::QObjectStar,   12,
    // 0x80000000 | 2,
    // parametersDataIndex 获取参数信息所在的位置,即指向QmetaType::QObjectStar后的12
    // 多个参数依次叠加在一起
    int namesIndex = parametersDataIndex() + argc;
    for (int i = 0; i < argc; ++i)
        list += stringData(mobj, mobj->d.data[namesIndex + i]);
    return list;
}

int QmetaMethodPrivate::parametersDataIndex() const
{
    Q_ASSERT(priv(mobj->d.data)->revision >= 7);
    return typesDataIndex() + 1;
}

int QmetaMethodPrivate::typesDataIndex() const
{
    Q_ASSERT(priv(mobj->d.data)->revision >= 7);
    return mobj->d.data[handle + 2];
}

从上面分析可知,其获取流程大致是先通过QmetaObjectPrivate找到构造函数信息,然后依据其信息获取构造函数参数信息,然后根据参数信息依次获取输入参数信息。

2.6 QmetaMethod::invoke 调用函数
bool QmetaMethod::invoke(QObject *object,
                         Qt::ConnectionType connectionType,
                         QGenericReturnArgument returnValue,
                         QGenericArgument val0,
                         QGenericArgument val1,
                         QGenericArgument val2,
                         QGenericArgument val3,
                         QGenericArgument val4,
                         QGenericArgument val5,
                         QGenericArgument val6,
                         QGenericArgument val7,
                         QGenericArgument val8,
                         QGenericArgument val9) const
{
    if (!object || !mobj)
        return false;

    Q_ASSERT(mobj->cast(object));

    // 检查返回类型是否匹配
    if (returnValue.data()) {
        // 获取返回值类型名
        // 与传入的返回值类型名进行比较,不相等则失败返回
        const char *retType = typeName();
        if (qstrcmp(returnValue.name(), retType) != 0) {
            // normalize the return value as well
            QByteArray normalized = QmetaObject::normalizedType(returnValue.name());
            if (qstrcmp(normalized.constData(), retType) != 0) {
                // String comparison failed, try compare the metatype.
                int t = returnType();
                if (t == QmetaType::UnknownType || t != QmetaType::type(normalized))
                    return false;
            }
        }
    }

    // 检查参数个数是否匹配,不匹配返回false
    const char *typeNames[] = {
        returnValue.name(),
        val0.name(),
        val1.name(),
        val2.name(),
        val3.name(),
        val4.name(),
        val5.name(),
        val6.name(),
        val7.name(),
        val8.name(),
        val9.name()
    };
    int paramCount;
    for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
        if (qstrlen(typeNames[paramCount]) <= 0)
            break;
    }
    if (paramCount <= QmetaMethodPrivate::get(this)->parameterCount())
        return false;

    // 检查连接类型,自动连接则依据是否在同一个线程进行修正
    QThread *currentThread = QThread::currentThread();
    QThread *objectThread = object->thread();
    if (connectionType == Qt::AutoConnection) {
        connectionType = currentThread == objectThread
                         ? Qt::DirectConnection
                         : Qt::QueuedConnection;
    }

#if !QT_ConFIG(thread)
    if (connectionType == Qt::BlockingQueuedConnection) {
        connectionType = Qt::DirectConnection;
    }
#endif

    // 调用函数
    void *param[] = {
        returnValue.data(),
        val0.data(),
        val1.data(),
        val2.data(),
        val3.data(),
        val4.data(),
        val5.data(),
        val6.data(),
        val7.data(),
        val8.data(),
        val9.data()
    };
    int idx_relative = QmetaMethodPrivate::get(this)->ownMethodIndex();
    int idx_offset =  mobj->methodOffset();
    Q_ASSERT(QmetaObjectPrivate::get(mobj)->revision >= 6);
    // callFunction为static_metacall,即由moc编译器生成的函数
    QObjectPrivate::StaticmetaCallFunction callFunction = mobj->d.static_metacall;

    if (connectionType == Qt::DirectConnection) {
        if (callFunction) {
            callFunction(object, QmetaObject::InvokemetaMethod, idx_relative, param);
            return true;
        } else {
            return QmetaObject::metacall(object, QmetaObject::InvokemetaMethod, idx_relative + idx_offset, param) < 0;
        }
    } else if (connectionType == Qt::QueuedConnection) {
        if (returnValue.data()) {
            // 队列调用不支持返回值
            qWarning("QmetaMethod::invoke: Unable to invoke methods with return values in "
                     "queued connections");
            return false;
        }

        int nargs = 1; // include return type
        void **args = (void **) malloc(paramCount * sizeof(void *));
        Q_CHECK_PTR(args);
        int *types = (int *) malloc(paramCount * sizeof(int));
        Q_CHECK_PTR(types);
        types[0] = 0; // return type
        args[0] = 0;

        for (int i = 1; i < paramCount; ++i) {
            types[i] = QmetaType::type(typeNames[i]);
            if (types[i] == QmetaType::UnknownType && param[i]) {
                // Try to register the type and try again before reporting an error.
                int index = nargs - 1;
                void *argv[] = { &types[i], &index };
                QmetaObject::metacall(object, QmetaObject::RegisterMethodArgumentmetaType,
                                      idx_relative + idx_offset, argv);
                if (types[i] == -1) {
                    qWarning("QmetaMethod::invoke: Unable to handle unregistered datatype '%s'",
                            typeNames[i]);
                    for (int x = 1; x < i; ++x) {
                        if (types[x] && args[x])
                            QmetaType::destroy(types[x], args[x]);
                    }
                    free(types);
                    free(args);
                    return false;
                }
            }
            if (types[i] != QmetaType::UnknownType) {
                args[i] = QmetaType::create(types[i], param[i]);
                ++nargs;
            }
        }

        QCoreApplication::postEvent(object, new QmetaCallEvent(idx_offset, idx_relative, callFunction,
                                                        0, -1, nargs, types, args));
    } else { // blocking queued connection
#if QT_ConFIG(thread)
        if (currentThread == objectThread) {
            qWarning("QmetaMethod::invoke: Dead lock detected in "
                        "BlockingQueuedConnection: Receiver is %s(%p)",
                        mobj->className(), object);
        }

        QSemaphore semaphore;
        QCoreApplication::postEvent(object, new QmetaCallEvent(idx_offset, idx_relative, callFunction,
                                                        0, -1, 0, 0, param, &semaphore));
        semaphore.acquire();
#endif // QT_ConFIG(thread)
    }
    return true;
}

从上面分析可知,其获取流程大致是调用前检查参数与返回值,修正连接模式。如果是

(1)直连则直接通过static_metacall调用

(2)队列模式,则创建参数,然后通过postEvent放入队列,后期调用static_metacall。

QCoreApplication::postEvent(object, new QmetaCallEvent(idx_offset, idx_relative, callFunction,0, -1, nargs, types, args));

(3)阻塞队列模式

五、通过QmetaObject::indexOfConstructor
int QmetaObject::indexOfConstructor(const char *constructor) const
{
    Q_ASSERT(priv(d.data)->revision >= 7);
    QArgumentTypeArray types;
    QByteArray name = QmetaObjectPrivate::decodeMethodSignature(constructor, types);
    return QmetaObjectPrivate::indexOfConstructor(this, name, types.size(), types.constData());
}

// 给定一个方法的签名(如: "foo(int,double)"), 该函数返回参数类型组(QArgumentTypeArray& types)和方法名
QByteArray QmetaObjectPrivate::decodeMethodSignature(
        const char *signature, QArgumentTypeArray &types)
{
    Q_ASSERT(signature != 0);
    const char *lparens = strchr(signature, '(');
    if (!lparens)
        return QByteArray();
    const char *rparens = strrchr(lparens + 1, ')');
    if (!rparens || *(rparens+1))
        return QByteArray();
    int nameLength = lparens - signature;
    argumentTypesFromString(lparens + 1, rparens, types);
    return QByteArray::fromRawData(signature, nameLength);
}

int QmetaObjectPrivate::indexOfConstructor(const QmetaObject *m, const QByteArray &name,
                                           int argc, const QArgumentType *types)
{
    // 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
    // constructorCount为2,constructorData为79
    for (int i = priv(m->d.data)->constructorCount-1; i >= 0; --i) {
        int handle = priv(m->d.data)->constructorData + 5*i;
        if (methodMatch(m, handle, name, argc, types))
            return i;
    }
    return -1;
}

// Returns c true if the method defined by the given meta-object&handle
// matches the given name, argument count and argument types, otherwise
// returns c false.
static bool methodMatch(const QmetaObject *m, int handle,
                        const QByteArray &name, int argc,
                        const QArgumentType *types)
{
    Q_ASSERT(priv(m->d.data)->revision >= 7);
    // constructors: name, argc, parameters, tag, flags
    // 0,    1,   72,    2, 0x0e ,
    // 0,    0,   75,    2, 0x2e ,
    // 比较参数个数
    if (int(m->d.data[handle + 1]) != argc)
        return false;
    // 比较方法名
    if (stringData(m, m->d.data[handle]) != name)
        return false;

    // m->d.data[handle + 2]指向下面数据
    // constructors: parameters
    // 0x80000000 | 2, QmetaType::QObjectStar,   12,
    // 0x80000000 | 2,
    // 依次比较参数类型,或者参数名
    int paramsIndex = m->d.data[handle + 2] + 1;
    for (int i = 0; i < argc; ++i) {
        uint typeInfo = m->d.data[paramsIndex + i];
        if (types[i].type()) {
            if (types[i].type() != typeFromTypeInfo(m, typeInfo))
                return false;
        } else {
            if (types[i].name() != typeNameFromTypeInfo(m, typeInfo))
                return false;
        }
    }

    return true;
}

从上面分析可知,其流程大致是先把字符串签名,解析成qt内部的参数和方法名,然后依次与保存的方法信息以及参数信息进行比较。都匹配则返回相应的构造函数方法序号。

六、qt_static_metacall

待续

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4751614.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-08
下一篇 2022-11-08

发表评论

登录后才能评论

评论列表(0条)

保存