QT6|QT6 源码杂记

菜鸡一个,随便写写,勿喷。好记性不如烂笔头。
了解qt,第一个绕不过的坎便是qt的元对象系统 QMetaObject。
QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片

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

View Code 通常继承qt的类,都会继承于QObject. 在类里添加一句 Q_OBJECT宏。如下所示,是qt信号槽的关键。
1 #define Q_OBJECT \ 2 public: \ 3QT_WARNING_PUSH \ 4Q_OBJECT_NO_OVERRIDE_WARNING \ 5static const QMetaObject staticMetaObject; \ 6virtual const QMetaObject *metaObject() const; \ 7virtual void *qt_metacast(const char *); \ 8virtual int qt_metacall(QMetaObject::Call, int, void **); \ 9QT_TR_FUNCTIONS \ 10 private: \ 11Q_OBJECT_NO_ATTRIBUTES_WARNING \ 12Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ 13QT_WARNING_POP \ 14struct QPrivateSignal {}; \ 15QT_ANNOTATE_CLASS(qt_qobject, "")

要想编译qt相关类,少不了moc工具。可以理解为qt的预编译工具,moc工具会解析具有Q_OBJECT宏的类,生成对应的moc_xx.cpp文件,该文件会随着项目一起编译。
QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片
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 { 25const uint offsetsAndSize[44]; 26char stringdata0[167]; 27 }; 28 #define QT_MOC_LITERAL(ofs, len) \ 29uint(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: 679,// revision 680,// classname 693,14, // classinfo 707,20, // methods 712,89, // properties 721,99, // enums/sets 732,110, // constructors 740,// flags 752,// signalCount 76 77// classinfo: key, value 781,2, 793,4, 805,6, 81 82// signals: name, argc, parameters, tag, flags, initial metatype offsets 837,1,62,8, 0x06,2 /* Public */, 8410,1,65,8, 0x06,4 /* Public */, 85 86// slots: name, argc, parameters, tag, flags, initial metatype offsets 8712,1,68,8, 0x0a,6 /* Public */, 8813,1,71,8, 0x0a,8 /* Public */, 89 90// methods: name, argc, parameters, tag, flags, initial metatype offsets 9114,1,74,8, 0x02,10 /* Public */, 9211,0,77,8, 0x02,12 /* Public */, 9315,1,78,8, 0x02,13 /* Public */, 94 95// signals: parameters 96QMetaType::Void, QMetaType::Int,9, 97QMetaType::Void, QMetaType::Int,11, 98 99// slots: parameters 100QMetaType::Void, QMetaType::Int,9, 101QMetaType::Void, QMetaType::Int,11, 102 103// methods: parameters 104QMetaType::Void, QMetaType::Int,9, 105QMetaType::Int, 106QMetaType::Void, QMetaType::Int,11, 107 108// constructors: parameters 1090x80000000 | 8, QMetaType::QString, QMetaType::QObjectStar,16,17, 1100x80000000 | 8, QMetaType::QString,16, 111 112// properties: name, type, flags 1139, QMetaType::Int, 0x00015103, uint(0), 0, 11411, QMetaType::Int, 0x00015103, uint(1), 0, 115 116// enums: name, alias, flags, count, data 11718,18, 0x0,3,104, 118 119// enum data: key, value 12019, uint(Object::Basic), 12120, uint(Object::Middle), 12221, uint(Object::Advanced), 123 124// constructors: name, argc, parameters, tag, flags, initial metatype offsets 1250,2,81,8, 0x0e,15 /* Public */, 1260,1,86,8, 0x2e,17 /* Public | MethodCloned */, 127 1280// eod 129 }; 130 131 void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) 132 { 133if (_c == QMetaObject::CreateInstance) { 134switch (_id) { 135case 0: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< QObject*(*)>(_a[2]))); 136if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break; 137case 1: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1]))); 138if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break; 139default: break; 140} 141} else if (_c == QMetaObject::InvokeMetaMethod) { 142auto *_t = static_cast(_o); 143(void)_t; 144switch (_id) { 145case 0: _t->ageChanged((*reinterpret_cast< int(*)>(_a[1]))); break; 146case 1: _t->scoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break; 147case 2: _t->onAgeChanged((*reinterpret_cast< int(*)>(_a[1]))); break; 148case 3: _t->onScoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break; 149case 4: _t->setAge((*reinterpret_cast< const int(*)>(_a[1]))); break; 150case 5: { int _r = _t->score(); 151if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }break; 152case 6: _t->setScore((*reinterpret_cast< const int(*)>(_a[1]))); break; 153default: ; 154} 155} else if (_c == QMetaObject::IndexOfMethod) { 156int *result = reinterpret_cast(_a[0]); 157{ 158using _t = void (Object::*)(int ); 159if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::ageChanged)) { 160*result = 0; 161return; 162} 163} 164{ 165using _t = void (Object::*)(int ); 166if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::scoreChanged)) { 167*result = 1; 168return; 169} 170} 171} 172 #ifndef QT_NO_PROPERTIES 173else if (_c == QMetaObject::ReadProperty) { 174auto *_t = static_cast(_o); 175(void)_t; 176void *_v = _a[0]; 177switch (_id) { 178case 0: *reinterpret_cast< int*>(_v) = _t->age(); break; 179case 1: *reinterpret_cast< int*>(_v) = _t->score(); break; 180default: break; 181} 182} else if (_c == QMetaObject::WriteProperty) { 183auto *_t = static_cast(_o); 184(void)_t; 185void *_v = _a[0]; 186switch (_id) { 187case 0: _t->setAge(*reinterpret_cast< int*>(_v)); break; 188case 1: _t->setScore(*reinterpret_cast< int*>(_v)); break; 189default: 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 = { { 198QMetaObject::SuperData::link(), 199qt_meta_stringdata_Object.offsetsAndSize, 200qt_meta_data_Object, 201qt_static_metacall, 202nullptr, 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 >, 209nullptr 210 } }; 211 212 213 const QMetaObject *Object::metaObject() const 214 { 215return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; 216 } 217 218 void *Object::qt_metacast(const char *_clname) 219 { 220if (!_clname) return nullptr; 221if (!strcmp(_clname, qt_meta_stringdata_Object.stringdata0)) 222return static_cast(this); 223return 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); 229if (_id < 0) 230return _id; 231if (_c == QMetaObject::InvokeMetaMethod) { 232if (_id < 7) 233qt_static_metacall(this, _c, _id, _a); 234_id -= 7; 235} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { 236if (_id < 7) 237*reinterpret_cast(_a[0]) = QMetaType(); 238_id -= 7; 239} 240 #ifndef QT_NO_PROPERTIES 241else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty 242|| _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty 243|| _c == QMetaObject::RegisterPropertyMetaType) { 244qt_static_metacall(this, _c, _id, _a); 245_id -= 2; 246} 247 #endif // QT_NO_PROPERTIES 248return _id; 249 } 250 251 // SIGNAL 0 252 void Object::ageChanged(int _t1) 253 { 254void *_a[] = { nullptr, const_cast(reinterpret_cast(std::addressof(_t1))) }; 255QMetaObject::activate(this, &staticMetaObject, 0, _a); 256 } 257 258 // SIGNAL 1 259 void Object::scoreChanged(int _t1) 260 { 261void *_a[] = { nullptr, const_cast(reinterpret_cast(std::addressof(_t1))) }; 262QMetaObject::activate(this, &staticMetaObject, 1, _a); 263 } 264 QT_WARNING_POP 265 QT_END_MOC_NAMESPACE
View Code QMetaObject 的主要数据
1 struct Q_CORE_EXPORT QMetaObject 2 { 3 ... 4struct Data { // private data 5SuperData superdata; 6const uint *stringdata; 7const uint *data; 8typedef void (*StaticMetacallFunction)(QObject *, 9QMetaObject::Call, int, void **); 10StaticMetacallFunction static_metacall; 11const SuperData *relatedMetaObjects; 12const QtPrivate::QMetaTypeInterface *const *metaTypes; 13void *extradata; //reserved for future use 14} d; 15 ... 16 }

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

QT6|QT6 源码杂记
文章图片


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

QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片
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() }; };

View Code 咱也不知道它是干嘛的,模板元编程不咋会。
先来看下信号槽机制是什么样的。先看连接connect,
连接方式有多种:
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection); //典型的SIGNAL, SLOT宏模式
QT6|QT6 源码杂记
文章图片



connect(this, &Object::ageChanged, this, &Object::onAgeChanged); 调用的上述第一个,成员函数槽连接。
QT6|QT6 源码杂记
文章图片



类型萃取成员函数。
QT6|QT6 源码杂记
文章图片



先确认有Q_OGJECT 宏:
QT6|QT6 源码杂记
文章图片



通过匹配test,确定匹配的项,模板的SFINAE技术,用test(...)是不是更容易看懂点。这种技术用的非常普遍,有需求可以参考一下。接下来确认参数是否匹配。这些都是在编译时期确定的
接下调用connectImpl,这个函数前4个参数都能看懂,主要看下第5个参数:
new QtPrivate::QSlotObject::Value, typename SignalType::ReturnType>(slot),

template class QSlotObject : public QSlotObjectBase { typedef QtPrivate::FunctionPointer FuncType; Func function; static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) { switch (which) { case Destroy: delete static_cast(this_); break; case Call: FuncType::template call(static_cast(this_)->function, static_cast(r), a); break; case Compare: *ret = *reinterpret_cast(a) == static_cast(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
QT6|QT6 源码杂记
文章图片



这个connection记录了sender, 信号索引,threadData(这个应该是线程相关,以后再研究),receiver, 槽函数对象,就是那个QSlotObject,连接类型等。然后addConnection(signal_index, c.get())。
QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片
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; }

View Code 拿到connectionData, 取出信号对应connectList,这是一个链表结构,去串接这个链表。同时也会更新recevier的connections,好像每次加入的connection会在链表头部。这样此次连接就保存下来了。
之后拿到QMetaMethod, moc产生的文件记录了相应的元信息qt_meta_data_Object,以此来创建metaMethod.
QT6|QT6 源码杂记
文章图片



d.data对应的是qt_meta_data_Object[137],methodData是20。size是6,每个信号有6个元信息参数,以此判断对应的信号信息。
QT6|QT6 源码杂记
文章图片



再看QMetaMethod data定义:
QT6|QT6 源码杂记
文章图片



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

再来看看槽是如何被触发的,顺便把属性设置一起看一下。通过调用setProperty(age),看看如何实现的。
QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片
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 = https://www.it610.com/article/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 =https://www.it610.com/article/= 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的数据结构:
QT6|QT6 源码杂记
文章图片


跟QMetaMethod差不多,相同的套路。再调用p.write(this, value):
QT6|QT6 源码杂记
文章图片
QT6|QT6 源码杂记
文章图片
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() && 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()) 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会生成它的实现。
QT6|QT6 源码杂记
文章图片


std::addressof目的是当存在operator&的重载时, 依然能够获得变量的地址。再看activate里干了什么。
0是在本类里的信号偏移。之后会加上父类的偏移。调用doActivate(sender, signal_index, argv);
该函数里面实现很复杂,主要的实现流程:拿到connect时构建的connectionData, 取出信号所对应的连接列表,判断发送信号的线程和当前线程是否相同。并确保在信号发射期间添加的信号不会被触发。之后遍历这个连接列表,获取对应的receiver。接着判断连接类型。以及是否是singleShot,是否已断开连接。再判断是成员函数槽调用,还是普通函数调用。成员函数调用如下实现:
QT6|QT6 源码杂记
文章图片


构建了一个QSlotObjectBase,并调用call函数,这个QSlotObject就是我们在connect时创建的。可以往前看,
FuncType::template call(static_cast(this_)->function, static_cast(r), a);
最终会调用到该成员函数,中间全是模板,不在此深究。调用完所有的receiver后,做一些收尾工作。
可以看到所有的机制都归功于moc 生成的元信息数据。后续有时间再看其他的实现。
【QT6|QT6 源码杂记】

    推荐阅读