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 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处理之后,可以得到如下的代码,

/****************************************************************************
** 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对象。

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 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;
    }

    // ... ...
}
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 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, 

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 索引方法(信号/槽)

知道了类型信息的索引及类型信息原始数据,就可以完成对信号、槽等方法的查找。比如,对于信号查询,有

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_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 索引属性

同样的,对于属性信息,有

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 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);
}
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 

相关推荐

  1. Qt分析QMetaObject实现原理

    2024-03-26 14:38:04       19 阅读
  2. Qt QMetaObject::invokeMethod

    2024-03-26 14:38:04       20 阅读
  3. Django实现热加载原理(从开始分析

    2024-03-26 14:38:04       34 阅读
  4. UGUI分析与研究1-UGUI底层的实现原理

    2024-03-26 14:38:04       17 阅读
  5. Qt C++ 实现文件监视

    2024-03-26 14:38:04       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-26 14:38:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-26 14:38:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-26 14:38:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-26 14:38:04       18 阅读

热门阅读

  1. MongoDB 的索引有哪些 nestjs mongoose示例

    2024-03-26 14:38:04       15 阅读
  2. 在线数据格式工具

    2024-03-26 14:38:04       14 阅读
  3. 论文阅读--Offline RL Without Off-Policy Evaluation

    2024-03-26 14:38:04       14 阅读
  4. vue/js总结合集

    2024-03-26 14:38:04       16 阅读
  5. 面试算法-117-组合总和 III

    2024-03-26 14:38:04       13 阅读
  6. 缓存Caffine

    2024-03-26 14:38:04       19 阅读
  7. django关于文件分块上传的简单实现(template+view)

    2024-03-26 14:38:04       18 阅读
  8. Promise封装ajax

    2024-03-26 14:38:04       16 阅读