Qt Meta Type System

来源:互联网 发布:什么软件可以锁一体机 编辑:程序博客网 时间:2024/06/01 21:22

Qt Meta Type System

  • Qt Meta Type System
    • Meta Type System支持下的异步的信号和槽连接
    • Meta Type System支持下的Property System
    • Meta Type System支持下的异步的InvokeMethod
    • 注册类型的方式

本文是对 Qt元系统之类型注册 的补充

Meta Type System支持下的异步的信号和槽连接

同步的信号和槽连接用不到类型信息,因为参数可以使用void指针来传递。但是,异步的信号和槽连接由于需要存储参数,所以需要类型信息:

static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc){    QScopedArrayPointer<int> types(new int [argc + 1]);    for (int i = 0; i < argc; ++i) {        const QArgumentType &type = argumentTypes[i];        if (type.type())            types[i] = type.type();        else if (type.name().endsWith('*'))            types[i] = QMetaType::VoidStar;        else            // QMetaType::type返回类名对应的type id                /*            如果该类型没有注册,返回值为QMetaType::UnknownType            */            types[i] = QMetaType::type(type.name());        if (!types[i]) {            qWarning("QObject::connect: Cannot queue arguments of type '%s'\n"                     "(Make sure '%s' is registered using qRegisterMetaType().)",                     type.name().constData(), type.name().constData());            return 0;        }    }    types[argc] = 0;    return types.take();}

QMetaType::type静态函数返回类型名称对应的type id,所以:

知道了类型名称字符串就可以得到类型的type id,就能得到类的类型信息。

class Q_CORE_EXPORT QMetaType {...//从tape name获取type idstatic int type(const char *typeName);static int type(const QByteArray &typeName);...//从tape id获取type name  static const char *typeName(int type);  ...  //从type id获取QMetaTypestatic QMetaType typeInfo(const int type);explicit QMetaType(const int type);...  };  

如果信号和槽使用的参数中存在未注册的类型,我们是无法建立起异步连接的:

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,                                     const QObject *receiver, const char *method,                                     Qt::ConnectionType type) {   ...    int *types = 0;    /*    如果连接类型为Qt::QueuedConnection,并且参数中存在未注册的类型    【type id为QMetaType::UnknownType】,则不会建立连接。    */    if ((type == Qt::QueuedConnection)&&        !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size())))     {        return QMetaObject::Connection(0);    }   ...     }

Meta Type System支持下的Property System

bool QObject::setProperty(const char *name, const QVariant &value){    Q_D(QObject);    const QMetaObject* meta = metaObject();    if (!name || !meta)        return false;    //根据属性名获取在整个继承链的属性信息<name, type, flags>中的位置:    //即确定name是类的整个继承链的第一个属性呢还是第二个属性呢...    int id = meta->indexOfProperty(name);  ...    QMetaProperty p = meta->property(id);#ifndef QT_NO_DEBUG    if (!p.isWritable())        qWarning("%s::setProperty: Property \"%s\" invalid,"                 " read-only or does not exist", metaObject()->className(), name);#endif    return p.write(this, value);}

setProperty的第二个参数是一个QVariant型变量,除了QVariant静态支持的类型外,所有自定义类型都需要使用Q_DECLARE_METATYPE(TYPE)生成一个QMetaTypeId2<MyClass>特化类。

Meta Type System支持下的异步的InvokeMethod

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{//...    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()    };//...     // 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);        //存放参数的type id        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) {            //QMetaType::type 返回type id             types[i] = QMetaType::type(typeNames[i]);            if (types[i] != QMetaType::UnknownType) {                //拷贝参数,args[i]指向拷贝的参数                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;                }            }        }        // Post a QMetaCallEvent        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;}

这样看来,其实和异步的信号槽一样,异步的InvokeMethod也需要元类型系统的支持。

注册类型的方式

//方式一:template <typename T>int qRegisterMetaType(const char *typeName)// qRegisterMetaType("MyClass");//方式二:Q_DECLARE_METATYPE(MyClass)// 延迟注册QMetaTypeId2<MyClass>::qt_metatype_id();  // Q_DECLARE_METATYPE(MyClass);// QMetaTypeId2<MyClass>::qt_metatype_id(); 

其实,第二种向系统注册类型的方式归根到低还是使用了方式一,那他有何存在的必要呢?

想想像QVariant这样的类,如果qt只提供方式一的qRegisterMetaType模板接口,那么QVariant势必需要知道他将要存储的类的类名字符串。那么,它的QVariant::fromValue接口就会是这样子:

MyClass mc;qRegisterMetaType<MyClass>("MyClass"); //注册QVariant v = QVariant::fromValue("MyClass",mc); //知道了类名才能获取它的注册信息

或者,将qRegisterMetaType<MyClass>("MyClass"); //注册延迟到QVariant::fromValue中进行:

MyClass mc; //知道了类名就能在找不到它的注册信息的情况下对其进行注册,并获取它的注册信息。QVariant v = QVariant::fromValue("MyClass",mc);

qt确实可以这样做,只提供方式一,不提供也不用实现方式二。

只提供方式一的问题是:对于每一个类型,每一次使用都得让用户传入相应的类名字符串,不仅麻烦,而且容易出错,不好处理。

与其要求用户指定类名字符串,不如把指定类名字符串的任务交给编译器,这应该就是方式二的价值。只需使用Q_DECLARE_METATYPE(MyClass)宏定义一个QMetaTypeId2<MyClass>特化类,QVariant结合模板推导机制就能获取MyClass的类型信息:

调用Q_DECLARE_METATYPE(MyClass)定义的QMetaTypeId<MyClass>::qt_metatype_id()保证能够返回MyClass注册到系统中的type id ,因为如果不存在,它会注册类型信息到系统中,然后返回注册的type id,这种情况在它第一次被调用时发生。

于是乎,世界就是现在这样子:

myclass.h

class MyClass {   ... } Q_DECLARE_METATYPE(MyClass)     // 延迟注册
MyClass mc;QVariant v = QVariant::fromValue(mc); 

确实省了用户不少事。

0 0