err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
在上面的处理之后,如果不能得到正确的序号,那么编译器就通过上面的函数输出编译信息,提示connec不正确。
int QMetaObjectPrivate::originalClone(const QMetaObject *mobj, int local_method_index)
{
int handle = get(mobj)->methodData + 5 * local_method_index;
while (mobj->d.data[handle + 4] & MethodCloned) {
handle -= 5;
local_method_index--;
}
return local_method_index;
}
每一个信号和槽函数的相关信息包含五个数字,所以这里的索引值是基位置加上5*索引值,其中以handle为基地址的第四个表示函数的一些属性。这里如果函数的属性是MethodClone则将这个索引减掉。有关信息可以参考QT实现(1)以及qmeta_p.h的相关定义。
static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset)
{
*signalOffset = *methodOffset = 0;
const QMetaObject *m = metaobject->d.superdata;
while (m) {
const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m);
*methodOffset += d->methodCount;
*signalOffset += (d->revision >= 4) ? d->signalCount : d->methodCount;
m = m->d.superdata;
}
}
将所有的信号和槽的偏移累加起来,由于在revision<4的时候,method技术和信号计数是同一个,所以才会有这样的格式。同样的,槽函数的计数实际可以通过method的计数减去信号的计数。由于这里是递加自身以及自身父类的信号和槽的函数技术,所以在QT实现一当中一次递减的是本身所在类的全部的索引。
int signal_absolute_index = signal_index + methodOffset;
signal_index += signalOffset;
QByteArray tmp_method_name;
int membcode = extract_code(method);
int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m,
const char *slot,
bool normalizeStringData)
{
return indexOfMethodRelative<MethodSlot>(m, slot, normalizeStringData);
}
这里又转到上面的函数当中,主要还是用于获取索引值。
QMetaMethod smethod = smeta->method(signal_absolute_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
上面的函数用于创建QMetaMethod类别,QMetaMethod定义在qmetaobject.h当中
QMetaMethod QMetaObject::method(int index) const
{
int i = index;
i -= methodOffset();
if (i < 0 && d.superdata)
return d.superdata->method(index);
QMetaMethod result;
if (i >= 0 && i < priv(d.data)->methodCount) {
result.mobj = this;
result.handle = priv(d.data)->methodData + 5*i;
}
return result;
}
bool QMetaObjectPrivate ::connect ( const QObject * sender , int signal_index ,const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
int method_offset = rmeta ? rmeta->methodOffset() : 0;
QObjectPrivate::StaticMetaCallFunction callFunction =
(rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection) {
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
if (connectionLists && connectionLists->count() > signal_index) {
const QObjectPrivate::Connection *c2 =
(*connectionLists)[signal_index].first;
int method_index_absolute = method_index + method_offset;
while (c2) {
if (c2->receiver == receiver && c2->method() == method_index_absolute)
return false;
c2 = c2->nextConnectionList;
}
}
type &= Qt::UniqueConnection - 1;
}
QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
c->sender = s;
c->receiver = r;
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->argumentTypes = types;
c->nextConnectionList = 0;
c->callFunction = callFunction;
QT_TRY {
QObjectPrivate::get(s)->addConnection(signal_index, c);
} QT_CATCH(...) {
delete c;
QT_RETHROW;
}
c->prev = &(QObjectPrivate::get(r)->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
QObjectPrivate *const sender_d = QObjectPrivate::get(s);
if (signal_index < 0) {
sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
} else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
}
return true;
}
整个connect过程分为四步,第一步根据类型是否是unique检验connect是否是唯一的连接。如果之前已经有连接,那么直接返回false,否则继续。第二步,在当前是unique的情况下之前没有connect或者没有unique要求的时候直接创建connection类别。第三步调用addConnection将刚刚创建的connection加入到链表当中。第四步,设置信号的位图。看起来很复杂的prev和next,实际这里只是单链表的挂在,最后的senders值为C,然后利用八位的位图置信号位。之余Lock是为了在函数跳出的时候自动析构,从而避免使用者忘记解锁。
void QObjectPrivate::addConnection(int signal, Connection *c)
{
if (!connectionLists)
connectionLists = new QObjectConnectionListVector();
if (signal >= connectionLists->count())
connectionLists->resize(signal + 1);
ConnectionList &connectionList = (*connectionLists)[signal];
if (connectionList.last) {
connectionList.last->nextConnectionList = c;
} else {
connectionList.first = c;
}
connectionList.last = c;
cleanConnectionLists();
}
利用尾插法将建立connection链表。然后根据需要清除无效的链表项目,因为QT是可以disconnect的。void QObjectPrivate::cleanConnectionLists()
{
if (connectionLists->dirty && !connectionLists->inUse) {
for (int signal = -1; signal < connectionLists->count(); ++signal) {
QObjectPrivate::ConnectionList &connectionList =
(*connectionLists)[signal];
QObjectPrivate::Connection *last = 0;
QObjectPrivate::Connection **prev = &connectionList.first;
QObjectPrivate::Connection *c = *prev;
while (c) {
if (c->receiver) {
last = c;
prev = &c->nextConnectionList;
c = *prev;
} else {
QObjectPrivate::Connection *next = c->nextConnectionList;
*prev = next;
delete c;
c = next;
}
}
connectionList.last = last;
}
connectionLists->dirty = false;
}
}
下面列出整个connect函数的实现。通过之前的分析,可以看出整个函数分为三部分,参数验证和相应的参数转化,然后才是真正的实现connect。bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;
}
if (type == Qt::AutoCompatConnection) {
type = Qt::AutoConnection;
}
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return false;
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
return false;
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
}
if (signal_index < 0) {
// re-use tmp_signal_name and signal from above
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
}
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
int signalOffset, methodOffset;
computeOffsets(smeta, &signalOffset, &methodOffset);
int signal_absolute_index = signal_index + methodOffset;
signal_index += signalOffset;
QByteArray tmp_method_name;
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
return false;
const char *method_arg = method;
++method; // skip code
const QMetaObject *rmeta = receiver->metaObject();
int method_index_relative = -1;
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
break;
}
if (method_index_relative < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
// rmeta may have been modified above
rmeta = receiver->metaObject();
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
if (method_index_relative < 0)
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
if (method_index_relative < 0)
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
break;
}
}
if (method_index_relative < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
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;
}
int *types = 0;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
return false;
if (warnCompat) {
QMetaMethod smethod = smeta->method(signal_absolute_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
}
if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
return false;
const_cast<QObject*>(sender)->connectNotify(signal - 1);
return true;
}