Qt源码分析:QMetaObject实现原理

2024-03-26 00:52

本文主要是介绍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实现原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/846817

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景