(1)Reciver对象可能有多个方法,qt如何知道要调用哪个函数详解
我们从QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)
函数入手。sender, receiver是外部传入的参数,signal_index, method_index是两个int。signal,method是字符串。QObject::connect(s,SIGNAL(send(int)),r1,SLOT(count(int)))中,SIGNAL(send(int))产生signal,内容为 "send(int)", SLOT(count(int))产生method,内容为 "1count(int)"。Connect函数中,先得到sender的元对象smeta = sender->metaObject(),然后通过这个函数 int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal),得到send(int)方法在元对象smeta中的索引值。通过rmeta = receiver->metaObject()得到元对象,method_index = rmeta->indexOfSlot(method);在receiver的元对象中找到"coun(int)"对应的函数的索引。知道了sender对象且知道要调用其函数的索引,知道了receiver对象且知道要调用其函数的索引,将四者连在一起。当触发sender的signal_index对应的函数时,调用已
连在一起的receiver的method_index对应的函数。
下面是具体的代码分析
QObject::connect(s,SIGNAL(send(int)),r1,SLOT(count(int))); bool QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) { //................................................ //<section1> const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); //</section1> //................................................ //<section2> const QMetaObject *rmeta = receiver->metaObject(); int method_index = -1; switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } //</section2> //............................................. //<section3> if (!QMetaObject::checkConnectArgs(signal, method)) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, receiver->metaObject()->className(), method); return false; } //</section3> //.............................................. //<section4> if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) return false; //</section4> const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; }
在此文中,sender对象是Informer的对象.receiver对象是Receiver的对象。
在section1中,const QMetaObject *smeta = sender->metaObject();得到了sender的元对象。这个元对象定义在.moc文件中。在Informer类中,有一个Q_OBJECT宏
class Informer:public QObject
{
Q_OBJECT;
//.....................
};
其中有个数据成员
static const QMetaObject staticMetaObject;
还有获得这个元对象的函数
virtual const QMetaObject *metaObject() const; \
在.moc文件中实现了它们
//<code_1>
const QMetaObject Informer::staticMetaObject = {
{ &QObject::staticMetaObject, qt_meta_stringdata_Informer,
qt_meta_data_Informer, 0 }
};
//</code_1>
const QMetaObject *Informer::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
}
sender->metaObject()便得到了QMetaObject对象。
重要代码
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal);
函数indexOfSignalRelative从sender对象的元对象中查找signal,即字符串"send(int)"对应的索引值。先看一下QMetaObjectPrivate的数据结构
//<code_2> struct QMetaObjectPrivate { 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 } //</code_2>
对比一下.moc文件中的qt_meta_data_Informer数组
//<code_3> static const uint qt_meta_data_Informer[] = { // content: 4, // revision //0 0, // classname 0, 0, // classinfo //2 1, 14, // methods//4 0, 0, // properties//6 0, 0, // enums/sets//8 0, 0, // constructors//10 0, // flags//12 1, // signalCount//13 // signals: signature, parameters, type, tag, flags 17, 10, 9, 9, 0x05, //14 0 // eod }; //</code_3>
发现qt_meta_data_Informer前14个字段对应QMetaObjectPrivate数据结构中的字段。能不能由qt_meta_data_Informer得到QMetaObjectPrivate对象呢。是可以的。见Qt的函数priv,代码如下
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
这样便得到了QMetaObjectPrivate对象。
我们回过头来看indexOfSignalRelative函数,代码如下
int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal) { int i = -1; while (*baseObject) { const QMetaObject *const m = *baseObject; for (i = priv(m->d.data)->methodCount-1; i >= 0; --i) if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal && strcmp(signal, m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { break; } if (i >= 0) break; *baseObject = m->d.superdata; } //...................................... }
QMetaObject的数据结构如下
struct QMetaObject { struct { // private data const QMetaObject *superdata; const char *stringdata; const uint *data; const void *extradata; } d; }
m->d.data是const uint *data。m->d.data保存了qt_meta_data_Informer静态数组的指针,见code_1。priv(m->d.data)将qt_meta_data_Informer转化成QMetaObjectPrivate数据类型。很明显,priv(m->d.data)->methodCount为14, 则priv(m->d.data)->methodData + 5*i + 4 = 18, m->d.data[priv(m->d.data)->methodData + 5*i + 4] = m->d.data[18], m->d.tata[18]为0x05, MethodTypeMask = 0x0c, 0x05 & 0x0c = 0x04 == MethodSignal。
这段代码(m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal,判断从元对象而来的函数是不是Signal,因为元对象的方法也可能是Slot
strcmp(signal, m->d.stringdata
+ m->d.data[priv(m->d.data)->methodData + 5*i])
priv(m->d.data)->methodData + 5*i = 14
d.data[14]为17
m->d.stringdata+ m->d.data[priv(m->d.data)->methodData + 5*i] = m->d.stringdata+17
strcmp(signal, m->d.stringdata+17)
Signal为"send(int)", m->d.stringdata为 "Informer",看一下.moc文件中有如下代码
static const char qt_meta_stringdata_Informer[] = {
"Informer\0\0status\0send(int)\0"
};
而 m->d.stringdata在IDE中显示为 "Informer",这是因为\0的原因。
我们看下m->d.stringdata内存,如下
查一下"Informer..status."刚好17位, m->d.stringdata+17正好移动到"send(int)"起始处。这回就明白了qt_meta_data_Informer中17的原因了。
(2)如何将参数传给reciver对应的函数
这个问题就简单了。看<section3>函数checkConnectArgs。这个函数很简单,就是检查const char *signal, const char *method参数是否一样。就是简单的字符串比较
代码如下
/*! Returns true if the \a signal and \a method arguments are compatible; otherwise returns false. Both \a signal and \a method are expected to be normalized. \sa normalizedSignature() */ bool QMetaObject::checkConnectArgs(const char *signal, const char *method) { const char *s1 = signal; const char *s2 = method; while (*s1++ != '(') { } // scan to first '(' while (*s2++ != '(') { } if (*s2 == ')' || qstrcmp(s1,s2) == 0) // method has no args or return true; // exact match int s1len = qstrlen(s1); int s2len = qstrlen(s2); if (s2len < s1len && strncmp(s1,s2,s2len-1)==0 && s1[s2len-1]==',') return true; // method has less args return false; }
参数类型一样后,在信号发送端,所有参数强转成void * [], 储存在void **argv中,在接受端强转成对应参数既可。
代码如下
// SIGNAL 0 void Informer::send(int _t1) { void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 0, _a); } int Receiver::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: count((*reinterpret_cast< int(*)>(_a[1]))); break; default: ; } _id -= 1; } return _id; }
本文分析了Qt信号槽机制脉络,更具体的细节就不分析了。至此完成了此机制的分析。