菜雞一個,隨便寫寫,勿噴。好記性不如爛筆頭。
瞭解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); }
首先拿到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; }
這個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 生成的元資訊資料。後續有時間再看其他的實現。