QT6 源码杂记
阅读原文时间:2021年11月14日阅读:1

菜鸡一个,随便写写,勿喷。好记性不如烂笔头。

了解qt,第一个绕不过的坎便是qt的元对象系统 QMetaObject。

1 class Object : public QObject
2 {
3 Q_OBJECT
4 Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
5 Q_PROPERTY(int score READ score WRITE setScore NOTIFY scoreChanged)
6 Q_CLASSINFO("Author", "Scorpio")
7 Q_CLASSINFO("Version", "2.0")
8 Q_CLASSINFO("Department", "wk")
9 Q_ENUMS(Level)
10 protected:
11 static const MyStruct sStruct;
12 QString m_name;
13 QString m_level;
14 int m_age;
15 int m_score;
16 public:
17 enum Level
18 {
19 Basic,
20 Middle,
21 Advanced
22 };
23 public:
24 Q_INVOKABLE explicit Object(QString name, QObject* parent = 0) :QObject(parent)
25 {
26 m_name = name;
27 setObjectName(m_name);
28 connect(this, &Object::ageChanged, this, &Object::onAgeChanged);
29 //connect(this, &Object::ageChanged, this, &Object::onScoreChanged);
30 connect(this, &Object::scoreChanged, this, &Object::onScoreChanged);
31 }
32
33 int age()const
34 {
35 return m_age;
36 }
37
38 Q_INVOKABLE void setAge(const int& age)
39 {
40 m_age = age;
41 emit ageChanged(m_age);
42 }
43
44 Q_INVOKABLE int score()const
45 {
46 return m_score;
47 }
48
49 Q_INVOKABLE void setScore(const int& score)
50 {
51 m_score = score;
52 emit scoreChanged(m_score);
53 }
54 signals:
55 void ageChanged(int age);
56 void scoreChanged(int score);
57 public slots:
58
59 void onAgeChanged(int age)
60 {
61 //QObjectPrivate* p = static_cast (&(*d_ptr));
62 //p->receiverList(SIGNAL(ageChanged));
63 //d_ptr->
64 qDebug() << "age changed:" << age;
65 }
66 void onScoreChanged(int score)
67 {
68 qDebug() << "score changed:" << score;
69 }
70 };

通常继承qt的类,都会继承于QObject. 在类里添加一句 Q_OBJECT宏。如下所示,是qt信号槽的关键。

1 #define Q_OBJECT \
2 public: \
3 QT_WARNING_PUSH \
4 Q_OBJECT_NO_OVERRIDE_WARNING \
5 static const QMetaObject staticMetaObject; \
6 virtual const QMetaObject *metaObject() const; \
7 virtual void *qt_metacast(const char *); \
8 virtual int qt_metacall(QMetaObject::Call, int, void **); \
9 QT_TR_FUNCTIONS \
10 private: \
11 Q_OBJECT_NO_ATTRIBUTES_WARNING \
12 Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
13 QT_WARNING_POP \
14 struct QPrivateSignal {}; \
15 QT_ANNOTATE_CLASS(qt_qobject, "")

要想编译qt相关类,少不了moc工具。可以理解为qt的预编译工具,moc工具会解析具有Q_OBJECT宏的类,生成对应的moc_xx.cpp文件,该文件会随着项目一起编译。

1 /****************************************************************************
2 ** Meta object code from reading C++ file 'Object.h'
3 **
4 ** Created by: The Qt Meta Object Compiler version 68 (Qt 6.0.0)
5 **
6 ** WARNING! All changes made in this file will be lost!
7 *****************************************************************************/
8
9 #include
10 #include "../../../Object.h"
11 #include
12 #include
13 #if !defined(Q_MOC_OUTPUT_REVISION)
14 #error "The header file 'Object.h' doesn't include ."
15 #elif Q_MOC_OUTPUT_REVISION != 68
16 #error "This file was generated using the moc from 6.0.0. It"
17 #error "cannot be used with the include files from this version of Qt."
18 #error "(The moc has changed too much.)"
19 #endif
20
21 QT_BEGIN_MOC_NAMESPACE
22 QT_WARNING_PUSH
23 QT_WARNING_DISABLE_DEPRECATED
24 struct qt_meta_stringdata_Object_t {
25 const uint offsetsAndSize[44];
26 char stringdata0[167];
27 };
28 #define QT_MOC_LITERAL(ofs, len) \
29 uint(offsetof(qt_meta_stringdata_Object_t, stringdata0) + ofs), len
30 static const qt_meta_stringdata_Object_t qt_meta_stringdata_Object = {
31 {
32 QT_MOC_LITERAL(0, 6), // "Object"
33 QT_MOC_LITERAL(7, 6), // "Author"
34 QT_MOC_LITERAL(14, 7), // "Scorpio"
35 QT_MOC_LITERAL(22, 7), // "Version"
36 QT_MOC_LITERAL(30, 3), // "2.0"
37 QT_MOC_LITERAL(34, 10), // "Department"
38 QT_MOC_LITERAL(45, 2), // "wk"
39 QT_MOC_LITERAL(48, 10), // "ageChanged"
40 QT_MOC_LITERAL(59, 0), // ""
41 QT_MOC_LITERAL(60, 3), // "age"
42 QT_MOC_LITERAL(64, 12), // "scoreChanged"
43 QT_MOC_LITERAL(77, 5), // "score"
44 QT_MOC_LITERAL(83, 12), // "onAgeChanged"
45 QT_MOC_LITERAL(96, 14), // "onScoreChanged"
46 QT_MOC_LITERAL(111, 6), // "setAge"
47 QT_MOC_LITERAL(118, 8), // "setScore"
48 QT_MOC_LITERAL(127, 4), // "name"
49 QT_MOC_LITERAL(132, 6), // "parent"
50 QT_MOC_LITERAL(139, 5), // "Level"
51 QT_MOC_LITERAL(145, 5), // "Basic"
52 QT_MOC_LITERAL(151, 6), // "Middle"
53 QT_MOC_LITERAL(158, 8) // "Advanced"
54
55 },
56 "Object\0Author\0Scorpio\0Version\0""2.0\0"
57 "Department\0wk\0ageChanged\0\0age\0"
58 "scoreChanged\0score\0onAgeChanged\0"
59 "onScoreChanged\0setAge\0setScore\0name\0"
60 "parent\0Level\0Basic\0Middle\0Advanced"
61 };
62 #undef QT_MOC_LITERAL
63
64 static const uint qt_meta_data_Object[] = {
65
66 // content:
67 9, // revision
68 0, // classname
69 3, 14, // classinfo
70 7, 20, // methods
71 2, 89, // properties
72 1, 99, // enums/sets
73 2, 110, // constructors
74 0, // flags
75 2, // signalCount
76
77 // classinfo: key, value
78 1, 2,
79 3, 4,
80 5, 6,
81
82 // signals: name, argc, parameters, tag, flags, initial metatype offsets
83 7, 1, 62, 8, 0x06, 2 /* Public */,
84 10, 1, 65, 8, 0x06, 4 /* Public */,
85
86 // slots: name, argc, parameters, tag, flags, initial metatype offsets
87 12, 1, 68, 8, 0x0a, 6 /* Public */,
88 13, 1, 71, 8, 0x0a, 8 /* Public */,
89
90 // methods: name, argc, parameters, tag, flags, initial metatype offsets
91 14, 1, 74, 8, 0x02, 10 /* Public */,
92 11, 0, 77, 8, 0x02, 12 /* Public */,
93 15, 1, 78, 8, 0x02, 13 /* Public */,
94
95 // signals: parameters
96 QMetaType::Void, QMetaType::Int, 9,
97 QMetaType::Void, QMetaType::Int, 11,
98
99 // slots: parameters
100 QMetaType::Void, QMetaType::Int, 9,
101 QMetaType::Void, QMetaType::Int, 11,
102
103 // methods: parameters
104 QMetaType::Void, QMetaType::Int, 9,
105 QMetaType::Int,
106 QMetaType::Void, QMetaType::Int, 11,
107
108 // constructors: parameters
109 0x80000000 | 8, QMetaType::QString, QMetaType::QObjectStar, 16, 17,
110 0x80000000 | 8, QMetaType::QString, 16,
111
112 // properties: name, type, flags
113 9, QMetaType::Int, 0x00015103, uint(0), 0,
114 11, QMetaType::Int, 0x00015103, uint(1), 0,
115
116 // enums: name, alias, flags, count, data
117 18, 18, 0x0, 3, 104,
118
119 // enum data: key, value
120 19, uint(Object::Basic),
121 20, uint(Object::Middle),
122 21, uint(Object::Advanced),
123
124 // constructors: name, argc, parameters, tag, flags, initial metatype offsets
125 0, 2, 81, 8, 0x0e, 15 /* Public */,
126 0, 1, 86, 8, 0x2e, 17 /* Public | MethodCloned */,
127
128 0 // eod
129 };
130
131 void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
132 {
133 if (_c == QMetaObject::CreateInstance) {
134 switch (_id) {
135 case 0: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< QObject*(*)>(_a[2])));
136 if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break;
137 case 1: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])));
138 if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break;
139 default: break;
140 }
141 } else if (_c == QMetaObject::InvokeMetaMethod) {
142 auto *_t = static_cast(_o);
143 (void)_t;
144 switch (_id) {
145 case 0: _t->ageChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
146 case 1: _t->scoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
147 case 2: _t->onAgeChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
148 case 3: _t->onScoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
149 case 4: _t->setAge((*reinterpret_cast< const int(*)>(_a[1]))); break;
150 case 5: { int _r = _t->score();
151 if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
152 case 6: _t->setScore((*reinterpret_cast< const int(*)>(_a[1]))); break;
153 default: ;
154 }
155 } else if (_c == QMetaObject::IndexOfMethod) {
156 int *result = reinterpret_cast(_a[0]);
157 {
158 using _t = void (Object::*)(int );
159 if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::ageChanged)) {
160 *result = 0;
161 return;
162 }
163 }
164 {
165 using _t = void (Object::*)(int );
166 if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::scoreChanged)) {
167 *result = 1;
168 return;
169 }
170 }
171 }
172 #ifndef QT_NO_PROPERTIES
173 else if (_c == QMetaObject::ReadProperty) {
174 auto *_t = static_cast(_o);
175 (void)_t;
176 void *_v = _a[0];
177 switch (_id) {
178 case 0: *reinterpret_cast< int*>(_v) = _t->age(); break;
179 case 1: *reinterpret_cast< int*>(_v) = _t->score(); break;
180 default: break;
181 }
182 } else if (_c == QMetaObject::WriteProperty) {
183 auto *_t = static_cast(_o);
184 (void)_t;
185 void *_v = _a[0];
186 switch (_id) {
187 case 0: _t->setAge(*reinterpret_cast< int*>(_v)); break;
188 case 1: _t->setScore(*reinterpret_cast< int*>(_v)); break;
189 default: break;
190 }
191 } else if (_c == QMetaObject::ResetProperty) {
192 } else if (_c == QMetaObject::BindableProperty) {
193 }
194 #endif // QT_NO_PROPERTIES
195 }
196
197 const QMetaObject Object::staticMetaObject = { {
198 QMetaObject::SuperData::link(),
199 qt_meta_stringdata_Object.offsetsAndSize,
200 qt_meta_data_Object,
201 qt_static_metacall,
202 nullptr,
203 qt_incomplete_metaTypeArray, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
205 , QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
206 , QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
207 , QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
208 >,
209 nullptr
210 } };
211
212
213 const QMetaObject *Object::metaObject() const
214 {
215 return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
216 }
217
218 void *Object::qt_metacast(const char *_clname)
219 {
220 if (!_clname) return nullptr;
221 if (!strcmp(_clname, qt_meta_stringdata_Object.stringdata0))
222 return static_cast(this);
223 return QObject::qt_metacast(_clname);
224 }
225
226 int Object::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
227 {
228 _id = QObject::qt_metacall(_c, _id, _a);
229 if (_id < 0) 230 return _id; 231 if (_c == QMetaObject::InvokeMetaMethod) { 232 if (_id < 7) 233 qt_static_metacall(this, _c, _id, _a); 234 _id -= 7; 235 } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { 236 if (_id < 7) 237 *reinterpret_cast(_a[0]) = QMetaType();
238 _id -= 7;
239 }
240 #ifndef QT_NO_PROPERTIES
241 else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
242 || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty
243 || _c == QMetaObject::RegisterPropertyMetaType) {
244 qt_static_metacall(this, _c, _id, _a);
245 _id -= 2;
246 }
247 #endif // QT_NO_PROPERTIES
248 return _id;
249 }
250
251 // SIGNAL 0
252 void Object::ageChanged(int _t1)
253 {
254 void *_a[] = { nullptr, const_cast(reinterpret_cast(std::addressof(_t1))) };
255 QMetaObject::activate(this, &staticMetaObject, 0, _a);
256 }
257
258 // SIGNAL 1
259 void Object::scoreChanged(int _t1)
260 {
261 void *_a[] = { nullptr, const_cast(reinterpret_cast(std::addressof(_t1))) };
262 QMetaObject::activate(this, &staticMetaObject, 1, _a);
263 }
264 QT_WARNING_POP
265 QT_END_MOC_NAMESPACE

QMetaObject 的主要数据

1 struct Q_CORE_EXPORT QMetaObject
2 {
3 …
4 struct Data { // private data
5 SuperData superdata;
6 const uint *stringdata;
7 const uint *data;
8 typedef void (*StaticMetacallFunction)(QObject *,
9 QMetaObject::Call, int, void **);
10 StaticMetacallFunction static_metacall;
11 const SuperData *relatedMetaObjects;
12 const QtPrivate::QMetaTypeInterface *const *metaTypes;
13 void *extradata; //reserved for future use
14 } d;
15 …
16 }

struct qt_meta_stringdata_Object_t {
  const uint offsetsAndSize[44];
  char stringdata0[167];
};

44对应 多少项 ,有22个QT_MOC_LITERAL宏,展开之后有44项,记录了类的所有元对象信息。 第一个宏代表的是类名,offsetof 用来查询结构体内的成员的偏移地址,类名Object的偏移地址是4*44 = 176, 6代表Object的长度。依次类推,注意QT_MOC_LITERAL(59, 0) 是个空,这是由构造函数前的宏造成的,Q_INVOKABLE,会被记录元信息,至于这个空代表什么,目前不知道啥意思。stringdata0[167] 则是这些字符串的数量。

  接下来看下qt_meta_data_Object[],在看这个之前,先给出QMetaObjectPrivate的定义:

struct QMetaObjectPrivate
{
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };

int revision;  
int className;  
int classInfoCount, classInfoData;  
int methodCount, methodData;  
int propertyCount, propertyData;  
int enumeratorCount, enumeratorData;  
int constructorCount, constructorData;  
int flags;  
int signalCount;  

………..
}

qt_meta_data_Object[]中的第一项9,代表版本,QT6.0,第2项0,对应qt_meta_stringdata_Object[0] ,恰好对应类名,14对应本身数组qt_meta_data_Object[14]的位置:

// classinfo: key, value
1, 2, //stringdata0[167], 猜测第一个\0对应的Author为key,以及第二个\0对应的Scorpio为value.
3, 4,
5, 6,

8对应8个成员方法,20同理对应位置qt_meta_data_Object[20], 8个方法由2个signals, 2个slots, 4个methods组成。

2, 97 //对应两个属性,数组97项所在的位置;

1, 107// 对应一个enum:  Level,  数组107所在的位置.

3,  118, // constructors 。3对应3种不同的传参方法,因为俩个都是默认构造函数

0,       // flags 这个不知道啥意思。

2,       // signalCount 两个信号;

// signals: name, argc, parameters, tag, flags, initial metatype offsets
7, 1, 68, 8, 0x06, 2 /* Public */,
10, 1, 71, 8, 0x06, 4 /* Public */,

7对应第7个\0对应的ageChanged, 1个参数, paremeters对应68不知道什么意思,tag,flags,offsets目前都不知道,估计只能看qmoc源码才能知道了。

剩下的大概自己过一眼了解下。太细究的话也得不到什么好处。

const QMetaObject Object::staticMetaObject = { {
QMetaObject::SuperData::link(),
qt_meta_stringdata_Object.offsetsAndSize,
qt_meta_data_Object,
qt_static_metacall,
nullptr,
qt_incomplete_metaTypeArray, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete

,
nullptr
} };

这个staticMetaObject便是Q_Object宏里定义的,是个静态变量,程序运行时会首先初始化它。

struct Data { // private data
SuperData superdata;
const uint *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const SuperData *relatedMetaObjects;
const QtPrivate::QMetaTypeInterface *const *metaTypes;
void *extradata; //reserved for future use
} d;

以上是QMetaObject的数据定义,可以看出实际是在初始化d,  superdata 是基类QOjbect的staticMetaobject,

SuperData superdata : QOjbect的staticMetaobject

const uint *stringdata: qt_meta_stringdata_Object.offsetsAndSize// 并没有直接指向stringdata0[167]。

const uint *data :qt_meta_data_Object

StaticMetacallFunction static_metacall: qt_static_metacall

const SuperData *relatedMetaObjects: nullptr;

const QtPrivate::QMetaTypeInterface *const *metaTypes: ….这个有点晕。模板元编程,萃取类型metaType信息

extradata:nullptr

template
struct QMetaTypeInterfaceWrapper
{
static inline constexpr const QMetaTypeInterface metaType = {
/*.revision=*/ 0,
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),
/*.flags=*/ QMetaTypeTypeFlags::Flags,
/*.typeId=*/ BuiltinMetaType::value,
/*.metaObjectFn=*/ MetaObjectForType::metaObjectFunction,
/*.name=*/ QMetaTypeForType::getName(),
/*.defaultCtr=*/ QMetaTypeForType::getDefaultCtr(),
/*.copyCtr=*/ QMetaTypeForType::getCopyCtr(),
/*.moveCtr=*/ QMetaTypeForType::getMoveCtr(),
/*.dtor=*/ QMetaTypeForType::getDtor(),
/*.equals=*/ QEqualityOperatorForType::equals,
/*.lessThan=*/ QLessThanOperatorForType::lessThan,
/*.debugStream=*/ QDebugStreamOperatorForType::debugStream,
/*.dataStreamOut=*/ QDataStreamOperatorForType::dataStreamOut,
/*.dataStreamIn=*/ QDataStreamOperatorForType::dataStreamIn,
/*.legacyRegisterOp=*/ QMetaTypeForType::getLegacyRegister()
};
};

咱也不知道它是干嘛的,模板元编程不咋会。

先来看下信号槽机制是什么样的。先看连接connect,

连接方式有多种:

static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);//典型的SIGNAL, SLOT宏模式

connect(this, &Object::ageChanged, this, &Object::onAgeChanged);调用的上述第一个,成员函数槽连接。

类型萃取成员函数。

先确认有Q_OGJECT 宏:

通过匹配test,确定匹配的项,模板的SFINAE技术,用test(…)是不是更容易看懂点。这种技术用的非常普遍,有需求可以参考一下。接下来确认参数是否匹配。这些都是在编译时期确定的

接下调用connectImpl,这个函数前4个参数都能看懂,主要看下第5个参数:

new QtPrivate::QSlotObject::Value,
typename SignalType::ReturnType>(slot),

template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase  
{  
    typedef QtPrivate::FunctionPointer<Func> FuncType;  
    Func function;  
    static void impl(int which, QSlotObjectBase \*this\_, QObject \*r, void \*\*a, bool \*ret)  
    {  
        switch (which) {  
        case Destroy:  
            delete static\_cast<QSlotObject\*>(this\_);  
            break;  
        case Call:  
            FuncType::template call<Args, R>(static\_cast<QSlotObject\*>(this\_)->function, static\_cast<typename FuncType::Object \*>(r), a);  
            break;  
        case Compare:  
            \*ret = \*reinterpret\_cast<Func \*>(a) == static\_cast<QSlotObject\*>(this\_)->function;  
            break;  
        case NumOperations: ;  
        }  
    }  
public:  
    explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}  
};

把slot传进去了,先简单看做是一个回调吧,一层一层的模板,太费劲。。。。List_left 得到的是个List 即参数类型

。 QSlotObject 保存了槽函数。接着看connectImpl.

QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
{
if (!signal) {
qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}

int signal\_index = -1;  
void \*args\[\] = { &signal\_index, signal };  
for (; senderMetaObject && signal\_index < 0; senderMetaObject = senderMetaObject->superClass()) {  
    senderMetaObject->static\_metacall(QMetaObject::IndexOfMethod, 0, args);  
    if (signal\_index >= 0 && signal\_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)  
        break;  
}  
if (!senderMetaObject) {  
    qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className());  
    slotObj->destroyIfLastRef();  
    return QMetaObject::Connection(nullptr);  
}  
signal\_index += QMetaObjectPrivate::signalOffset(senderMetaObject);  
return QObjectPrivate::connectImpl(sender, signal\_index, receiver, slot, slotObj, type, types, senderMetaObject);  

}

定义了一个*args[], 第一个参数是信号所对应的索引,第二个是信号的函数指针。使用发送方的senderMetaObject ,调用static_metacall。这是QmetaObject的内部函数,进而调用d->static_metacall,而这个static_metacall是个函数指针,保存的就是子类的qt_static_metacall,之前staticMetaObject对象初始化时,赋值进去的。qt_static_metacall对多种枚举类型做了处理,列举出来的枚举类型有:

enum Call {
InvokeMetaMethod,
ReadProperty,
WriteProperty,
ResetProperty,
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType,
BindableProperty
};

这次传的是IndexOfMethod,会跟相应的信号类型去做对比,并填充args的第一个参数。

else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast(_a[0]);
{
using _t = void (Object::*)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::ageChanged)) {
*result = 0;
return;
}
}
{
using _t = void (Object::*)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::scoreChanged)) {
*result = 1;
return;
}
}
}

此时signal_index对应的只是在本类中的信号偏移。之后还会算上相对于父类信号的偏移。接下来调用:

QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);

先对Qt::UniqueConnection类型的connect进行特殊处理,拿到对应的ConnectionData,这个数据类型包含所有的connect信息,再从中取出对应信号所对应的那些connections,毕竟一个信号可以连接多个,但uniqueConnection只能有一个,所以会去除这次多余的槽。

然后再记录是否是 Qt::SingleShotConnection。新建connection

这个connection记录了sender, 信号索引,threadData(这个应该是线程相关,以后再研究),receiver, 槽函数对象,就是那个QSlotObject,连接类型等。然后addConnection(signal_index, c.get())。

void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
ensureConnectionData();
ConnectionData *cd = connections.loadRelaxed();
cd->resizeSignalVector(signal + 1);

ConnectionList &connectionList = cd->connectionsForSignal(signal);  
if (connectionList.last.loadRelaxed()) {  
    Q\_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());  
    connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);  
} else {  
    connectionList.first.storeRelaxed(c);  
}  
c->id = ++cd->currentConnectionId;  
c->prevConnectionList = connectionList.last.loadRelaxed();  
connectionList.last.storeRelaxed(c);

QObjectPrivate \*rd = QObjectPrivate::get(c->receiver.loadRelaxed());  
rd->ensureConnectionData();

c->prev = &(rd->connections.loadRelaxed()->senders);  
c->next = \*c->prev;  
\*c->prev = c;  
if (c->next)  
    c->next->prev = &c->next;  

}

拿到connectionData, 取出信号对应connectList,这是一个链表结构,去串接这个链表。同时也会更新recevier的connections,好像每次加入的connection会在链表头部。这样此次连接就保存下来了。

之后拿到QMetaMethod, moc产生的文件记录了相应的元信息qt_meta_data_Object,以此来创建metaMethod.

d.data对应的是qt_meta_data_Object[137],methodData是20。size是6,每个信号有6个元信息参数,以此判断对应的信号信息。

再看QMetaMethod data定义:

刚好对应上。s->connectNotify(method);调用了这个method,发现这个在基类里是空实现,看来子类要重写这个函数才能起到作用,可以在信号连接时做一些回调工作。至此完成了所有的连接工作。至于其他的几个连接大概也差不了太多。非成员函数的槽注意一下。

再来看看槽是如何被触发的,顺便把属性设置一起看一下。通过调用setProperty(age),看看如何实现的。

bool QObject::setProperty(const char *name, const QVariant &value)
{
Q_D(QObject);
const QMetaObject *meta = metaObject();
if (!name || !meta)
return false;

int id = meta->indexOfProperty(name);  
if (id < 0) {  
    if (!d->extraData)  
        d->extraData = new QObjectPrivate::ExtraData;

    const int idx = d->extraData->propertyNames.indexOf(name);

    if (!value.isValid()) {  
        if (idx == -1)  
            return false;  
        d->extraData->propertyNames.removeAt(idx);  
        d->extraData->propertyValues.removeAt(idx);  
    } else {  
        if (idx == -1) {  
            d->extraData->propertyNames.append(name);  
            d->extraData->propertyValues.append(value);  
        } else {  
            if (value.userType() == d->extraData->propertyValues.at(idx).userType()  
                    && value == d->extraData->propertyValues.at(idx))  
                return false;  
            d->extraData->propertyValues\[idx\] = value;  
        }  
    }

    QDynamicPropertyChangeEvent ev(name);  
    QCoreApplication::sendEvent(this, &ev);

    return false;  
}  
QMetaProperty p = meta->property(id);  

#ifndef QT_NO_DEBUG
if (!p.isWritable())
qWarning("%s::setProperty: Property \"%s\" invalid,"
" read-only or does not exist", metaObject()->className(), name);
#endif
return p.write(this, value);
}

setProperty

首先拿到metaobject, 调用indexOfProperty(name)。里面实现就不细究了,创建了一个QMetaProperty,去与name(age)相比较,返回属性的索引,不过一样会加上父类的偏移。如果没有就会创建一个动态属性(前提是有DynamicMetaObject)。如果还没拿到,可以看到先判断有没有extraData, 没有就创建一个。并将这个属性加入extraData。接着调用meta->property(id),其实就是在里面构建了一个QMetaProperty。顺便展示一下QMetaProperty的数据结构:

跟QMetaMethod差不多,相同的套路。再调用p.write(this, value):

bool QMetaProperty::write(QObject *object, const QVariant &value) const
{
if (!object || !isWritable())
return false;

QVariant v = value;  
QMetaType t(mobj->d.metaTypes\[data.index(mobj)\]);  
if (t != QMetaType::fromType<QVariant>() && t != v.metaType()) {  
    if (isEnumType() && !t.metaObject() && v.metaType().id() == QMetaType::QString) {  
        // Assigning a string to a property of type Q\_ENUMS (instead of Q\_ENUM)  
        bool ok;  
        if (isFlagType())  
            v = QVariant(menum.keysToValue(value.toByteArray(), &ok));  
        else  
            v = QVariant(menum.keyToValue(value.toByteArray(), &ok));  
        if (!ok)  
            return false;  
    } else if (!value.isValid()) {  
        if (isResettable())  
            return reset(object);  
        v = QVariant(t, nullptr);  
    } else if (!v.convert(t)) {  
        return false;  
    }  
}  
// the status variable is changed by qt\_metacall to indicate what it did  
// this feature is currently only used by Qt D-Bus and should not be depended  
// upon. Don't change it without looking into QDBusAbstractInterface first  
// -1 (unchanged): normal qt\_metacall, result stored in argv\[0\]  
// changed: result stored directly in value, return the value of status  
int status = -1;  
// the flags variable is used by the declarative module to implement  
// interception of property writes.  
int flags = 0;  
void \*argv\[\] = { nullptr, &v, &status, &flags };  
if (t == QMetaType::fromType<QVariant>())  
    argv\[0\] = &v;  
else  
    argv\[0\] = v.data();  
if (priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static\_metacall)  
    mobj->d.static\_metacall(object, QMetaObject::WriteProperty, data.index(mobj), argv);  
else  
    QMetaObject::metacall(object, QMetaObject::WriteProperty, data.index(mobj) + mobj->propertyOffset(), argv);

return status;  

}

QMetaProperty::write

这个metaTypes似乎有点眼熟,就是前面moc文件一堆模板创建的。判断传进来的variant的类型是否与属性相同。这个metaTypes怎么构建的还是不清楚。PropertyAccessInStaticMetaCall这个标志目前也不知道是什么,

enum MetaObjectFlag {
DynamicMetaObject = 0x01,
RequiresVariantMetaObject = 0x02,
PropertyAccessInStaticMetaCall = 0x04 // since Qt 5.5, property code is in the static metacall
};

接着往下看,最终还是调到qt_metacall。qt_static_metacall,找到对应的函数setAge。至此完成setProperty的任务。

在setAge里发送了一个ageChaged信号。实际上会调到moc文件里。其实agechaned(age)本来就是个函数,忽略emit即可,虽然我们只申明了该函数,但moc会生成它的实现。

std::addressof目的是当存在operator&的重载时, 依然能够获得变量的地址。再看activate里干了什么。

0是在本类里的信号偏移。之后会加上父类的偏移。调用doActivate(sender, signal_index, argv);

该函数里面实现很复杂,主要的实现流程:拿到connect时构建的connectionData, 取出信号所对应的连接列表,判断发送信号的线程和当前线程是否相同。并确保在信号发射期间添加的信号不会被触发。之后遍历这个连接列表,获取对应的receiver。接着判断连接类型。以及是否是singleShot,是否已断开连接。再判断是成员函数槽调用,还是普通函数调用。成员函数调用如下实现:

构建了一个QSlotObjectBase,并调用call函数,这个QSlotObject就是我们在connect时创建的。可以往前看,

FuncType::template call(static_cast(this_)->function, static_cast(r), a);

最终会调用到该成员函数,中间全是模板,不在此深究。调用完所有的receiver后,做一些收尾工作。

可以看到所有的机制都归功于moc 生成的元信息数据。后续有时间再看其他的实现。