Qt MetaObject System详解
网上的资源比较乱,该文章整理自地址:http://www.xuebuyuan.com/735789.html
Qt meta-object系统基于三个方面:
1、QObject提供一个基类,方便派生类使用meta-object系统的功能;
2、Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号、槽;
3、Meta Object编译器(MOC),为每个QObject派生类生成代码,以支持meta-object功能
QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应MetaObject类以及对QObject的函数override。
QObject和QMetaObject:
顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特有的signal&slot信息。
1 QObject::metaObject()
该方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
1,基本信息
1 struct Q_CORE_EXPORT QMetaObject 2 { 3 const char *className() const; 4 const QMetaObject *superClass() const;
2,classinfo:提供额外的类信息。其实就是一些名值对。用户可以在类的声明中以
1 Q_CLASSINFO(name, value)
方式添加
1 int classInfoOffset() const; 2 int classInfoCount() const; 3 int indexOfClassInfo(const char *name) const; 4 QMetaClassInfo classInfo(int index) const;
3、contructor:提供该类的构造方法信息
1 int constructorCount() const; 2 int indexOfConstructor(const char *constructor) const; 3 QMetaMethod constructor(int index) const;
4、enum:描述该类声明体中所包含的枚举类型信息
1 int enumeratorOffset() const; 2 int enumeratorCount() const; 3 int indexOfEnumerator(const char *name) const; 4 QMetaEnum enumerator(int index) const;
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
1 int methodOffset() const; 2 int methodCount() const; 3 int indexOfMethod(const char *method) const; 4 int indexOfSignal(const char *signal) const; 5 int indexOfSlot(const char *slot) const; 6 QMetaMethod method(int index) const;
6、property:类型的属性信息
1 int propertyOffset() const; 2 int propertyCount() const; 3 int indexOfProperty(const char *name) const; 4 QMetaProperty property(int index) const; ////返回类中设置了USERflag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,
Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
下文来源: http://biancheng.dnbcw.info/linux/253557.html
如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成meta信息,这个信息在前一篇中所提到的moc文件中。这一篇通过解析这个一个示例moc文件来阐述这些meta信息的存储方式和格式;本篇先说明了一下QMetaObject的数据结构,然后呈现了一个简单的类TestObject类及其生成的moc文件,最后对这个moc文件个内容进行了详细解释。
QMetaObject的数据定义:
QMetaObject包含唯一的数据成员如下(见头文件qobjectdefs.h)
1 struct Q_CORE_EXPORT QMetaObject 2 { 3 struct { // private data 4 const QMetaObject *superdata; //父类QMetaObject实例的指针 5 const char *stringdata; //一段字符串内存块,包含MetaObject信息之字符串信息 6 const uint *data; //一段二级制内存块,包含MetaObject信息之二进制信息 7 const void *extradata; //额外字段,暂未使用 8 } d; 9 };
QMetaObjectPrivate的数据定义:
QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,比如classInfoCount、
methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。下文结合这两个内存块的结构再分析个字段的含义。
1 struct QMetaObjectPrivate 2 { 3 int revision; 4 int className; 5 int classInfoCount, classInfoData; 6 int methodCount, methodData; 7 int propertyCount, propertyData; 8 int enumeratorCount, enumeratorData; 9 int constructorCount, constructorData; //since revision 2 10 int flags; //since revision 3 11 int signalCount; //since revision 4 12 // revision 5 introduces changes in normalized signatures, no new members 13 // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself 14 };
下文利用一个示例QObject子类及其moc文件,来分析QMetaObject的信息结构。
示例类TestObject:
TestObject类继承自QObject,定义了两个Property:propertyA,propertyB;两个classinfo:Author,Version;一个枚举:TestEnum。
1 #include <QObject> 2 class TestObject : public QObject 3 { 4 Q_OBJECT 5 Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false) 6 Q_PROPERTY(QString propertyB READ getPropertyB WRITE getPropertyB RESET resetPropertyB) 7 Q_CLASSINFO("Author", "Long Huihu") 8 Q_CLASSINFO("Version", "TestObjectV1.0") 9 Q_ENUMS(TestEnum) 10 public: 11 enum TestEnum { 12 EnumValueA, 13 EnumValueB 14 }; 15 public: 16 TestObject(); 17 signals: 18 void clicked(); 19 void pressed(); 20 public slots: 21 void onEventA(const QString &); 22 void onEventB(int ); 23 }
示例类TestObject的moc文件:
1 #include "TestObject.h" 2 #if !defined(Q_MOC_OUTPUT_REVISION) 3 #error "The header file 'TestObject.h' doesn't include <QObject>." 4 #elif Q_MOC_OUTPUT_REVISION != 62 5 #error "This file was generated using the moc from 4.6.0. It" 6 #error "cannot be used with the include files from this version of Qt." 7 #error "(The moc has changed too much.)" 8 #endif 9 QT_BEGIN_MOC_NAMESPACE 10 static const uint qt_meta_data_TestObject[] = { 11 // content: 12 4, // revision 13 0, // classname 14 2, 14, // classinfo 15 4, 18, // methods 16 2, 38, // properties 17 1, 44, // enums/sets 18 0, 0, // constructors 19 0, // flags 20 2, // signalCount 21 // classinfo: key, value 22 22, 11, 23 44, 29, 24 // signals: signature, parameters, type, tag, flags 25 53, 52, 52, 52, 0x05, 26 63, 52, 52, 52, 0x05, 27 // slots: signature, parameters, type, tag, flags 28 73, 52, 52, 52, 0x0a, 29 91, 52, 52, 52, 0x0a, 30 // properties: name, type, flags 31 113, 105, 0x0a095007, 32 123, 105, 0x0a095007, 33 // enums: name, flags, count, data 34 133, 0x0, 2, 48, 35 // enum data: key, value 36 142, uint(TestObject::EnumValueA), 37 153, uint(TestObject::EnumValueB), 38 0 // eod 39 }; 40 static const char qt_meta_stringdata_TestObject[] = { 41 "TestObject\0Long Huihu\0Author\0" 42 "TestObjectV1.0\0Version\0\0clicked()\0" 43 "pressed()\0onEventA(QString)\0onEventB(int)\0" 44 "QString\0propertyA\0propertyB\0TestEnum\0" 45 "EnumValueA\0EnumValueB\0" 46 }; 47 const QMetaObject TestObject::staticMetaObject = { 48 { &QObject::staticMetaObject, qt_meta_stringdata_TestObject, 49 qt_meta_data_TestObject, 0 } 50 }; 51 #ifdef Q_NO_DATA_RELOCATION 52 const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; } 53 #endif //Q_NO_DATA_RELOCATION 54 const QMetaObject *TestObject::metaObject() const 55 { 56 return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; 57 } 58 void *TestObject::qt_metacast(const char *_clname) 59 { 60 if (!_clname) return 0; 61 if (!strcmp(_clname, qt_meta_stringdata_TestObject)) 62 return static_cast<void*>(const_cast< TestObject*>(this)); 63 return QObject::qt_metacast(_clname); 64 } 65 int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) 66 { 67 _id = QObject::qt_metacall(_c, _id, _a); 68 if (_id < 0) 69 return _id; 70 if (_c == QMetaObject::InvokeMetaMethod) { 71 switch (_id) { 72 case 0: clicked(); break; 73 case 1: pressed(); break; 74 case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break; 75 case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break; 76 default: ; 77 } 78 _id -= 4; 79 } 80 #ifndef QT_NO_PROPERTIES 81 else if (_c == QMetaObject::ReadProperty) { 82 void *_v = _a[0]; 83 switch (_id) { 84 case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break; 85 case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break; 86 } 87 _id -= 2; 88 } else if (_c == QMetaObject::WriteProperty) { 89 void *_v = _a[0]; 90 switch (_id) { 91 case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break; 92 case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break; 93 } 94 _id -= 2; 95 } else if (_c == QMetaObject::ResetProperty) { 96 switch (_id) { 97 case 0: resetPropertyA(); break; 98 case 1: resetPropertyB(); break; 99 } 100 _id -= 2; 101 } else if (_c == QMetaObject::QueryPropertyDesignable) { 102 _id -= 2; 103 } else if (_c == QMetaObject::QueryPropertyScriptable) { 104 _id -= 2; 105 } else if (_c == QMetaObject::QueryPropertyStored) { 106 _id -= 2; 107 } else if (_c == QMetaObject::QueryPropertyEditable) { 108 _id -= 2; 109 } else if (_c == QMetaObject::QueryPropertyUser) { 110 _id -= 2; 111 } 112 #endif // QT_NO_PROPERTIES 113 return _id; 114 } 115 // SIGNAL 0 116 void TestObject::clicked() 117 { 118 QMetaObject::activate(this, &staticMetaObject, 0, 0); 119 } 120 // SIGNAL 1 121 void TestObject::pressed() 122 { 123 QMetaObject::activate(this, &staticMetaObject, 1, 0); 124 } 125 QT_END_MOC_NAMESPACE
qt_meta_data_TestObject::定义的正是QMetaObject::d.data指向的信息块;
qt_meta_stringdata_TestObject:定义的是QMetaObject::d.dataString指向的信息块;
const QMetaObject TestObject::staticMetaObject :定义TestObject类的MetaObject实例,从中可以看出QMetaObject各个字段是如何被赋值的;
const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,返回上述的MetaObject实例指针。
TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或访问property,动态方法调用属性访问正是依赖于这个方法,在第四篇中会再讲到该方法。
TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。
TestObject类的所有meta信息就存储在 qt_meta_data_TestObject和qt_meta_stringdata_TestObject这两个静态数据中。 QMetaObject的接口的实现正是基于这两块数据。下面就对这两个数据进行分块说明。
1 static const uint qt_meta_data_TestObject[] = { 2 3 数据块一: 4 // content: 5 4, // revision 6 0, // classname 7 8 2, 14, // classinfo 9 10 4, 18, // methods 11 12 2, 38, // properties 13 1, 44, // enums/sets 14 0, 0, // constructors 15 0, // flags 16 2, // signalCount 17 18 这块数据可以被看做meta信息的头部,正好和QMetaObjectPrivate数据结构相对应,在QMetaObject的实现中,正是将这块数据映射为QMetaObjectPrivate进行使用的。 19 20 第一行数据“4”:版本号; 21 22 第二行数据“0”:类型名,该值是qt_meta_stringdata_TestObject的索引,qt_meta_stringdata_TestObject[0]这个字符串不正是类型名“TestObject”吗。 23 24 第三行数据“2,14”,第一个表明有2个classinfo被定义,第二个是说具体的 classinfo信息在qt_meta_data_TestObject中的索引,qt_meta_data_TestObject[14]的位置两个 classinfo名值对的定义; 25 26 第四行数据“4,18”,指明method的信息,模式同上; 27 28 第五行数据“2,38”,指明property的信息,模式同上; 29 第六行数据“1,14”,指明enum的信息,模式同上。 30 31 数据块二: 32 // classinfo: key, value 33 22, 11, 34 44, 29, 35 36 classinfo信息块。第一行“22,11”,22表明 qt_meta_stringdata_TestObject[22]处定义的字符串是classinfo的key,11表明 qt_meta_stringdata_TestObject[11]处的字符串就是value。第二行“44,29”定义第二个classinfo。 37 38 数据块三: 39 // signals: signature, parameters, type, tag, flags 40 53, 52, 52, 52, 0x05, 41 63, 52, 52, 52, 0x05, 42 43 signal信息块。第一行“53, 52, 52, 52, 0x05”定义第一个signal clicked()。qt_meta_stringdata_TestObject[53]是signal名称字符串。parameters 52, type 52, tag 52, flags如何解释暂未知。 44 45 数据块四: 46 // slots: signature, parameters, type, tag, flags 47 73, 52, 52, 52, 0x0a, 48 91, 52, 52, 52, 0x0a, 49 50 slots信息,模式类似signal。 51 52 数据块五: 53 // properties: name, type, flags 54 113, 105, 0x0a095007, 55 123, 105, 0x0a095007, 56 57 property性信息,模式类signal和slots,105如何和type对应暂未知。 58 59 数据块六: 60 // enums: name, flags, count, data 61 133, 0x0, 2, 48, 62 // enum data: key, value 63 142, uint(TestObject::EnumValueA), 64 153, uint(TestObject::EnumValueB), 65 66 enum信息,第一行定义的是枚举名,flag,值的数目,data48不知是什么。 67 68 几行定义的是各枚举项的名称和值。名称同上都是qt_meta_stringdata_TestObject的索引值。 69 70 0 // eod 71 }; 72 73 static const char qt_meta_stringdata_TestObject[] = { 74 75 这块数据就是meta信息所需的字符串。是一个字符串的序列。 76 "TestObject\0Long Huihu\0Author\0" 77 "TestObjectV1.0\0Version\0\0clicked()\0" 78 "pressed()\0onEventA(QString)\0onEventB(int)\0" 79 "QString\0propertyA\0propertyB\0TestEnum\0" 80 "EnumValueA\0EnumValueB\0" 81 };
本篇从Qt MetaObject源代码解读相关接口的实现,这些接口都定义于qmetaobject.cpp中。
QMetaObject::className()
1 inline const char *QMetaObject::className() const 2 { return d.stringdata; }
d.stringdata就是那块字符串数据,包含若干c字符串(以'\0')结尾。如果把d.stringdata当做一个c字符串指针的话,就是这个字符串序列的第一个字符串,正是类名。
QMetaObject::superClass()
1 inline const QMetaObject *QMetaObject::superClass() const 2 { return d.superdata; }
QMetaObject::classInfoCount()
1 int QMetaObject::classInfoCount() const 2 { 3 int n = priv(d.data)->classInfoCount; 4 const QMetaObject *m = d.superdata; 5 while (m) { 6 n += priv(m->d.data)->classInfoCount; 7 m = m->d.superdata; 8 } 9 return n; 10 }
从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。
函数priv是一个简单inline函数:
1 static inline const QMetaObjectPrivate *priv(const uint* data) 2 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。
QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。和前一篇的示例moc文件内容一对应,其含义一目了然。
1 struct QMetaObjectPrivate 2 { 3 int revision; 4 int className; 5 int classInfoCount, classInfoData; 6 int methodCount, methodData; 7 int propertyCount, propertyData; 8 int enumeratorCount, enumeratorData; 9 int constructorCount, constructorData; //since revision 2 10 int flags; //since revision 3 11 int signalCount; //since revision 12 }
QMetaObject:: classInfoOffset ()
1 int QMetaObject::classInfoOffset() const 2 { 3 int offset = 0; 4 const QMetaObject *m = d.superdata; 5 while (m) { 6 offset += priv(m->d.data)->classInfoCount; 7 m = m->d.superdata; 8 } 9 return offset; 10 }
该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量
QMetaObject:: classInfo (int index)
1 QMetaClassInfo QMetaObject::classInfo(int index) const 2 { 3 int i = index; 4 i -= classInfoOffset(); 5 if (i < 0 && d.superdata) 6 return d.superdata->classInfo(index); 7 8 QMetaClassInfo result; 9 if (i >= 0 && i < priv(d.data)->classInfoCount) { 10 result.mobj = this; 11 result.handle = priv(d.data)->classInfoData + 2*i; 12 } 13 return result; 14 }
1 class Q_CORE_EXPORT QMetaClassInfo 2 { 3 public: 4 inline QMetaClassInfo() : mobj(0),handle(0) {} 5 const char *name() const; 6 const char *value() const; 7 inline const QMetaObject *enclosingMetaObject() const { return mobj; } 8 private: 9 const QMetaObject *mobj; 10 uint handle; 11 friend struct QMetaObject; 12 };
这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;每条classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData +
2*i”这个表达式的值就是第i个classinfo的信息在d.data中的偏移。
QMetaObject:: indexOfClassInfo ()
1 int QMetaObject::indexOfClassInfo(const char *name) const 2 { 3 int i = -1; 4 const QMetaObject *m = this; 5 while (m && i < 0) { 6 for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) 7 if (strcmp(name, m->d.stringdata 8 + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { 9 i += m->classInfoOffset(); 10 break; 11 } 12 m = m->d.superdata; 13 } 14 return i; 15 }
按照继承层次,从下往上寻找名字为name的classinfo。
参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息的第一个32位值。该值是字符信息块d.stringdata中的索引值。因此 m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。
int constructorCount () const
1 int QMetaObject::constructorCount() const 2 { 3 if (priv(d.data)->revision < 2) 4 return 0; 5 return priv(d.data)->constructorCount; 6 }
QMetaMethod constructor ( int index ) const
1 QMetaMethod QMetaObject::constructor(int index) const 2 { 3 int i = index; 4 QMetaMethod result; 5 if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) { 6 result.mobj = this; 7 result.handle = priv(d.data)->constructorData + 5*i; 8 } 9 return result; 10 }
int indexOfConstructor ( const char * constructor ) const
1 int QMetaObject::indexOfConstructor(const char *constructor) const 2 { 3 if (priv(d.data)->revision < 2) 4 return -1; 5 for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { 6 const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; 7 if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { 8 return i; 9 } 10 } 11 return -1; 12 }
1 int enumeratorCount () const 2 3 int enumeratorOffset () const 4 5 QMetaEnum enumerator ( int index ) const 6 7 int indexOfEnumerator ( const char * name ) const
这组函数与classinfo那一组的实现及其相似。
1 int methodCount () const 略; 2 int methodOffset () const 略;
1 QMetaMethod QMetaObject::method(int index) const 2 { 3 int i = index; 4 i -= methodOffset(); 5 if (i < 0 && d.superdata) 6 return d.superdata->method(index); 7 8 QMetaMethod result; 9 if (i >= 0 && i < priv(d.data)->methodCount) { 10 result.mobj = this; 11 result.handle = priv(d.data)->methodData + 5*i; 12 } 13 return result; 14 }
该函数的实现方式也一目了然。
1 int indexOfMethod ( const char * method ) const 略;
1 int QMetaObject::indexOfSignal(const char *signal) const 2 { 3 const QMetaObject *m = this; 4 int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); 5 if (i < 0) { 6 m = this; 7 i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); 8 } 9 if (i >= 0) 10 i += m->methodOffset(); 11 return i; 12 }
1 int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, 2 const char *signal, 3 bool normalizeStringData) 4 { 5 int i = indexOfMethodRelative<MethodSignal>(baseObject, signal, normalizeStringData); 6 #ifndef QT_NO_DEBUG 7 const QMetaObject *m = *baseObject; 8 if (i >= 0 && m && m->d.superdata) { 9 int conflict = m->d.superdata->indexOfMethod(signal); 10 if (conflict >= 0) 11 qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", 12 signal, m->d.superdata->d.stringdata, m->d.stringdata); 13 } 14 #endif 15 return i; 16 }
1 template<int MethodType> 2 static inline int indexOfMethodRelative(const QMetaObject **baseObject, 3 const char *method, 4 bool normalizeStringData) 5 { 6 for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { 7 int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) 8 ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); 9 const int end = (MethodType == MethodSlot && priv(m->d.data)->revision >= 4) 10 ? (priv(m->d.data)->signalCount) : 0; 11 if (!normalizeStringData) { 12 for (; i >= end; --i) { 13 const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; 14 if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { 15 *baseObject = m; 16 return i; 17 } 18 } 19 } else if (priv(m->d.data)->revision < 5) { 20 for (; i >= end; --i) { 21 const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); 22 const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); 23 if (normalizedSignature == method) { 24 *baseObject = m; 25 return i; 26 } 27 } 28 } 29 } 30 return -1; 31 }
可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。
1 int indexOfSlot ( const char * slot ) const 略; 2 int propertyCount () const 略; 3 int propertyOffset () const 略; 4 int indexOfProperty ( const char * name ) const 略;
1 QMetaProperty QMetaObject::property(int index) const 2 { 3 int i = index; 4 i -= propertyOffset(); 5 if (i < 0 && d.superdata) 6 return d.superdata->property(index); 7 8 QMetaProperty result; 9 if (i >= 0 && i < priv(d.data)->propertyCount) { 10 int handle = priv(d.data)->propertyData + 3*i; 11 int flags = d.data[handle + 2]; 12 const char *type = d.stringdata + d.data[handle + 1]; 13 result.mobj = this; 14 result.handle = handle; 15 result.idx = i; 16 17 if (flags & EnumOrFlag) { 18 result.menum = enumerator(indexOfEnumerator(type)); 19 if (!result.menum.isValid()) { 20 QByteArray enum_name = type; 21 QByteArray scope_name = d.stringdata; 22 int s = enum_name.lastIndexOf("::"); 23 if (s > 0) { 24 scope_name = enum_name.left(s); 25 enum_name = enum_name.mid(s + 2); 26 } 27 const QMetaObject *scope = 0; 28 if (scope_name == "Qt") 29 scope = &QObject::staticQtMetaObject; 30 else 31 scope = QMetaObject_findMetaObject(this, scope_name); 32 if (scope) 33 result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name)); 34 } 35 } 36 } 37 return result; 38 }
该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。
1 QMetaProperty QMetaObject::userProperty() const 2 { 3 const int propCount = propertyCount(); 4 for (int i = propCount - 1; i >= 0; --i) { 5 const QMetaProperty prop = property(i); 6 if (prop.isUser()) 7 return prop; 8 } 9 return QMetaProperty(); 10 }
从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。
看一个整体例子
源码:
结果:
1 QT_BEGIN_MOC_NAMESPACE 2 static const uint qt_meta_data_MainWindow[] = { 3 4 // content: 5 6, // revision 6 0, // classname 7 2, 14, // classinfo 8 6, 18, // methods 9 1, 48, // properties 10 1, 51, // enums/sets 11 0, 0, // constructors 12 0, // flags 13 2, // signalCount 14 15 // classinfo: key, value 16 20, 11, 17 39, 27, 18 19 // signals: signature, parameters, type, tag, flags 20 48, 47, 47, 47, 0x05, 21 69, 62, 58, 47, 0x05, 22 23 // slots: signature, parameters, type, tag, flags 24 86, 47, 47, 47, 0x09, 25 100, 47, 47, 47, 0x08, 26 115, 47, 47, 47, 0x0a, 27 214, 138, 130, 47, 0x0a, 28 29 // properties: name, type, flags 30 290, 285, 0x01095003, 31 32 // enums: name, flags, count, data 33 302, 0x0, 2, 55, 34 35 // enum data: key, value 36 311, uint(MainWindow::EnumValueA), 37 322, uint(MainWindow::EnumValueB), 38 39 0 // eod 40 }; 41 42 static const char qt_meta_stringdata_MainWindow[] = { 43 "MainWindow\0lfsblack\0Author\0lfsblack-v1\0" 44 "Version\0\0clicked()\0int\0enable\0" 45 "JustLookIt(bool)\0slotNewFile()\0" 46 "slotOpenFile()\0slotSaveFile()\0QString\0" 47 "mbool,mInt,m_char,mString,mm_bool,intptr,charptr,boolptr,mmm,mmmmm,mmm" 48 "mmmmm\0" 49 "resultLook(bool,int,char,QString,bool,int*,char*,bool*,bool,bool,bool)\0" 50 "bool\0colorDialog\0TestEnum\0EnumValueA\0" 51 "EnumValueB\0" 52 };
所谓meta call就是通过object的meta system的支持来动态调用object的方法,metacall也是signal&slot的机制的基石。本篇通过参考源代码来探究meta call的实现方法
1 static bool invokeMethod(QObject *obj, const char *member, 2 Qt::ConnectionType, 3 QGenericReturnArgument ret, 4 QGenericArgument val0 = QGenericArgument(0), 5 QGenericArgument val1 = QGenericArgument(), 6 QGenericArgument val2 = QGenericArgument(), 7 QGenericArgument val3 = QGenericArgument(), 8 QGenericArgument val4 = QGenericArgument(), 9 QGenericArgument val5 = QGenericArgument(), 10 QGenericArgument val6 = QGenericArgument(), 11 QGenericArgument val7 = QGenericArgument(), 12 QGenericArgument val8 = QGenericArgument(), 13 QGenericArgument val9 = QGenericArgument()); 14 15 static inline bool invokeMethod(QObject *obj, const char *member, 16 QGenericReturnArgument ret, 17 QGenericArgument val0 = QGenericArgument(0), 18 QGenericArgument val1 = QGenericArgument(), 19 QGenericArgument val2 = QGenericArgument(), 20 QGenericArgument val3 = QGenericArgument(), 21 QGenericArgument val4 = QGenericArgument(), 22 QGenericArgument val5 = QGenericArgument(), 23 QGenericArgument val6 = QGenericArgument(), 24 QGenericArgument val7 = QGenericArgument(), 25 QGenericArgument val8 = QGenericArgument(), 26 QGenericArgument val9 = QGenericArgument()) 27 { 28 return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, 29 val4, val5, val6, val7, val8, val9); 30 } 31 32 static inline bool invokeMethod(QObject *obj, const char *member, 33 Qt::ConnectionType type, 34 QGenericArgument val0 = QGenericArgument(0), 35 QGenericArgument val1 = QGenericArgument(), 36 QGenericArgument val2 = QGenericArgument(), 37 QGenericArgument val3 = QGenericArgument(), 38 QGenericArgument val4 = QGenericArgument(), 39 QGenericArgument val5 = QGenericArgument(), 40 QGenericArgument val6 = QGenericArgument(), 41 QGenericArgument val7 = QGenericArgument(), 42 QGenericArgument val8 = QGenericArgument(), 43 QGenericArgument val9 = QGenericArgument()) 44 { 45 return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, 46 val3, val4, val5, val6, val7, val8, val9); 47 } 48 49 static inline bool invokeMethod(QObject *obj, const char *member, 50 QGenericArgument val0 = QGenericArgument(0), 51 QGenericArgument val1 = QGenericArgument(), 52 QGenericArgument val2 = QGenericArgument(), 53 QGenericArgument val3 = QGenericArgument(), 54 QGenericArgument val4 = QGenericArgument(), 55 QGenericArgument val5 = QGenericArgument(), 56 QGenericArgument val6 = QGenericArgument(), 57 QGenericArgument val7 = QGenericArgument(), 58 QGenericArgument val8 = QGenericArgument(), 59 QGenericArgument val9 = QGenericArgument()) 60 { 61 return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, 62 val1, val2, val3, val4, val5, val6, val7, val8, val9); 63 }
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,type参数表明该调用时同步的还是异步的。ret是一个 通用的用来存储返回值的类型,后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。(这里让人感觉 比较奇怪的是Qt为什么不将这个参数列表弄成某种动态的形式,而是最多九个)
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
1 bool QMetaObject::invokeMethod(QObject *obj, 2 const char *member, 3 Qt::ConnectionType type, 4 QGenericReturnArgument ret, 5 QGenericArgument val0, 6 QGenericArgument val1, 7 QGenericArgument val2, 8 QGenericArgument val3, 9 QGenericArgument val4, 10 QGenericArgument val5, 11 QGenericArgument val6, 12 QGenericArgument val7, 13 QGenericArgument val8, 14 QGenericArgument val9) 15 { 16 if (!obj) 17 return false; 18 19 QVarLengthArray<char, 512> sig; 20 int len = qstrlen(member); 21 if (len <= 0) 22 return false; 23 sig.append(member, len); 24 sig.append('('); 25 26 const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), 27 val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), 28 val9.name()}; 29 30 int paramCount; 31 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 32 len = qstrlen(typeNames[paramCount]); 33 if (len <= 0) 34 break; 35 sig.append(typeNames[paramCount], len); 36 sig.append(','); 37 } 38 if (paramCount == 1) 39 sig.append(')'); // no parameters 40 else 41 sig[sig.size() - 1] = ')'; 42 sig.append('\0'); 43 44 int idx = obj->metaObject()->indexOfMethod(sig.constData()); 45 if (idx < 0) { 46 QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); 47 idx = obj->metaObject()->indexOfMethod(norm.constData()); 48 } 49 50 if (idx < 0 || idx >= obj->metaObject()->methodCount()) { 51 qWarning("QMetaObject::invokeMethod: No such method %s::%s", 52 obj->metaObject()->className(), sig.constData()); 53 return false; 54 } 55 QMetaMethod method = obj->metaObject()->method(idx); 56 return method.invoke(obj, type, ret, 57 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 58 }
先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,这是运行时的行为,参数类型转换是编译时的行为。
然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个QMetaMethod值,再将调用委托给QMetaMethod::invoke方法。
1 bool QMetaMethod::invoke(QObject *object, 2 Qt::ConnectionType connectionType, 3 QGenericReturnArgument returnValue, 4 QGenericArgument val0, 5 QGenericArgument val1, 6 QGenericArgument val2, 7 QGenericArgument val3, 8 QGenericArgument val4, 9 QGenericArgument val5, 10 QGenericArgument val6, 11 QGenericArgument val7, 12 QGenericArgument val8, 13 QGenericArgument val9) const 14 { 15 if (!object || !mobj) 16 return false; 17 18 Q_ASSERT(mobj->cast(object)); 19 20 // check return type 21 if (returnValue.data()) { 22 const char *retType = typeName(); 23 if (qstrcmp(returnValue.name(), retType) != 0) { 24 // normalize the return value as well 25 // the trick here is to make a function signature out of the return type 26 // so that we can call normalizedSignature() and avoid duplicating code 27 QByteArray unnormalized; 28 int len = qstrlen(returnValue.name()); 29 30 unnormalized.reserve(len + 3); 31 unnormalized = "_("; // the function is called "_" 32 unnormalized.append(returnValue.name()); 33 unnormalized.append(')'); 34 35 QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData()); 36 normalized.truncate(normalized.length() - 1); // drop the ending ')' 37 38 if (qstrcmp(normalized.constData() + 2, retType) != 0) 39 return false; 40 } 41 } 42 43 // check argument count (we don't allow invoking a method if given too few arguments) 44 const char *typeNames[] = { 45 returnValue.name(), 46 val0.name(), 47 val1.name(), 48 val2.name(), 49 val3.name(), 50 val4.name(), 51 val5.name(), 52 val6.name(), 53 val7.name(), 54 val8.name(), 55 val9.name() 56 }; 57 int paramCount; 58 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 59 if (qstrlen(typeNames[paramCount]) <= 0) 60 break; 61 } 62 int metaMethodArgumentCount = 0; 63 { 64 // based on QMetaObject::parameterNames() 65 const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; 66 if (*names == 0) { 67 // do we have one or zero arguments? 68 const char *signature = mobj->d.stringdata + mobj->d.data[handle]; 69 while (*signature && *signature != '(') 70 ++signature; 71 if (*++signature != ')') 72 ++metaMethodArgumentCount; 73 } else { 74 --names; 75 do { 76 ++names; 77 while (*names && *names != ',') 78 ++names; 79 ++metaMethodArgumentCount; 80 } while (*names); 81 } 82 } 83 if (paramCount <= metaMethodArgumentCount) 84 return false; 85 86 // check connection type 87 QThread *currentThread = QThread::currentThread(); 88 QThread *objectThread = object->thread(); 89 if (connectionType == Qt::AutoConnection) { 90 connectionType = currentThread == objectThread 91 ? Qt::DirectConnection 92 : Qt::QueuedConnection; 93 } 94 95 #ifdef QT_NO_THREAD 96 if (connectionType == Qt::BlockingQueuedConnection) { 97 connectionType = Qt::DirectConnection; 98 } 99 #endif 100 101 // invoke! 102 void *param[] = { 103 returnValue.data(), 104 val0.data(), 105 val1.data(), 106 val2.data(), 107 val3.data(), 108 val4.data(), 109 val5.data(), 110 val6.data(), 111 val7.data(), 112 val8.data(), 113 val9.data() 114 }; 115 // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() 116 int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5); 117 int idx_offset = mobj->methodOffset(); 118 QObjectPrivate::StaticMetaCallFunction callFunction = 119 (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata) 120 ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0; 121 122 if (connectionType == Qt::DirectConnection) { 123 if (callFunction) { 124 callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param); 125 return true; 126 } else { 127 return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0; 128 } 129 } else if (connectionType == Qt::QueuedConnection) { 130 if (returnValue.data()) { 131 qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " 132 "queued connections"); 133 return false; 134 } 135 136 int nargs = 1; // include return type 137 void **args = (void **) qMalloc(paramCount * sizeof(void *)); 138 Q_CHECK_PTR(args); 139 int *types = (int *) qMalloc(paramCount * sizeof(int)); 140 Q_CHECK_PTR(types); 141 types[0] = 0; // return type 142 args[0] = 0; 143 144 for (int i = 1; i < paramCount; ++i) { 145 types[i] = QMetaType::type(typeNames[i]); 146 if (types[i]) { 147 args[i] = QMetaType::construct(types[i], param[i]); 148 ++nargs; 149 } else if (param[i]) { 150 qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", 151 typeNames[i]); 152 for (int x = 1; x < i; ++x) { 153 if (types[x] && args[x]) 154 QMetaType::destroy(types[x], args[x]); 155 } 156 qFree(types); 157 qFree(args); 158 return false; 159 } 160 } 161 162 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 163 0, -1, nargs, types, args)); 164 } else { // blocking queued connection 165 #ifndef QT_NO_THREAD 166 if (currentThread == objectThread) { 167 qWarning("QMetaMethod::invoke: Dead lock detected in " 168 "BlockingQueuedConnection: Receiver is %s(%p)", 169 mobj->className(), object); 170 } 171 172 QSemaphore semaphore; 173 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 174 0, -1, 0, 0, param, &semaphore)); 175 semaphore.acquire(); 176 #endif // QT_NO_THREAD 177 } 178 return true; 179 }
代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,看懂这段代码需要参考该系列之二对moc文件的解析;再依据当前线程和被调对象所属 线程来调整connnection type;如果是directconnection,直接调用 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param),param是将所有参数值指针排列组成的指针数组。如果不是directconnection,也即异步调用,就通过一个post一个 QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。
QMetaObject::metacall的实现如下:
1 int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) 2 { 3 if (QMetaObject *mo = object->d_ptr->metaObject) 4 return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv); 5 else 6 return object->qt_metacall(cl, idx, argv); 7 }
如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject 来调用,这里要参考该系列之三对QMetaObjectPrivate的介绍,这个条件实际上就是object就是QObject类型,而不是派生类型。 否则调用object::qt_metacall。
对于异步调用,QObject的event函数里有如下代码:
1 class Q_CORE_EXPORT QMetaCallEvent : public QEvent 2 { 3 public: 4 QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject *sender, int signalId, 5 int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0); 6 ~QMetaCallEvent(); 7 8 inline int id() const { return method_offset_ + method_relative_; } 9 inline const QObject *sender() const { return sender_; } 10 inline int signalId() const { return signalId_; } 11 inline void **args() const { return args_; } 12 13 virtual void placeMetaCall(QObject *object); 14 15 private: 16 const QObject *sender_; 17 int signalId_; 18 int nargs_; 19 int *types_; 20 void **args_; 21 QSemaphore *semaphore_; 22 QObjectPrivate::StaticMetaCallFunction callFunction_; 23 ushort method_offset_; 24 ushort method_relative_; 25 };
1 class Q_CORE_EXPORT QObjectPrivate : public QObjectData 2 { 3 Q_DECLARE_PUBLIC(QObject) 4 5 public: 6 struct ExtraData 7 { 8 ExtraData() {} 9 #ifndef QT_NO_USERDATA 10 QVector<QObjectUserData *> userData; 11 #endif 12 QList<QByteArray> propertyNames; 13 QList<QVariant> propertyValues; 14 }; 15 16 typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); 17 struct Connection 18 { 19 QObject *sender; 20 QObject *receiver; 21 StaticMetaCallFunction callFunction; 22 // The next pointer for the singly-linked ConnectionList 23 Connection *nextConnectionList; 24 //senders linked list 25 Connection *next; 26 Connection **prev; 27 QBasicAtomicPointer<int> argumentTypes; 28 ushort method_offset; 29 ushort method_relative; 30 ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking 31 ~Connection(); 32 int method() const { return method_offset + method_relative; } 33 }; 34 // ConnectionList is a singly-linked list 35 struct ConnectionList { 36 ConnectionList() : first(0), last(0) {} 37 Connection *first; 38 Connection *last; 39 }; 40 41 struct Sender 42 { 43 QObject *sender; 44 int signal; 45 int ref; 46 };
1 case QEvent::MetaCall: 2 { 3 #ifdef QT_JAMBI_BUILD 4 d_func()->inEventHandler = false; 5 #endif 6 QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e); 7 QObjectPrivate::Sender currentSender; 8 currentSender.sender = const_cast<QObject*>(mce->sender()); 9 currentSender.signal = mce->signalId(); 10 currentSender.ref = 1; 11 QObjectPrivate::Sender * const previousSender = 12 QObjectPrivate::setCurrentSender(this, ¤tSender); 13 #if defined(QT_NO_EXCEPTIONS) 14 mce->placeMetaCall(this); 15 #else 16 QT_TRY { 17 mce->placeMetaCall(this); 18 } QT_CATCH(...) { 19 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 20 QT_RETHROW; 21 } 22 #endif 23 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 24 break; 25 }
QMetaCallEvent的代码很简单:
1 void QMetaCallEvent::placeMetaCall(QObject *object) 2 { 3 if (callFunction_) { 4 callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_); 5 } else { 6 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_); 7 } 8 }
殊途同归。
最后来看一下object->qt_metacall是如何实现的,这又回到了上面所提供的示例moc文件中去了。该文件提供了该方法的实现:
1 int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) 2 # { 3 # _id = QObject::qt_metacall(_c, _id, _a); 4 # if (_id < 0) 5 # return _id; 6 # if (_c == QMetaObject::InvokeMetaMethod) { 7 # switch (_id) { 8 # case 0: clicked(); break; 9 # case 1: pressed(); break; 10 # case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break; 11 # case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break; 12 # default: ; 13 # } 14 # _id -= 4; 15 # } 16 # #ifndef QT_NO_PROPERTIES 17 # else if (_c == QMetaObject::ReadProperty) { 18 # void *_v = _a[0]; 19 # switch (_id) { 20 # case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break; 21 # case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break; 22 # } 23 # _id -= 2; 24 # } else if (_c == QMetaObject::WriteProperty) { 25 # void *_v = _a[0]; 26 # switch (_id) { 27 # case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break; 28 # case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break; 29 # } 30 # _id -= 2; 31 # } else if (_c == QMetaObject::ResetProperty) { 32 # switch (_id) { 33 # case 0: resetPropertyA(); break; 34 # case 1: resetPropertyB(); break; 35 # } 36 # _id -= 2; 37 # } else if (_c == QMetaObject::QueryPropertyDesignable) { 38 # _id -= 2; 39 # } else if (_c == QMetaObject::QueryPropertyScriptable) { 40 # _id -= 2; 41 # } else if (_c == QMetaObject::QueryPropertyStored) { 42 # _id -= 2; 43 # } else if (_c == QMetaObject::QueryPropertyEditable) { 44 # _id -= 2; 45 # } else if (_c == QMetaObject::QueryPropertyUser) { 46 # _id -= 2; 47 # } 48 # #endif // QT_NO_PROPERTIES 49 # return _id; 50 # }
这段代码将调用最终转到我们自己的实现的函数中来。这个函数不经提供了metamethod的动态调用,而且也提供了property的动态操作方法。可想而知,property的动态调用的实现方式一定和invocalbe method是一致的。
在qobjectdefs.h中有这样的定义:
1 # define METHOD(a) "0"#a 2 # define SLOT(a) "1"#a 3 # define SIGNAL(a) "2"#a
不过是在方法签名之前加了一个数字标记。因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。
QObject::connect()
1 bool QObject::connect(const QObject *sender, const char *signal, 2 const QObject *receiver, const char *method, 3 Qt::ConnectionType type) 4 { 5 { 6 const void *cbdata[] = { sender, signal, receiver, method, &type }; 7 if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) 8 return true; 9 } 10 11 #ifndef QT_NO_DEBUG 12 bool warnCompat = true; 13 #endif 14 if (type == Qt::AutoCompatConnection) { 15 type = Qt::AutoConnection; 16 #ifndef QT_NO_DEBUG 17 warnCompat = false; 18 #endif 19 } 20 21 if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { 22 qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", 23 sender ? sender->metaObject()->className() : "(null)", 24 (signal && *signal) ? signal+1 : "(null)", 25 receiver ? receiver->metaObject()->className() : "(null)", 26 (method && *method) ? method+1 : "(null)"); 27 return false; 28 } 29 QByteArray tmp_signal_name; 30 31 if (!check_signal_macro(sender, signal, "connect", "bind")) 32 return false; 33 const QMetaObject *smeta = sender->metaObject(); 34 const char *signal_arg = signal; 35 ++signal; //skip code 36 int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); 37 if (signal_index < 0) { 38 // check for normalized signatures 39 tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); 40 signal = tmp_signal_name.constData() + 1; 41 42 smeta = sender->metaObject(); 43 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); 44 } 45 if (signal_index < 0) { 46 // re-use tmp_signal_name and signal from above 47 48 smeta = sender->metaObject(); 49 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); 50 } 51 if (signal_index < 0) { 52 err_method_notfound(sender, signal_arg, "connect"); 53 err_info_about_objects("connect", sender, receiver); 54 return false; 55 } 56 signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); 57 int signalOffset, methodOffset; 58 computeOffsets(smeta, &signalOffset, &methodOffset); 59 int signal_absolute_index = signal_index + methodOffset; 60 signal_index += signalOffset; 61 62 QByteArray tmp_method_name; 63 int membcode = extract_code(method); 64 65 if (!check_method_code(membcode, receiver, method, "connect")) 66 return false; 67 const char *method_arg = method; 68 ++method; // skip code 69 70 const QMetaObject *rmeta = receiver->metaObject(); 71 int method_index_relative = -1; 72 switch (membcode) { 73 case QSLOT_CODE: 74 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 75 break; 76 case QSIGNAL_CODE: 77 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 78 break; 79 } 80 81 if (method_index_relative < 0) { 82 // check for normalized methods 83 tmp_method_name = QMetaObject::normalizedSignature(method); 84 method = tmp_method_name.constData(); 85 86 // rmeta may have been modified above 87 rmeta = receiver->metaObject(); 88 switch (membcode) { 89 case QSLOT_CODE: 90 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 91 if (method_index_relative < 0) 92 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); 93 break; 94 case QSIGNAL_CODE: 95 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 96 if (method_index_relative < 0) 97 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); 98 break; 99 } 100 } 101 102 if (method_index_relative < 0) { 103 err_method_notfound(receiver, method_arg, "connect"); 104 err_info_about_objects("connect", sender, receiver); 105 return false; 106 } 107 108 if (!QMetaObject::checkConnectArgs(signal, method)) { 109 qWarning("QObject::connect: Incompatible sender/receiver arguments" 110 "\n %s::%s --> %s::%s", 111 sender->metaObject()->className(), signal, 112 receiver->metaObject()->className(), method); 113 return false; 114 } 115 116 int *types = 0; 117 if ((type == Qt::QueuedConnection) 118 && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) 119 return false; 120 121 #ifndef QT_NO_DEBUG 122 if (warnCompat) { 123 QMetaMethod smethod = smeta->method(signal_absolute_index); 124 QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset()); 125 check_and_warn_compat(smeta, smethod, rmeta, rmethod); 126 } 127 #endif 128 if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)) 129 return false; 130 const_cast<QObject*>(sender)->connectNotify(signal - 1); 131 return true; 132 }
忽略细节,只关注主要的流程,这段代码的做的事情就是将signal在sender的meta system中的signal索引找出,以及接受者方法(signal或slot)在receiver的meta
system中的索引找出来。在委托QMetaObjectPrivate::connect()执行连接。
1 bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, 2 const QObject *receiver, int method_index, 3 const QMetaObject *rmeta, int type, int *types) 4 { 5 QObject *s = const_cast<QObject *>(sender); 6 QObject *r = const_cast<QObject *>(receiver); 7 8 int method_offset = rmeta ? rmeta->methodOffset() : 0; 9 QObjectPrivate::StaticMetaCallFunction callFunction = 10 (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata) 11 ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0; 12 13 QOrderedMutexLocker locker(signalSlotLock(sender), 14 signalSlotLock(receiver)); 15 16 if (type & Qt::UniqueConnection) { 17 QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; 18 if (connectionLists && connectionLists->count() > signal_index) { 19 const QObjectPrivate::Connection *c2 = 20 (*connectionLists)[signal_index].first; 21 22 int method_index_absolute = method_index + method_offset; 23 24 while (c2) { 25 if (c2->receiver == receiver && c2->method() == method_index_absolute) 26 return false; 27 c2 = c2->nextConnectionList; 28 } 29 } 30 type &= Qt::UniqueConnection - 1; 31 } 32 33 QObjectPrivate::Connection *c = new QObjectPrivate::Connection; 34 c->sender = s; 35 c->receiver = r; 36 c->method_relative = method_index; 37 c->method_offset = method_offset; 38 c->connectionType = type; 39 c->argumentTypes = types; 40 c->nextConnectionList = 0; 41 c->callFunction = callFunction; 42 43 QT_TRY { 44 QObjectPrivate::get(s)->addConnection(signal_index, c); 45 } QT_CATCH(...) { 46 delete c; 47 QT_RETHROW; 48 } 49 50 c->prev = &(QObjectPrivate::get(r)->senders); 51 c->next = *c->prev; 52 *c->prev = c; 53 if (c->next) 54 c->next->prev = &c->next; 55 56 QObjectPrivate *const sender_d = QObjectPrivate::get(s); 57 if (signal_index < 0) { 58 sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; 59 } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { 60 sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); 61 } 62 63 return true; 64 }
同样忽略细节,这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。然后创建一个
QObjectPrivate::Connection结构,这个结构包含了sender,receiver,接受方法的method_index,然后
加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。可以推测,当signal方法被调用时,一定会到这个
结构中查找所有连接到该signal的connection,connection保存了receiver和method_index,由上一篇可知,可以很容易调用到receiver的对应method。