QT6 原始碼雜記

青山見我發表於2021-11-13

菜雞一個,隨便寫寫,勿噴。好記性不如爛筆頭。

瞭解qt,第一個繞不過的坎便是qt的元物件系統 QMetaObject。

QT6 原始碼雜記
 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 };
View Code

通常繼承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檔案,該檔案會隨著專案一起編譯。

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 <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
View Code

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
QT6 原始碼雜記
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()
    };
};
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巨集模式

 

 

 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())。

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.

 

 

 d.data對應的是qt_meta_data_Object[137],methodData是20。size是6,每個訊號有6個元資訊引數,以此判斷對應的訊號資訊。

 

 

 再看QMetaMethod data定義:

 

 

 剛好對應上。s->connectNotify(method);呼叫了這個method,發現這個在基類裡是空實現,看來子類要重寫這個函式才能起到作用,可以在訊號連線時做一些回撥工作。至此完成了所有的連線工作。至於其他的幾個連線大概也差不了太多。非成員函式的槽注意一下。

 

再來看看槽是如何被觸發的,順便把屬性設定一起看一下。通過呼叫setProperty(age),看看如何實現的。

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 = 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):

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<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 生成的元資訊資料。後續有時間再看其他的實現。

 

相關文章