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, &currentSender);
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, &currentSender, previousSender);
20                 QT_RETHROW;
21             }
22 #endif
23             QObjectPrivate::resetCurrentSender(this, &currentSender, 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。

posted @ 2016-03-17 11:25  lfsblack  阅读(2546)  评论(0编辑  收藏  举报