QT6 源码杂记

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

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

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

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

 
 

QT6 源码杂记_第1张图片

 

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<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, QtPrivate::TypeAndForceComplete, QtPrivate::TypeAndForceComplete
>,
    nullptr
} };

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

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

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

SuperData superdata : QOjbect的staticMetaobject
const uint *stringdata: qt_meta_stringdata_Object.offsetsAndSize// 并没有直接指向stringdata0[167]。
const uint *data :qt_meta_data_Object
StaticMetacallFunction static_metacall: qt_static_metacall
const SuperData *relatedMetaObjects: nullptr;
const QtPrivate::QMetaTypeInterface *const *metaTypes: ....这个有点晕。模板元编程,萃取类型metaType信息
extradata:nullptr
template
struct QMetaTypeInterfaceWrapper
{
    static inline constexpr const QMetaTypeInterface metaType = {
        /*.revision=*/ 0,
        /*.alignment=*/ alignof(T),
        /*.size=*/ sizeof(T),
        /*.flags=*/ QMetaTypeTypeFlags::Flags,
        /*.typeId=*/ BuiltinMetaType::value,
        /*.metaObjectFn=*/ MetaObjectForType::metaObjectFunction,
        /*.name=*/ QMetaTypeForType::getName(),
        /*.defaultCtr=*/ QMetaTypeForType::getDefaultCtr(),
        /*.copyCtr=*/ QMetaTypeForType::getCopyCtr(),
        /*.moveCtr=*/ QMetaTypeForType::getMoveCtr(),
        /*.dtor=*/ QMetaTypeForType::getDtor(),
        /*.equals=*/ QEqualityOperatorForType::equals,
        /*.lessThan=*/ QLessThanOperatorForType::lessThan,
        /*.debugStream=*/ QDebugStreamOperatorForType::debugStream,
        /*.dataStreamOut=*/ QDataStreamOperatorForType::dataStreamOut,
        /*.dataStreamIn=*/ QDataStreamOperatorForType::dataStreamIn,
        /*.legacyRegisterOp=*/ QMetaTypeForType::getLegacyRegister()
    };
};
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 源码杂记_第2张图片

 

 

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

QT6 源码杂记_第3张图片

 

 

 类型萃取成员函数。

QT6 源码杂记_第4张图片

 

 

 先确认有Q_OGJECT 宏:

QT6 源码杂记_第5张图片

 

 

 通过匹配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<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

QT6 源码杂记_第6张图片

 

 

 这个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;
}
View Code

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

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

QT6 源码杂记_第7张图片

 

 

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

 

 

 再看QMetaMethod data定义:

QT6 源码杂记_第8张图片

 

 

 刚好对应上。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的数据结构:

QT6 源码杂记_第9张图片

 

 跟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() && 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会生成它的实现。

 

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

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

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

QT6 源码杂记_第10张图片

 

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

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

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

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

 

你可能感兴趣的:(QT6 源码杂记)