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,

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的。

网络资料

The Meta-Object System

QMetaObject

相关推荐
锦亦之22337 小时前
QT+OSG+OSG-earth如何在窗口显示一个地球
开发语言·qt
柳鲲鹏10 小时前
编译成功!QT/6.7.2/Creator编译Windows64 MySQL驱动(MinGW版)
开发语言·qt·mysql
三玖诶10 小时前
如何在 Qt 的 QListWidget 中逐行添加和显示数据
开发语言·qt
阳光开朗_大男孩儿16 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
Alphapeople17 小时前
Qt Modbus
开发语言·qt
竹林海中敲代码18 小时前
Qt Creator 集成开发环境 常见问题
qt·qt工具常见问题
竹林海中敲代码21 小时前
Qt安卓开发连接手机调试(红米K60为例)
android·qt·智能手机
长沙红胖子Qt1 天前
关于 Qt运行加载内存较大崩溃添加扩大运行内存 的解决方法
开发语言·qt·qt扩大运行内存
gopher95111 天前
qt相关面试题
开发语言·qt·面试
三玖诶1 天前
在 Qt 中使用 QLabel 设置 GIF 动态背景
开发语言·qt·命令模式