Qt Meta Type System
Posted unclerunning
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt Meta Type System相关的知识,希望对你有一定的参考价值。
Qt Meta Type System
本文是对 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 id
static int type(const char *typeName);
static int type(const QByteArray &typeName);
...
//从tape id获取type name
static const char *typeName(int type);
...
//从type id获取QMetaType
static 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的主要内容,如果未能解决你的问题,请参考以下文章