所谓meta call就是通过object的meta system的支持来动态调用object的方法,metacall也是signal&slot的机制的基石。本篇通过参考源代码来探究meta call的实现方法。
QMetaObject::invokeMethod():
bool
( QObject * obj
, const char * member
, Qt::ConnectionType type
,
QGenericReturnArgument ret
, QGenericArgument val0
=
QGenericArgument( 0 ), 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()
)
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,type参数表明该调用时同步的还是异步的。ret是一个
通用的用来存储返回值的类型,后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。(这里让人感觉
比较奇怪的是Qt为什么不将这个参数列表弄成某种动态的形式,而是最多九个)
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
这个方法的实现如下:
- 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(')');
- else
- sig[sig.size() - 1] = ')';
- sig.append('\0');
- int idx = obj->metaObject()->indexOfMethod(sig.constData());
- if (idx < 0) {
- QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
- idx = obj->metaObject()->indexOfMethod(norm.constData());
- }
- if (idx < 0 || idx >= obj->metaObject()->methodCount())
- return false;
- QMetaMethod method = obj->metaObject()->method(idx);
- return method.invoke(obj, type, ret,
- val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
- }
先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,这是运行时的行为,参数类型转换是编译时的行为。
然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个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;
-
- if (returnValue.data()) {
- const char *retType = typeName();
- if (qstrcmp(returnValue.name(), retType) != 0) {
-
-
-
- QByteArray unnormalized;
- int len = qstrlen(returnValue.name());
- unnormalized.reserve(len + 3);
- unnormalized = "_(";
- unnormalized.append(returnValue.name());
- unnormalized.append(')');
- QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());
- normalized.truncate(normalized.length() - 1);
- if (qstrcmp(normalized.constData() + 2, retType) != 0)
- return false;
- }
- }
-
- 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;
- }
- int metaMethodArgumentCount = 0;
- {
-
- const char *names = mobj->d.stringdata + mobj->d.data[handle + 1];
- if (*names == 0) {
-
- const char *signature = mobj->d.stringdata + mobj->d.data[handle];
- while (*signature && *signature != '(')
- ++signature;
- if (*++signature != ')')
- ++metaMethodArgumentCount;
- } else {
- --names;
- do {
- ++names;
- while (*names && *names != ',')
- ++names;
- ++metaMethodArgumentCount;
- } while (*names);
- }
- }
- if (paramCount <= metaMethodArgumentCount)
- return false;
-
- QThread *currentThread = QThread::currentThread();
- QThread *objectThread = object->thread();
- if (connectionType == Qt::AutoConnection) {
- connectionType = currentThread == objectThread
- ? Qt::DirectConnection
- : Qt::QueuedConnection;
- }
-
- 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 methodIndex = ((handle - priv(mobj->d.data)->methodData) / 5) + mobj->methodOffset();
- if (connectionType == Qt::DirectConnection) {
- return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0;
- } else {
- if (returnValue.data()) {
- qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
- "queued connections");
- return false;
- }
- int nargs = 1;
- void **args = (void **) qMalloc(paramCount * sizeof(void *));
- Q_CHECK_PTR(args);
- int *types = (int *) qMalloc(paramCount * sizeof(int));
- Q_CHECK_PTR(types);
- types[0] = 0;
- args[0] = 0;
- for (int i = 1; i < paramCount; ++i) {
- types[i] = QMetaType::type(typeNames[i]);
- if (types[i]) {
- args[i] = QMetaType::construct(types[i], param[i]);
- ++nargs;
- } else if (param[i]) {
- 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]);
- }
- qFree(types);
- qFree(args);
- return false;
- }
- }
- if (connectionType == Qt::QueuedConnection) {
- QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,
- 0,
- -1,
- nargs,
- types,
- args));
- } else {
- if (currentThread == objectThread) {
- qWarning("QMetaMethod::invoke: Dead lock detected in "
- "BlockingQueuedConnection: Receiver is %s(%p)",
- mobj->className(), object);
- }
-
- #ifdef QT_NO_THREAD
- QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,
- 0,
- -1,
- nargs,
- types,
- args));
- #else
- QSemaphore semaphore;
- QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,
- 0,
- -1,
- nargs,
- types,
- args,
- &semaphore));
- semaphore.acquire();
- #endif // QT_NO_THREAD
- }
- }
- return true;
- }
代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,看懂这段代码需要参考该系列之二对moc文件的解析;再依据当前线程和被调对象所属
线程来调整connnection type;如果是directconnection,直接调用
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod,
methodIndex,
param),param是将所有参数值指针排列组成的指针数组。如果不是directconnection,也即异步调用,就通过一个post一个
QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。
QMetaObject::metacall的实现如下:
-
-
-
- int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
- {
- if (QMetaObject *mo = object->d_ptr->metaObject)
- return static_cast(mo)->metaCall(cl, idx, argv);
- else
- return object->qt_metacall(cl, idx, argv);
- }
如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject
来调用,这里要参考该系列之三对QMetaObjectPrivate的介绍,这个条件实际上就是object就是QObject类型,而不是派生类型。
否则调用object::qt_metacall。
对于异步调用,QObject的event函数里有如下代码:
- case QEvent::MetaCall:
- {
- d_func()->inEventHandler = false;
- QMetaCallEvent *mce = static_cast(e);
- QObjectPrivate::Sender currentSender;
- currentSender.sender = const_cast(mce->sender());
- currentSender.signal = mce->signalId();
- currentSender.ref = 1;
- QObjectPrivate::Sender * const previousSender =
- QObjectPrivate::setCurrentSender(this, ¤tSender);
- #if defined(QT_NO_EXCEPTIONS)
- mce->placeMetaCall(this);
- #else
- QT_TRY {
- mce->placeMetaCall(this);
- } QT_CATCH(...) {
- QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender);
- QT_RETHROW;
- }
- #endif
- QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender);
- break;
- }
QMetaCallEvent的代码很简单:
int QMetaCallEvent::placeMetaCall(QObject *object)
{ return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, id_, args_);}
殊途同归。
最后来看一下object->qt_metacall是如何实现的,这又回到了该系统之二所提供的示例moc文件中去了。该文件提供了该方法的实现:
- # int TestObject::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) {
- # switch (_id) {
- # case 0: clicked(); break;
- # case 1: pressed(); break;
- # case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;
- # case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;
- # default: ;
- # }
- # _id -= 4;
- # }
- # #ifndef QT_NO_PROPERTIES
- # else if (_c == QMetaObject::ReadProperty) {
- # void *_v = _a[0];
- # switch (_id) {
- # case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;
- # case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;
- # }
- # _id -= 2;
- # } else if (_c == QMetaObject::WriteProperty) {
- # void *_v = _a[0];
- # switch (_id) {
- # case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;
- # case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;
- # }
- # _id -= 2;
- # } else if (_c == QMetaObject::ResetProperty) {
- # switch (_id) {
- # case 0: resetPropertyA(); break;
- # case 1: resetPropertyB(); break;
- # }
- # _id -= 2;
- # } else if (_c == QMetaObject::QueryPropertyDesignable) {
- # _id -= 2;
- # } else if (_c == QMetaObject::QueryPropertyScriptable) {
- # _id -= 2;
- # } else if (_c == QMetaObject::QueryPropertyStored) {
- # _id -= 2;
- # } else if (_c == QMetaObject::QueryPropertyEditable) {
- # _id -= 2;
- # } else if (_c == QMetaObject::QueryPropertyUser) {
- # _id -= 2;
- # }
- # #endif // QT_NO_PROPERTIES
- # return _id;
- # }
这段代码将调用最终转到我们自己的实现的函数中来。这个函数不经提供了metamethod的动态调用,而且也提供了property的动态操作方法。可想而知,property的动态调用的实现方式一定和invocalbe method是一致的。