Qt基于QMetaObject实现了信号/槽机制、属性机制等多个功能特性,而QMetaObject实际上是实现了一种反射机制。
Ref. from Reflection in Java
The term "RTTI" is a C++-specific term referring to the functionality of the core language that allows the program to determine the dynamic types of various objects at runtime.
It usually refers to the dynamic_cast or typeid operators, along with the associated std::type_info object produced by typeid.
The term reflection, on the other hand, is a generic term used across programming languages to refer to the ability of a program to inspect and modify its objects, types, etc. at runtime.
本文拟对QMetaObject实现原理予以分析。
一、研究素材:一个小例子
为了便于说明问题,考虑派生于QObject的Gui::WorkbenchManager,
cpp
namespace Gui
{
// Forward declarations
class Workbench;
class MainWindow;
class YQ_GUI_EXPORT WorkbenchManager : public QObject
{
Q_OBJECT
Q_PROPERTY(Gui::Workbench* myActiveWorkbench READ getActiveWorkbench WRITE setActiveWorkbench)
public:
WorkbenchManager(MainWindow* p);
virtual ~WorkbenchManager();
/** Activate the workbench with name \a name. */
bool activate(const QString& className, const QString& objectName);
bool activate(const QString& name);
public slots:
void onWorkbenchActivated(Gui::Workbench* wb);
void onWorkbenchDeactivated(Gui::Workbench* previous, Gui::Workbench* current);
signals:
void activated(Gui::Workbench *wb);
private:
MainWindow* myMainWnd;
Workbench* myActiveWorkbench;
QMap<QString, Workbench*> myWorkbenches;
};
}
可以看到,Gui::WorkbenchManager具有如下的类型信息,
|----|-----------------------------------------------------------------------------------------------------------------------------------|
| 属性 | Workbench* myActiveWorkbench |
| 信号 | void activated(Gui::Workbench *wb); |
| 槽 | void onWorkbenchActivated(Gui::Workbench* wb); void onWorkbenchDeactivated(Gui::Workbench* previous, Gui::Workbench* current); |
经过Qt moc处理之后,可以得到如下的代码,
cpp
/****************************************************************************
** Meta object code from reading C++ file 'WorkbenchManager.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.14.0)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include <memory>
#include "../../../Acise/src/Gui/WorkbenchManager.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'WorkbenchManager.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.14.0. 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_Gui__WorkbenchManager_t {
QByteArrayData data[10];
char stringdata0[131];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Gui__WorkbenchManager_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_Gui__WorkbenchManager_t qt_meta_stringdata_Gui__WorkbenchManager = {
{
QT_MOC_LITERAL(0, 0, 21), // "Gui::WorkbenchManager"
QT_MOC_LITERAL(1, 22, 9), // "activated"
QT_MOC_LITERAL(2, 32, 0), // ""
QT_MOC_LITERAL(3, 33, 15), // "Gui::Workbench*"
QT_MOC_LITERAL(4, 49, 2), // "wb"
QT_MOC_LITERAL(5, 52, 20), // "onWorkbenchActivated"
QT_MOC_LITERAL(6, 73, 22), // "onWorkbenchDeactivated"
QT_MOC_LITERAL(7, 96, 8), // "previous"
QT_MOC_LITERAL(8, 105, 7), // "current"
QT_MOC_LITERAL(9, 113, 17) // "myActiveWorkbench"
},
"Gui::WorkbenchManager\0activated\0\0"
"Gui::Workbench*\0wb\0onWorkbenchActivated\0"
"onWorkbenchDeactivated\0previous\0current\0"
"myActiveWorkbench"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Gui__WorkbenchManager[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
3, 14, // methods
1, 40, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 1, 29, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
5, 1, 32, 2, 0x0a /* Public */,
6, 2, 35, 2, 0x0a /* Public */,
// signals: parameters
QMetaType::Void, 0x80000000 | 3, 4,
// slots: parameters
QMetaType::Void, 0x80000000 | 3, 4,
QMetaType::Void, 0x80000000 | 3, 0x80000000 | 3, 7, 8,
// properties: name, type, flags
9, 0x80000000 | 3, 0x0009500b,
0 // eod
};
void Gui::WorkbenchManager::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<WorkbenchManager *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->activated((*reinterpret_cast< Gui::Workbench*(*)>(_a[1]))); break;
case 1: _t->onWorkbenchActivated((*reinterpret_cast< Gui::Workbench*(*)>(_a[1]))); break;
case 2: _t->onWorkbenchDeactivated((*reinterpret_cast< Gui::Workbench*(*)>(_a[1])),(*reinterpret_cast< Gui::Workbench*(*)>(_a[2]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (WorkbenchManager::*)(Gui::Workbench * );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WorkbenchManager::activated)) {
*result = 0;
return;
}
}
}
#ifndef QT_NO_PROPERTIES
else if (_c == QMetaObject::ReadProperty) {
auto *_t = static_cast<WorkbenchManager *>(_o);
Q_UNUSED(_t)
void *_v = _a[0];
switch (_id) {
case 0: *reinterpret_cast< Gui::Workbench**>(_v) = _t->getActiveWorkbench(); break;
default: break;
}
} else if (_c == QMetaObject::WriteProperty) {
auto *_t = static_cast<WorkbenchManager *>(_o);
Q_UNUSED(_t)
void *_v = _a[0];
switch (_id) {
case 0: _t->setActiveWorkbench(*reinterpret_cast< Gui::Workbench**>(_v)); break;
default: break;
}
} else if (_c == QMetaObject::ResetProperty) {
}
#endif // QT_NO_PROPERTIES
}
QT_INIT_METAOBJECT const QMetaObject Gui::WorkbenchManager::staticMetaObject = { {
QMetaObject::SuperData::link<QObject::staticMetaObject>(),
qt_meta_stringdata_Gui__WorkbenchManager.data,
qt_meta_data_Gui__WorkbenchManager,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *Gui::WorkbenchManager::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *Gui::WorkbenchManager::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_Gui__WorkbenchManager.stringdata0))
return static_cast<void*>(this);
return QObject::qt_metacast(_clname);
}
int Gui::WorkbenchManager::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 < 3)
qt_static_metacall(this, _c, _id, _a);
_id -= 3;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 3)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 3;
}
#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 Gui::WorkbenchManager::activated(Gui::Workbench * _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE
二、QMetaObject实现原理
当派生于QObject的类头文件中包含Q_OBJECT时,moc程序会自动为该类生成静态QMetaObject对象。
cpp
QT_INIT_METAOBJECT const QMetaObject Gui::WorkbenchManager::staticMetaObject = { {
QMetaObject::SuperData::link<QObject::staticMetaObject>(),
qt_meta_stringdata_Gui__WorkbenchManager.data,
qt_meta_data_Gui__WorkbenchManager,
qt_static_metacall,
nullptr,
nullptr
} };
可以看到,QMetaObject主要由qt_meta_stringdata_Gui__WorkbenchManager.data、qt_meta_data_Gui__WorkbenchManager、qt_static_metacall等组成,这实际上对应QMetaObject内的匿名结构体,(参见qtbase/src/corelib/kernel/qobjectdefs.h)
cpp
struct { // private data
SuperData superdata;
const QByteArrayData *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const SuperData *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
Qt正是使用这个匿名结构体实现了Reflection机制。
2.1 元数据串行化
要实现Reflection,首先就需要将类型元数据以某种方式进行存储,其实这就是QByteArrayData *stringdata来完成。
QByteArrayData实际上就是结构体QArrayData,这个数据结构设计的比较巧妙,主要用于访问连续内存中的部分连续内存块,这一点可以通过QArrayData::data()函数看出。(参见qtbase/src/corelib/tools/qarraydata.h)
struct Q_CORE_EXPORT QArrayData
{
QtPrivate::RefCount ref;
int size;
uint alloc : 31;
uint capacityReserved : 1;
qptrdiff offset; // in bytes from beginning of header
void *data()
{
Q_ASSERT(size == 0
|| offset < 0 || size_t(offset) >= sizeof(QArrayData));
return reinterpret_cast<char *>(this) + offset;
}
const void *data() const
{
Q_ASSERT(size == 0
|| offset < 0 || size_t(offset) >= sizeof(QArrayData));
return reinterpret_cast<const char *>(this) + offset;
}
// ... ...
}
cpp
struct qt_meta_stringdata_Gui__WorkbenchManager_t {
QByteArrayData data[10];
char stringdata0[131];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Gui__WorkbenchManager_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_Gui__WorkbenchManager_t qt_meta_stringdata_Gui__WorkbenchManager = {
{
QT_MOC_LITERAL(0, 0, 21), // "Gui::WorkbenchManager"
QT_MOC_LITERAL(1, 22, 9), // "activated"
QT_MOC_LITERAL(2, 32, 0), // ""
QT_MOC_LITERAL(3, 33, 15), // "Gui::Workbench*"
QT_MOC_LITERAL(4, 49, 2), // "wb"
QT_MOC_LITERAL(5, 52, 20), // "onWorkbenchActivated"
QT_MOC_LITERAL(6, 73, 22), // "onWorkbenchDeactivated"
QT_MOC_LITERAL(7, 96, 8), // "previous"
QT_MOC_LITERAL(8, 105, 7), // "current"
QT_MOC_LITERAL(9, 113, 17) // "myActiveWorkbench"
},
"Gui::WorkbenchManager\0activated\0\0"
"Gui::Workbench*\0wb\0onWorkbenchActivated\0"
"onWorkbenchDeactivated\0previous\0current\0"
"myActiveWorkbench"
};
对于QT_MOC_LITERAL(1, 22, 9),扩展之后为
cpp
{ { { -1 } }, 9, 0, 0, qptrdiff(((::size_t)& reinterpret_cast<char const volatile&>((((qt_meta_stringdata_Gui__WorkbenchManager_t*)0)->stringdata0))) + 22 - 1 * sizeof(QByteArrayData)) }
也就是说,QT_MOC_LITERAL(1, 22, 9)对应的QByteArrayData实际上是用于访问"activated\0"。
正是通过这种方式,Qt实现了将类型元数据串行化。这个也可以通过stringData函数看出。(参见qtbase/src/corelib/kernel/qmetaobject.cpp)
cpp
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;
}
2.2 元数据的反串行化:元数据索引
那么,每个QArrayData表示什么语义呢?也就是说每个QArrayData用于描述什么信息呢?这个实际上是通过qt_meta_data_Gui__WorkbenchManager来描述的,qt_meta_data_Gui__WorkbenchManager实际上是用于生成QMetaObjectPrivate类型的结构体对象(参见qtbase/src/corelib/kernel/qmetaobject_p.h)
cpp
struct QMetaObjectPrivate
{
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
int signalCount;
// ... ...
static int indexOfSignalRelative(const QMetaObject **baseObject,
const QByteArray &name, int argc,
const QArgumentType *types);
static int indexOfSlotRelative(const QMetaObject **m,
const QByteArray &name, int argc,
const QArgumentType *types);
static int indexOfSignal(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfSlot(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfMethod(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfConstructor(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
// ... ...
}
对于Gui::WorkbenchManager,
cpp
static const uint qt_meta_data_Gui__WorkbenchManager[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
3, 14, // methods
1, 40, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 1, 29, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
5, 1, 32, 2, 0x0a /* Public */,
6, 2, 35, 2, 0x0a /* Public */,
// signals: parameters
QMetaType::Void, 0x80000000 | 3, 4,
// slots: parameters
QMetaType::Void, 0x80000000 | 3, 4,
QMetaType::Void, 0x80000000 | 3, 0x80000000 | 3, 7, 8,
// properties: name, type, flags
9, 0x80000000 | 3, 0x0009500b,
0 // eod
};
可以看到,content部分数据用于实例化QMetaObjectPrivate,同时有如下语义,
|----------------------|---------------------------------------------------------|
| 3, 14, // methods | 有3个方法,第一个方法索引在qt_meta_data_Gui__WorkbenchManager[14]处 |
| 1, 40, // properties | 有一个属性,第一个属性索引在qt_meta_data_Gui__WorkbenchManager[40]处 |
| 1, // signalCount | 有一个信号, |
需要说明的是,在Qt中,方法包括信号、槽等,而且在存储索引的时候,是先存储信号索引,再存储槽索引。因此,知道了方法的个数与索引位置,根据信号的数量是可以推断出槽的索引位置的。(每个方法占用5个字段)。
2.2.1 索引方法(信号/槽)
知道了类型信息的索引及类型信息原始数据,就可以完成对信号、槽等方法的查找。比如,对于信号查询,有
cpp
int QMetaObject::indexOfSignal(const char *signal) const
{
const QMetaObject *m = this;
int i;
Q_ASSERT(priv(m->d.data)->revision >= 7);
QArgumentTypeArray types;
QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signal, types);
i = QMetaObjectPrivate::indexOfSignalRelative(&m, name, types.size(), types.constData());
if (i >= 0)
i += m->methodOffset();
return i;
}
实际上,上述信号查询正是基于methodCount、methodData、signalCount等完成的,methodData正是第一个方法的索引。
cpp
int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject,
const QByteArray &name, int argc,
const QArgumentType *types)
{
int i = indexOfMethodRelative<MethodSignal>(baseObject, name, argc, types);
#ifndef QT_NO_DEBUG
const QMetaObject *m = *baseObject;
if (i >= 0 && m && m->d.superdata) {
int conflict = indexOfMethod(m->d.superdata, name, argc, types);
if (conflict >= 0) {
QMetaMethod conflictMethod = m->d.superdata->method(conflict);
qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s",
conflictMethod.methodSignature().constData(),
objectClassName(m->d.superdata), objectClassName(m));
}
}
#endif
return i;
}
template<int MethodType>
static inline int indexOfMethodRelative(const QMetaObject **baseObject,
const QByteArray &name, int argc,
const QArgumentType *types)
{
for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) {
Q_ASSERT(priv(m->d.data)->revision >= 7);
int i = (MethodType == MethodSignal)
? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1);
const int end = (MethodType == MethodSlot)
? (priv(m->d.data)->signalCount) : 0;
for (; i >= end; --i) {
int handle = priv(m->d.data)->methodData + 5*i;
if (methodMatch(m, handle, name, argc, types)) {
*baseObject = m;
return i;
}
}
}
return -1;
}
2.2.2 索引属性
同样的,对于属性信息,有
cpp
int QMetaObject::indexOfProperty(const char *name) const
{
const QMetaObject *m = this;
while (m) {
const QMetaObjectPrivate *d = priv(m->d.data);
for (int i = d->propertyCount-1; i >= 0; --i) {
const char *prop = rawStringData(m, m->d.data[d->propertyData + 3*i]);
if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
i += m->propertyOffset();
return i;
}
}
m = m->d.superdata;
}
Q_ASSERT(priv(this->d.data)->revision >= 3);
if (priv(this->d.data)->flags & DynamicMetaObject) {
QAbstractDynamicMetaObject *me =
const_cast<QAbstractDynamicMetaObject *>(static_cast<const QAbstractDynamicMetaObject *>(this));
return me->createProperty(name, 0);
}
return -1;
}
可以看到,属性信息查询是通过propertyCount、propertyData信息进行的,propertyData正是第一个属性的索引。
2.3 函数回调
在生成QMetaObject时,已经将Gui::WorkbenchManager::qt_static_metacall的函数地址存入到QMetaObject::d.static_metacall,Qt正是通过该回调函数完成了对槽函数的调用。
cpp
bool QMetaObject::invokeMethod(QObject *obj,
const char *member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9)
{
if (!obj)
return false;
QVarLengthArray<char, 512> sig;
int len = qstrlen(member);
if (len <= 0)
return false;
sig.append(member, len);
sig.append('(');
const char *typeNames[] = {ret.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) {
len = qstrlen(typeNames[paramCount]);
if (len <= 0)
break;
sig.append(typeNames[paramCount], len);
sig.append(',');
}
if (paramCount == 1)
sig.append(')'); // no parameters
else
sig[sig.size() - 1] = ')';
sig.append('\0');
const QMetaObject *meta = obj->metaObject();
int idx = meta->indexOfMethod(sig.constData());
if (idx < 0) {
QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
idx = meta->indexOfMethod(norm.constData());
}
if (idx < 0 || idx >= meta->methodCount()) {
// This method doesn't belong to us; print out a nice warning with candidates.
qWarning("QMetaObject::invokeMethod: No such method %s::%s%s",
meta->className(), sig.constData(), findMethodCandidates(meta, member).constData());
return false;
}
QMetaMethod method = meta->method(idx);
return method.invoke(obj, type, ret,
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
cpp
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));
// check return type
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;
}
}
}
// check argument count (we don't allow invoking a method if given too few arguments)
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;
// check connection type
if (connectionType == Qt::AutoConnection) {
QThread *currentThread = QThread::currentThread();
QThread *objectThread = object->thread();
connectionType = currentThread == objectThread
? Qt::DirectConnection
: Qt::QueuedConnection;
}
#if !QT_CONFIG(thread)
if (connectionType == Qt::BlockingQueuedConnection) {
connectionType = Qt::DirectConnection;
}
#endif
// invoke!
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);
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;
}
QScopedPointer<QMetaCallEvent> event(new QMetaCallEvent(idx_offset, idx_relative, callFunction, 0, -1, paramCount));
int *types = event->types();
void **args = event->args();
int argIndex = 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.
void *argv[] = { &types[i], &argIndex };
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]);
return false;
}
}
if (types[i] != QMetaType::UnknownType) {
args[i] = QMetaType::create(types[i], param[i]);
++argIndex;
}
}
QCoreApplication::postEvent(object, event.take());
} else { // blocking queued connection
#if QT_CONFIG(thread)
QThread *currentThread = QThread::currentThread();
QThread *objectThread = object->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, param, &semaphore));
semaphore.acquire();
#endif // QT_CONFIG(thread)
}
return true;
}
2.4 小结
由QMetaObject的实现原理,可以得到以下结论,
- QMetaObject实际上是实现了一种Reflection机制;
- 元数据串行化/反串行化、函数回调是Reflection机制实现的要点。
- Qt的反射机制是不依赖于编译器RTTI的。