我们在使用connect函数的时候一般是这样调用的:
[cpp] view plain copy connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));这里用到了两个宏:SIGNAL() 和SLOT();通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。 在qobjectdefs.h中可以看到SIGNAL() 和SLOT()的宏定义:
[cpp] view plain copy #ifndef QT_NO_DEBUG # define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__) # define METHOD(a) qFlagLocation("0"#a QLOCATION) # define SLOT(a) qFlagLocation("1"#a QLOCATION) # define SIGNAL(a) qFlagLocation("2"#a QLOCATION) #else # define METHOD(a) "0"#a # define SLOT(a) "1"#a # define SIGNAL(a) "2"#a #endif所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。
[cpp] view plain copy const void *cbdata[] = { sender, signal, receiver, method, &type }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) return true;
判断连接是否已经建立。 QInternal::ConnectCallback在qglobal.cpp中实现。
[cpp] view plain copy bool QInternal::activateCallbacks(Callback cb, void **parameters) { Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id"); QInternal_CallBackTable *cbt = global_callback_table(); if (cbt && cb < cbt->callbacks.size()) { QList<qInternalCallback> callbacks = cbt->callbacks[cb]; bool ret = false; for (int i=0; i<callbacks.size(); ++i) ret |= (callbacks.at(i))(parameters); return ret; } return false; }
QInternal_CallBackTable 定义为(qglobal.cpp)
[cpp] view plain copy struct QInternal_CallBackTable { QVector<QList<qInternalCallback> > callbacks; };qInternalCallback定义为(qnamespace.h)
[cpp] view plain copy typedef bool (*qInternalCallback)(void **);这是一个函数指针 返回值是bool,只有一个参数为void**。这个指针在调用registerCallback加入列表。
[cpp] view plain copy if (!check_signal_macro(sender, signal, "connect", "bind")) return false;
判断signal是否合法。
在QObject.cpp文件中可以找到check_signal_macro的实现
[cpp] view plain copy static bool check_signal_macro(const QObject *sender, const char *signal, const char *func, const char *op) { int sigcode = extract_code(signal); if (sigcode != QSIGNAL_CODE) { if (sigcode == QSLOT_CODE) qWarning("Object::%s: Attempt to %s non-signal %s::%s", func, op, sender->metaObject()->className(), signal+1); else qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s", func, op, sender->metaObject()->className(), signal); return false; } return true; }extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。
[cpp] view plain copy static int extract_code(const char *member) { // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE return (((int)(*member) - '0') & 0x3); }这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。
[cpp] view plain copy #ifdef QT3_SUPPORT #define METHOD_CODE 0 // member type codes #define SLOT_CODE 1 #define SIGNAL_CODE 2 #endif这个定义与之前的SIGNAL和SLOT的定义是对应的。
[cpp] view plain copy const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } }
获取signal的索引。
metaObject()是在moc_name.cpp文件中生成的。
[cpp] view plain copy return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;其中staticMetaObject也是在moc文件中定义的
[cpp] view plain copy const QMetaObject MainWindow::staticMetaObject = { { &QMainWindow::staticMetaObject, qt_meta_stringdata_MainWindow, qt_meta_data_MainWindow, 0 } };qt_meta_stringdata_MainWindow(具体名字和类名有关)就是staticconstchar[]类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。
qt_meta_data_MainWindow(具体名字和类名有关)是staticconstuint[]类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。
[cpp] view plain copy enum MethodFlags { AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, AccessMask = 0x03, //mask MethodMethod = 0x00, MethodSignal = 0x04, MethodSlot = 0x08, MethodConstructor = 0x0c, MethodTypeMask = 0x0c, MethodCompatibility = 0x10, MethodCloned = 0x20, MethodScriptable = 0x40 };indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。
[cpp] view plain copy 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 = -1; switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } if (method_index < 0) { // check for normalized methods tmp_method_name = QMetaObject::normalizedSignature(method); method = tmp_method_name.constData(); switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } } if (method_index < 0) { err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; }
校验method并且查找它的索引。过程与signal类似。
[cpp] view plain copy 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; }
判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少(method与signal前几个参数一致)的时候返回true,其它返回false。
[cpp] view plain copy int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes()))) return false;
如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个
static conststruct { constchar * typeName;int type;} types[];在这里记录了全部类型和名称如({"void",QMetaType::Void});Void在Qmetatype.h中定义。