#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" }, "test sgn1 sgn2 slt1 slt2 slt3 slt4 " "t1 t2 name mark parent a" }; #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"
},
"test sgn1 sgn2 slt1 slt2 slt3 slt4 "
"t1 t2 name mark parent a"
};
由上面可知,其保存了元对象的类名、方法名、信号名、槽名、属性名以及各个参数的名字,这些名字主要用来通过名字反射相应的类、方法、信号、槽以及属性等。
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(&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"
},
"test sgn1 sgn2 slt1 slt2 slt3 slt4 "
"t1 t2 name mark parent a"
};
即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_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::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待续



