(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>
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信号槽机制脉络,更具体的细节就不分析了。至此完成了此机制的分析。