网上的资源比较乱,该文章整理自地址: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。

转自:http://www.cnblogs.com/lfsblack/p/5286654.html

7、Qt MetaObject System详解的更多相关文章

  1. Qt MetaObject System详解

    网上的资源比较乱,该文章整理自地址:http://www.xuebuyuan.com/735789.html Qt meta-object系统基于三个方面: 1.QObject提供一个基类,方便派生类 ...

  2. QT信号槽详解

    1         QT信号槽详解 1.1  信号和槽的定义 信号是触发信号,例如按钮的点击触发一个clicked信号,槽是用来接收信号,并处理信号,相当于信号响应函数.一个信号可以关联多个槽函数,信 ...

  3. Qt .pro文件 详解

    1. TEMPLATE变量TEMPLATE描述了为建立目标文件而采用何种模板,即生成何种形式的Makefile文件.Qmake工具定义了5种模板:1. 应用程序App,为建立一个Qt应用程序创建Mak ...

  4. 【Win10+eclipse+MinGW+QT安装教程】已有eclipse环境下配置QT插件出错详解

    (纪念一下装了一整天濒临绝望的自己[/doge]) (以下所有下载项一律使用32位) step1:下载安装eclipse.适配的jdk.MinGW和QT 1.下载32位eclipse for c++以 ...

  5. Qt .pro文件详解

    在我们创建Qt工程项目时,Qt Creator总会创建一个.pro文件,我们称.pro文件为Qt的工程管理文件.一个工程项目可以包含一个或多个.pro文件.理解和掌握pro文件的用法,将有利于Qt开发 ...

  6. bootstrap中的Grid system详解

    啦啦啦,都十月中旬啦,好快啊,这个月的多一半都过去了,然而我才写第三篇随笔,小颖得加油啦~~~ 下面来看下小颖给大家分享的内容 1.   .col-md-*和.col-xs-* <!doctyp ...

  7. Qt零基础教程(四) QWidget详解篇

    在博客园里面转载我自己写的关于Qt的基础教程,没次写一篇我会在这里更新一下目录: Qt零基础教程(四) QWidget详解(1):创建一个窗口 Qt零基础教程(四) QWidget详解(2):QWid ...

  8. Qt零基础教程(四)QWidget详解(3):QWidget的几何结构

    Qt零基础教程(四)  QWidget详解(3):QWidget的几何结构 这篇文章里面分析了QWidget中常用的几种几何结构 下图是Qt提供的分析QWidget几何结构的一幅图,在帮助的 Wind ...

  9. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

随机推荐

  1. 解决Maven->update project 恢复为默认jdk1.5以及One or more constraints have not been satisfied问题

    使用maven架构创建dynamic web 项目之后,默认指定的jdk 和compilerVersion都非常古老,而且即便你手动更新了版本之后,每次update project都会复位,非常不爽. ...

  2. 哈佛大学 Machine Learning

    https://am207.github.io/2017/material.html https://am207.github.io/2017/topics.html https://am207.gi ...

  3. Recommender Systems中Yehuda Koren 和 Ma Hao的paper

    以前读了Yehuda Koren和Ma Hao的论文,感觉非常不错,这里分享一下.如果想着具体了解他们近期发的论文,可以去DBLP去看看. Yehuda Koren也是Netflix Prize的冠军 ...

  4. linux Ctrl+z和Ctrl+c的区别

    1.Ctrl+z 挂起进程,并不会结束,执行fg命令可以重新启动这个被挂起的命令. 2.Ctrl+c 终止进程

  5. Android 编程下 Eclipse 恢复被删除的文件

    开发过程中文件误删除,又没有 Git,SVN 等版本控制软件的备份,怎么办? Eclipse 自带了恢复历史文件的功能:在项目上点击右键,选择 Restore from Local History,在 ...

  6. 【FastDFS】FastDFS在CentOS的搭建

    准备安装软件 [root@blog third_package]# cp fastdfs-nginx-module_v1.16.tar.gz FastDFS_v5.08.tar.gz libfastc ...

  7. C# IOCP服务器项目(学习)

    无论什么平台,编写支持高并发性的网络服务器,瓶颈往往出在I/O上,目前最高效的是采用Asynchronous I/O模型,Linux平台提供了epoll,Windows平台提供了I/O Complet ...

  8. DLL中获取主程序句柄

    一.问题由来及说明 在实际编程中遇到一些系统模块(或API)调用初始化需要窗口句柄,在主程序中(MFC或者win32)都可以很容易获得,比如mfc中可以直接调用AfxGetMainWnd().GetS ...

  9. JS实现获取当前URL和来源URL的方法

    通用模式: Javascript 正常取来源网页的URL只要用: index.html: <!DOCTYPE html> <html lang="zh-cn"&g ...

  10. LeetCode: Best Time to Buy and Sell Stock 解题报告

    Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of a gi ...