#ifndef TEST_H #define TEST_H #includetest.cppclass 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
#include "test.h" #includetest::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二、QmetaObject#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 类描述了 QObject 及其派生类对象的所有元信息,该类是 Qt 元对象系
统的核心类,通过该类的成员函数可以获取 QObject 及其派生类对象的所有元信息,
因此可以说 QmetaObject 类的对象是 Qt 中的元对象。
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_teststruct 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_teststatic 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四、通过QmetaObject获取constructor 1、constructor函数(&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(); }
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 QTypedArrayDataData; inline QByteArray(QByteArrayDataPtr dd) : d(static_cast(dd.ptr)) { } template struct QTypedArraydata: QArrayData { ... }
其调用此构造函数转换成QByteArray,从代码中可知其把dd.ptr转换成了Data结构体,而Data是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_cast2.2 QmetaMethod::access()(this) + offset; } ... } typedef QTypedArrayData Data; inline QByteArray(QByteArrayDataPtr dd) : d(static_cast(dd.ptr)) { } template struct QTypedArraydata: QArrayData { ... }
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_TAG2.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::parameterNamesQListQmetaMethod::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::indexOfConstructorint 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待续
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)