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

了解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<QObjectPrivate*> (&(*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 <memory>
10 #include "../../../Object.h"
11 #include <QtCore/qbytearray.h>
12 #include <QtCore/qmetatype.h>
13 #if !defined(Q_MOC_OUTPUT_REVISION)
14 #error "The header file 'Object.h' doesn't include <QObject>."
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<QObject**>(_a[0]) = _r; } break;
137 case 1: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])));
138 if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
139 default: break;
140 }
141 } else if (_c == QMetaObject::InvokeMetaMethod) {
142 auto *_t = static_cast<Object *>(_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<int *>(_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<Object *>(_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<Object *>(_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<QObject::staticMetaObject>(),
199 qt_meta_stringdata_Object.offsetsAndSize,
200 qt_meta_data_Object,
201 qt_static_metacall,
202 nullptr,
203 qt_incomplete_metaTypeArray<qt_meta_stringdata_Object_t
204 , QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
205 , QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
206 , QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>
207 , QtPrivate::TypeAndForceComplete<QString, std::false_type>, QtPrivate::TypeAndForceComplete<QObject *, std::false_type>, QtPrivate::TypeAndForceComplete<QString, std::false_type>
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<void*>(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<QMetaType *>(_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<void*>(reinterpret_cast<const void*>(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<void*>(reinterpret_cast<const void*>(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<QObject::staticMetaObject>(),
qt_meta_stringdata_Object.offsetsAndSize,
qt_meta_data_Object,
qt_static_metacall,
nullptr,
qt_incomplete_metaTypeArray<qt_meta_stringdata_Object_t
, QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>
, QtPrivate::TypeAndForceComplete<QString, std::false_type>, QtPrivate::TypeAndForceComplete<QObject *, std::false_type>, QtPrivate::TypeAndForceComplete<QString, std::false_type>
>,
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<typename T>
struct QMetaTypeInterfaceWrapper
{
static inline constexpr const QMetaTypeInterface metaType = {
/*.revision=*/ 0,
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),
/*.flags=*/ QMetaTypeTypeFlags<T>::Flags,
/*.typeId=*/ BuiltinMetaType<T>::value,
/*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,
/*.name=*/ QMetaTypeForType<T>::getName(),
/*.defaultCtr=*/ QMetaTypeForType<T>::getDefaultCtr(),
/*.copyCtr=*/ QMetaTypeForType<T>::getCopyCtr(),
/*.moveCtr=*/ QMetaTypeForType<T>::getMoveCtr(),
/*.dtor=*/ QMetaTypeForType<T>::getDtor(),
/*.equals=*/ QEqualityOperatorForType<T>::equals,
/*.lessThan=*/ QLessThanOperatorForType<T>::lessThan,
/*.debugStream=*/ QDebugStreamOperatorForType<T>::debugStream,
/*.dataStreamOut=*/ QDataStreamOperatorForType<T>::dataStreamOut,
/*.dataStreamIn=*/ QDataStreamOperatorForType<T>::dataStreamIn,
/*.legacyRegisterOp=*/ QMetaTypeForType<T>::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<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::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<type1,type2...> 即参数类型

。 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<int *>(_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<false>(sender, signal_index, argv);

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

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

FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);

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

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

QT6 源码杂记的更多相关文章

  1. 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器

    [摘要] timers模块部分源码和定时器原理 示例代码托管在:http://www.github.com/dashnowords/blogs 一.概述 Timer模块相关的逻辑较为复杂,不仅包含Ja ...

  2. 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器

    目录 一.概述 二. 数据结构 2.1 链表 2.2 二叉堆 三. 从setTimeout理解Timer模块源码 3.1 timers.js中的定义 3.2 Timeout类定义 3.3 active ...

  3. C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库

    C# DateTime的11种构造函数   别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...

  4. React事件杂记及源码分析

    前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点  调用方法时需要手动绑定this  React事件是一种合成事件SyntheticEvent,什么是合成事件?  事件属性 ...

  5. TensorFlow源码框架 杂记

    一.为什么我们需要使用线程池技术(ThreadPool) 线程:采用“即时创建,即时销毁”策略,即接受请求后,创建一个新的线程,执行任务,完毕后,线程退出: 线程池:应用软件启动后,立即创建一定数量的 ...

  6. 【实习记】2014-09-04浏览代码查middle资料+总结我折腾过的源码浏览器

        浏览着代码,看源码可以先看make文件,make文件有制造的流程信息. 一般可以从运行的程序对应的cpp看起.然而如果有框架,那就不容易了,会关系错纵复杂. 总结一下我折腾过的源码阅读器. s ...

  7. HeartBeat源码安装

    只是写了安装流程,具体信息查看互联网; 环境: CentOS6.8 x86_64 min Heartbeat 3.0.6 http://hg.linux-ha.org/heartbeat-STABLE ...

  8. Python源码读后小结

    Python 笔记 前言(还是叫杂记吧) 在python中一切皆对象, python中的对象体系大致包含了"类型对象", "Mapping对象(dict)", ...

  9. epoll(2) 使用及源码分析的引子

    epoll(2) 使用及源码分析的引子 本文代码取自内核版本 4.17 epoll(2) - I/O 事件通知设施. epoll 是内核在2.6版本后实现的,是对 select(2)/poll(2) ...

随机推荐

  1. 实时渲染基础(4)纹理(Texture)

    目录 纹理映射(Texture Mapping) 球形贴图(Spherical Map) 立方体贴图(Cube Map) 纹理走样问题 Mipmap 各向异性过滤(Ripmap) 纹理应用技术(Tex ...

  2. 【.NET 与树莓派】气压传感器——BMP180

    BMP180 是一款数字气压计传感器,实际可读出温度和气压值.此模块使用 IIC(i2c)协议.模块体积很小,比老周的大拇指指甲还小:也很便宜,一般是长这样的.螺丝孔只开一个,也有开两个孔的. 这货基 ...

  3. 易华录 X ShardingSphere|葫芦 App 后台数据处理的逻辑捷径

    "ShardingSphere 大大简化了分库分表的开发和维护工作,对于业务的快速上线起到了非常大的支撑作用,保守估计 ShardingSphere 至少为我们节省了 4 个月的研发成本.& ...

  4. Apache ShardingSphere 邀您相约 Open Source Day

    Apache ShardingSphere 很荣幸接受 AnitaB.org 的邀请参加 OpenSourceDay Summer 2021 活动. Open Source Day (OSD) 是一个 ...

  5. ArcPy数据列表遍历

    ArcPy数据列表遍历 批处理脚本的首要任务之一是为可用数据编写目录,以便在处理过程中可以遍历数据. ArcPy 具有多个专为创建此类列表而构建的函数. 函数 说明 ListFields(datase ...

  6. iOS路由最佳选择是什么

    背景 记得四年前iOS路由开始盛行,当时比较有名的是蘑菇街的,后来CTMediator写了几篇文章把蘑菇街批的体无完肤,导致我后来写新项目用了CTMediator,那一堆组件创建的叫一个酸爽啊!再后来 ...

  7. Less-32 宽字节

    <!-- 下午整了半天Less-29~31,愣是没调好jsp环境,只好跳过. 难受.jpg !--> Less-32: 核心语句: 各种回显均存在. 第一句话指定了字符集为gbk. che ...

  8. Asp.net Core使用EFCore+Linq进行操作

    注:EFCore和EF有区别,在core中写的也有一点区别,每个人写法不同仅供参考写的比较细致耐性一点看完会有收获的 首先加上必要的引用 using Microsoft.EntityFramework ...

  9. alertmanager的使用

    alertmanager的使用 一.Alertanager的安装 1.下载 2.安装 3.启动 4.alertmanager和prometheus的整合 二.告警分组 1.告警规则 2.alertma ...

  10. IDEA + maven 零基础构建 java agent 项目

    200316-IDEA + maven 零基础构建 java agent 项目 Java Agent(java 探针)虽说在 jdk1.5 之后就有了,但是对于绝大多数的业务开发 javaer 来说, ...