Qt--QMetaObject

来源:互联网 发布:汽车销售网站模板 cms 编辑:程序博客网 时间:2024/05/16 06:06

QMetaObject接口分析

上一节中我们概述了Meta-Object Model的架构,它通过提供了一个QObject的基类,使用Q_OBJECT宏,这样moc编译器就会自动帮我们生成带有QMetaObject记录了元信息的源码。

这节我们看下QMetaObject提供了哪些接口去获取或使用这些元信息

上源代码:

struct Q_CORE_EXPORT QMetaObject{    class Connection;    const char *className() const;    const QMetaObject *superClass() const;    bool inherits(const QMetaObject *metaObject) const Q_DECL_NOEXCEPT;    QObject *cast(QObject *obj) const;    const QObject *cast(const QObject *obj) const;#ifndef QT_NO_TRANSLATION    QString tr(const char *s, const char *c, int n = -1) const;#endif // QT_NO_TRANSLATION    int methodOffset() const;    int enumeratorOffset() const;    int propertyOffset() const;    int classInfoOffset() const;    int constructorCount() const;    int methodCount() const;    int enumeratorCount() const;    int propertyCount() const;    int classInfoCount() const;    int indexOfConstructor(const char *constructor) const;    int indexOfMethod(const char *method) const;    int indexOfSignal(const char *signal) const;    int indexOfSlot(const char *slot) const;    int indexOfEnumerator(const char *name) const;    int indexOfProperty(const char *name) const;    int indexOfClassInfo(const char *name) const;    QMetaMethod constructor(int index) const;    QMetaMethod method(int index) const;    QMetaEnum enumerator(int index) const;    QMetaProperty property(int index) const;    QMetaClassInfo classInfo(int index) const;    QMetaProperty userProperty() const;    static bool checkConnectArgs(const char *signal, const char *method);    static bool checkConnectArgs(const QMetaMethod &signal,                                 const QMetaMethod &method);    static QByteArray normalizedSignature(const char *method);    static QByteArray normalizedType(const char *type);    // internal index-based connect    static Connection connect(const QObject *sender, int signal_index,                        const QObject *receiver, int method_index,                        int type = 0, int *types = Q_NULLPTR);    // internal index-based disconnect    static bool disconnect(const QObject *sender, int signal_index,                           const QObject *receiver, int method_index);    static bool disconnectOne(const QObject *sender, int signal_index,                              const QObject *receiver, int method_index);    // internal slot-name based connect    static void connectSlotsByName(QObject *o);    // internal index-based signal activation    static void activate(QObject *sender, int signal_index, void **argv);    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);    static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);    static bool invokeMethod(QObject *obj, const char *member,                             Qt::ConnectionType,                             QGenericReturnArgument ret,                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),                             QGenericArgument val1 = QGenericArgument(),                             QGenericArgument val2 = QGenericArgument(),                             QGenericArgument val3 = QGenericArgument(),                             QGenericArgument val4 = QGenericArgument(),                             QGenericArgument val5 = QGenericArgument(),                             QGenericArgument val6 = QGenericArgument(),                             QGenericArgument val7 = QGenericArgument(),                             QGenericArgument val8 = QGenericArgument(),                             QGenericArgument val9 = QGenericArgument());    static inline bool invokeMethod(QObject *obj, const char *member,                             QGenericReturnArgument ret,                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),                             QGenericArgument val1 = QGenericArgument(),                             QGenericArgument val2 = QGenericArgument(),                             QGenericArgument val3 = QGenericArgument(),                             QGenericArgument val4 = QGenericArgument(),                             QGenericArgument val5 = QGenericArgument(),                             QGenericArgument val6 = QGenericArgument(),                             QGenericArgument val7 = QGenericArgument(),                             QGenericArgument val8 = QGenericArgument(),                             QGenericArgument val9 = QGenericArgument())    {        return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,                val4, val5, val6, val7, val8, val9);    }    static inline bool invokeMethod(QObject *obj, const char *member,                             Qt::ConnectionType type,                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),                             QGenericArgument val1 = QGenericArgument(),                             QGenericArgument val2 = QGenericArgument(),                             QGenericArgument val3 = QGenericArgument(),                             QGenericArgument val4 = QGenericArgument(),                             QGenericArgument val5 = QGenericArgument(),                             QGenericArgument val6 = QGenericArgument(),                             QGenericArgument val7 = QGenericArgument(),                             QGenericArgument val8 = QGenericArgument(),                             QGenericArgument val9 = QGenericArgument())    {        return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,                                 val3, val4, val5, val6, val7, val8, val9);    }    static inline bool invokeMethod(QObject *obj, const char *member,                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),                             QGenericArgument val1 = QGenericArgument(),                             QGenericArgument val2 = QGenericArgument(),                             QGenericArgument val3 = QGenericArgument(),                             QGenericArgument val4 = QGenericArgument(),                             QGenericArgument val5 = QGenericArgument(),                             QGenericArgument val6 = QGenericArgument(),                             QGenericArgument val7 = QGenericArgument(),                             QGenericArgument val8 = QGenericArgument(),                             QGenericArgument val9 = QGenericArgument())    {        return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,                val1, val2, val3, val4, val5, val6, val7, val8, val9);    }    QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR),                         QGenericArgument val1 = QGenericArgument(),                         QGenericArgument val2 = QGenericArgument(),                         QGenericArgument val3 = QGenericArgument(),                         QGenericArgument val4 = QGenericArgument(),                         QGenericArgument val5 = QGenericArgument(),                         QGenericArgument val6 = QGenericArgument(),                         QGenericArgument val7 = QGenericArgument(),                         QGenericArgument val8 = QGenericArgument(),                         QGenericArgument val9 = QGenericArgument()) const;    enum Call {        InvokeMetaMethod,        ReadProperty,        WriteProperty,        ResetProperty,        QueryPropertyDesignable,        QueryPropertyScriptable,        QueryPropertyStored,        QueryPropertyEditable,        QueryPropertyUser,        CreateInstance,        IndexOfMethod,        RegisterPropertyMetaType,        RegisterMethodArgumentMetaType    };    int static_metacall(Call, int, void **) const;    static int metacall(QObject *, Call, int, void **);    struct { // private data        const QMetaObject *superdata;        const QByteArrayData *stringdata;        const uint *data;        typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);        StaticMetacallFunction static_metacall;        const QMetaObject * const *relatedMetaObjects;        void *extradata; //reserved for future use    } d;};

从接口命名就可以简单分析出可以获取类名、类信息、方法(细分为信号、槽、构造函数、一般函数)、属性、枚举等信息

枚举类型Call中实际给出了可以进行的操作,包括调用方法、读写属性、查询属性各种特性、创建实例、注册属性类型、注册方法参数类型

enum Call {   InvokeMetaMethod,   ReadProperty,   WriteProperty,   ResetProperty,   QueryPropertyDesignable,   QueryPropertyScriptable,   QueryPropertyStored,   QueryPropertyEditable,   QueryPropertyUser,   CreateInstance,   IndexOfMethod,   RegisterPropertyMetaType,   RegisterMethodArgumentMetaType};

还给出了activate(发射信号实际调用的函数),connect(连接信号与槽),disconnect(断连信号与槽)的接口

QMetaObject实现分析

获取超类QMetaObject 指针

inline const QMetaObject *QMetaObject::superClass() const{ return d.superdata; }

获取类名
先看一个转换函数

static inline const QMetaObjectPrivate *priv(const uint* data){ return reinterpret_cast<const QMetaObjectPrivate*>(data); }

这个函数将uint*指针转化为QMetaObjectPrivate ,也就是说QMetaObject中的d.data实际就是一个QMetaObjectPrivate 结构体指针

那么让我们看看QMetaObjectPrivate 这个结构体

struct QMetaObjectPrivate{    enum { OutputRevision = 7 }; // 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; //since revision 2    int flags; //since revision 3    int signalCount; //since revision 4     //...}

原来这个结构体就是对应我们上节的qt_meta_data_MyClass整型数组,里面记录了类、方法、属性等名称字符串的索引、参数的类型信息等

那么再来看获取类名的步骤

const char *QMetaObject::className() const{    return objectClassName(this);}static inline const char *objectClassName(const QMetaObject *m){    return rawStringData(m, priv(m->d.data)->className);}static inline const char *rawStringData(const QMetaObject *mo, int index){    return stringData(mo, index).data();}static inline const QByteArray stringData(const QMetaObject *mo, int index){    const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };    return data;}

实际就是通过d.data中记录的索引找到d.stringdata中的字符串,即mo->d.stringdata[index]

判断继承关系

bool QMetaObject::inherits(const QMetaObject *metaObject){    const QMetaObject *m = this;    do {        if (metaObject == m)            return true;    } while ((m = m->d.superdata));    return false;}

就是循环比较超类的QMetaObject是否等于这个metaObject

方法偏移

int QMetaObject::methodOffset() const{    int offset = 0;    const QMetaObject *m = d.superdata;    while (m) {        offset += priv(m->d.data)->methodCount;        m = m->d.superdata;    }    return offset;}

返回所有基类的方法数之和

方法总数

int QMetaObject::methodCount() const{    int n = priv(d.data)->methodCount;    const QMetaObject *m = d.superdata;    while (m) {        n += priv(m->d.data)->methodCount;        m = m->d.superdata;    }    return n;}

返回自身和基类所有的方法数之和

方法索引

int QMetaObject::indexOfMethod(const char *method) const{    const QMetaObject *m = this;    int i;    QArgumentTypeArray types;    QByteArray name = QMetaObjectPrivate::decodeMethodSignature(method, types);    i = indexOfMethodRelative<0>(&m, name, types.size(), types.constData());    if (i >= 0)        i += m->methodOffset();    return i;}

说明下decodeMethodSignature用来解析一个函数字符串如func(int a, QMap<int, QString> b),返回值是函数名func,types中记录参数类型

然后通过indexOfMethodRelative去匹配是否有函数名和参数个数,参数类型都一样的,有就返回索引,最后和方法偏移相加,就是该方法的最终索引

根据索引获取方法

QMetaMethod QMetaObject::method(int index) const{    int i = index;    i -= methodOffset();    if (i < 0 && d.superdata)        return d.superdata->method(index);    QMetaMethod result;    if (i >= 0 && i < priv(d.data)->methodCount) {        result.mobj = this;        result.handle = priv(d.data)->methodData + 5*i;    }    return result;}

返回的是一个QMetaMethod类,Qt提供了QMetaClassInfo,QMetaMethod、QMetaProperty、QMetaEnum来包装这些元信息和对其的一些操作,比如QMetaMethod就提供了name、parameterTypes、invoke等接口。

其它QMetaObejct中classinfo、property、enum的接口与上面method的接口实现类似。

继续探索QMetaOjbect中invokeMethod接口的实现

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

嗯,就是根据函数名、函数返回类型、参数类型,构建函数签名sig,然后通过indexOfMethod获取索引号,再通过method得到QMetaMethod ,最后实际调用的是QMetaMethod 的invoke接口

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    QThread *currentThread = QThread::currentThread();    QThread *objectThread = object->thread();    if (connectionType == Qt::AutoConnection) {        connectionType = currentThread == objectThread                         ? Qt::DirectConnection                         : Qt::QueuedConnection;    }#ifdef QT_NO_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;        }        int nargs = 1; // include return type        void **args = (void **) malloc(paramCount * sizeof(void *));        Q_CHECK_PTR(args);        int *types = (int *) malloc(paramCount * sizeof(int));        Q_CHECK_PTR(types);        types[0] = 0; // return type        args[0] = 0;        for (int i = 1; i < paramCount; ++i) {            types[i] = QMetaType::type(typeNames[i]);            if (types[i] != QMetaType::UnknownType) {                args[i] = QMetaType::create(types[i], param[i]);                ++nargs;            } else if (param[i]) {                // Try to register the type and try again before reporting an error.                void *argv[] = { &types[i], &i };                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]);                    for (int x = 1; x < i; ++x) {                        if (types[x] && args[x])                            QMetaType::destroy(types[x], args[x]);                    }                    free(types);                    free(args);                    return false;                }            }        }        QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,                                                        0, -1, nargs, types, args));    } else { // blocking queued connection#ifndef QT_NO_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, 0, 0, param, &semaphore));        semaphore.acquire();#endif // QT_NO_THREAD    }    return true;}

代码首先检查返回值的类型是否正确;
再检查参数的个数是否匹配;
再依据当前线程和被调对象所属 线程来调整connnection type;
如果是Qt::DirectConnection,直接调用

QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param)

如果是Qt::QueuedConnection,即异步调用

QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,0, -1, nargs, types, args));

当然还有一种Qt::BlockingQueuedConnection阻塞式异步调用,通过QSemaphore信号量来等待调用结束后再继续执行

QSemaphore semaphore;QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 0, -1, 0, 0, param, &semaphore));semaphore.acquire();

关于异步调用,是在对象所属线程中的事件循环中,从事件队列中取得这个事件时,再去调用这个函数

int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv){    if (object->d_ptr->metaObject)        return object->d_ptr->metaObject->metaCall(object, cl, idx, argv);    else        return object->qt_metacall(cl, idx, argv);}

如果是QObject类,那就直接调用metaCall,否则就是QObject的派生类,调用qt_metacall,就是moc文件自动生成的代码了。

moc_myclass.cpp参考上一章,下面回顾下qt_metacall的代码加深理解

int MyClass::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;}

可以看到如果是QMetaObject::InvokeMetaMethod,那么将调用

qt_static_metacall(this, _c, _id, _a);
void MyClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a){    if (_c == QMetaObject::CreateInstance) {        switch (_id) {        case 0: { MyClass *_r = new MyClass((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< QObject*(*)>(_a[2])));            if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;        default: break;        }    } else if (_c == QMetaObject::InvokeMetaMethod) {        MyClass *_t = static_cast<MyClass *>(_o);        Q_UNUSED(_t)        switch (_id) {        case 0: _t->signal1((*reinterpret_cast< int(*)>(_a[1]))); break;        case 1: { int _r = _t->slot1((*reinterpret_cast< int(*)>(_a[1])));            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;        case 2: { int _r = _t->method1((*reinterpret_cast< int(*)>(_a[1])));            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;        default: ;        }    } else if (_c == QMetaObject::IndexOfMethod) {        int *result = reinterpret_cast<int *>(_a[0]);        void **func = reinterpret_cast<void **>(_a[1]);        {            typedef void (MyClass::*_t)(int );            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&MyClass::signal1)) {                *result = 0;                return;            }        }    }#ifndef QT_NO_PROPERTIES    else if (_c == QMetaObject::ReadProperty) {    } else if (_c == QMetaObject::WriteProperty) {    } else if (_c == QMetaObject::ResetProperty) {    }#endif // QT_NO_PROPERTIES}

这段代码将调用最终转到我们自己的实现的函数(这里是signal1、slot1、method1)中来。这个函数不仅提供了method的动态调用,而且也提供了property的动态操作方法。可想而知,property的动态调用的实现方式一定和invocalbe method是类似的。

总结

通过上一节moc文件分析,和这一节的QMetaObject源码分析,我们应该是对Qt Meta-Object Model有了一个清楚的认识

  • Qt提供了一个QObject的基类
  • Qt扩展了C++语法,提供了Q_OBJECT、Q_INVOKABLE、signals、slots、emit、SIGNAL,SLOT、Q_PROPERT、Q_ENUM、Q_FLAG、Q_CLASSINFO等宏,moc会识别这些宏并生成对应的moc源代码
  • Q_OBJECT宏中声明了QMetaObject的静态对象,QMetaObject中记录了classiofo、method、property、enum,并提供了metacall来操作

*关于信号与槽机制关键几个接口activate和connect、disconect的实现,见下一节

阅读全文
1 0
原创粉丝点击