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);
确实省了用户不少事。
- Qt Meta Type System
- qt meta-object system
- Qt Meta-Object System
- Qt Meta-Object System
- Register a Meta Type in Qt
- Qt 元对象系统(Meta-Object System)
- Qt 元对象系统(Meta-Object System)
- Qt 元对象系统(Meta-Object System)
- Qt Meta Object system 学习(一)
- Qt Meta Object system 学习(二)
- Qt Meta Object system 学习(三)
- Qt Meta Object system 学习(一)
- Qt Meta Object system 学习(二)
- Qt Meta Object system 学习(一)
- Inside QT Series (五):元对象系统(Meta-Object System)
- Inside Qt Series (五):元对象系统(Meta-Object System)
- Changes to the Meta-Object System in Qt 5
- Inside QT Series (三):元对象系统(Meta-Object System)
- php+android 搭建后台及显示
- MVC开发模式
- Ajax学习笔记
- CF#410(Div.2) 解题报告
- Ajax小案例:验证表单中用户名是否可用
- Qt Meta Type System
- String和StringBuffer的区别
- java语言基础(41)——面向对象(类名接口名作参数和返回值)
- 第十五届北京师范大学程序设计竞赛决赛 K. Keep In Line【模拟】
- ConcurrentHashMap原理详解
- Scrapy全局变量
- Android蓝牙连接之SPP协议
- 事务
- 晨兴资本程宇:新一波技术革命前夜,谁的机会更大?