本文主要是介绍Qt源码分析:QMetaObject实现原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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,
namespace Gui
{// Forward declarationsclass Workbench;class MainWindow;class YQ_GUI_EXPORT WorkbenchManager : public QObject{Q_OBJECTQ_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处理之后,可以得到如下的代码,
/****************************************************************************
** 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.)"
#endifQT_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_LITERALstatic const uint qt_meta_data_Gui__WorkbenchManager[] = {// content:8, // revision0, // classname0, 0, // classinfo3, 14, // methods1, 40, // properties0, 0, // enums/sets0, 0, // constructors0, // flags1, // signalCount// signals: name, argc, parameters, tag, flags1, 1, 29, 2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags5, 1, 32, 2, 0x0a /* Public */,6, 2, 35, 2, 0x0a /* Public */,// signals: parametersQMetaType::Void, 0x80000000 | 3, 4,// slots: parametersQMetaType::Void, 0x80000000 | 3, 4,QMetaType::Void, 0x80000000 | 3, 0x80000000 | 3, 7, 8,// properties: name, type, flags9, 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_PROPERTIESelse 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_PROPERTIESelse 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_PROPERTIESreturn _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对象。
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)
struct { // private dataSuperData 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 headervoid *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;}// ... ...
}
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),扩展之后为
{ { { -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)
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)
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 QMetaEnumenum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbusint 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,
static const uint qt_meta_data_Gui__WorkbenchManager[] = {// content:8, // revision0, // classname0, 0, // classinfo3, 14, // methods1, 40, // properties0, 0, // enums/sets0, 0, // constructors0, // flags1, // signalCount// signals: name, argc, parameters, tag, flags1, 1, 29, 2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags5, 1, 32, 2, 0x0a /* Public */,6, 2, 35, 2, 0x0a /* Public */,// signals: parametersQMetaType::Void, 0x80000000 | 3, 4,// slots: parametersQMetaType::Void, 0x80000000 | 3, 4,QMetaType::Void, 0x80000000 | 3, 0x80000000 | 3, 7, 8,// properties: name, type, flags9, 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 索引方法(信号/槽)
知道了类型信息的索引及类型信息原始数据,就可以完成对信号、槽等方法的查找。比如,对于信号查询,有
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正是第一个方法的索引。
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_DEBUGconst 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));}}#endifreturn 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 索引属性
同样的,对于属性信息,有
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正是通过该回调函数完成了对槽函数的调用。
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 parameterselsesig[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);
}
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 typeif (returnValue.data()) {const char *retType = typeName();if (qstrcmp(returnValue.name(), retType) != 0) {// normalize the return value as wellQByteArray 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 typeif (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的。
网络资料
The Meta-Object System
QMetaObject
这篇关于Qt源码分析:QMetaObject实现原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!