QT-元对象编译器moc
在Qt中有一个元对象系统,该系统通过扩展C++,提供了信号与槽机制(事件处理)和属性系统。为了支持这些新特性,Qt引入了元对象编译器(Meta-Object Compiler,MOC),用来解析头文件并生成moc_className.cpp
,将其与其他文件一起编译,实现元对象系统的基本功能。
MOC的工作原理
MOC会去读取C++的源文件,寻找qt特定宏(如Q_OBJECT
,Q_PROPERTY
,signals
,slots
等),当它找到这些宏时,就会生成一个moc_className.cpp
,其中包括了类的元信息,然后将这个文件以某种方式编译和链接到应用程序中。
MOC生成的代码主要有:
- 元对象代码:指对象的信息,如类名、超类名、方法、属性、信号、槽。
- 信号和槽的实现:实现了信号槽机制,允许对象之间进行低耦合通信。
- 动态属性系统代码:允许在运行时内省和修改对象的属性。
案例
定义一个类XObject,为了让该类可以使用元对象系统,类定义中添加了Q_OBJECT
宏。
XObject.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class XObject : public QObject
{
Q_OBJECT
public:
XObject();
~XObject();
Q_PROPERTY(int count READ getCount WRITE setCount)
int getCount() const;
void setCount(int val);
signals:
void sig_countChanged(int val);
public slots:
void slot_countChanged(int val);
private:
int count;
};XObject.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
XObject::XObject()
{
connect(this, SIGNAL(sig_countChanged(int)), this, SLOT(slot_countChanged(int)));
}
XObject::~XObject()
{
}
int XObject::getCount() const
{
return count;
}
void XObject::setCount(int val)
{
if ( val != count)
{
count = val;
emit sig_countChanged(val);
}
}
void XObject::slot_countChanged(int val)
{
qDebug() << "count is modified: " << val;
}moc_XObject.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185/****************************************************************************
** Meta object code from reading C++ file 'XObject.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.14.2)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_XObject_t {
QByteArrayData data[6];
char stringdata0[54];
};
static const qt_meta_stringdata_XObject_t qt_meta_stringdata_XObject = {
{
QT_MOC_LITERAL(0, 0, 7), // "XObject"
QT_MOC_LITERAL(1, 8, 16), // "sig_countChanged"
QT_MOC_LITERAL(2, 25, 0), // ""
QT_MOC_LITERAL(3, 26, 3), // "val"
QT_MOC_LITERAL(4, 30, 17), // "slot_countChanged"
QT_MOC_LITERAL(5, 48, 5) // "count"
},
"XObject\0sig_countChanged\0\0val\0"
"slot_countChanged\0count"
};
static const uint qt_meta_data_XObject[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
2, 14, // methods
1, 30, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 1, 24, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
4, 1, 27, 2, 0x0a /* Public */,
// signals: parameters
QMetaType::Void, QMetaType::Int, 3,
// slots: parameters
QMetaType::Void, QMetaType::Int, 3,
// properties: name, type, flags
5, QMetaType::Int, 0x00095103,
0 // eod
};
void XObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<XObject *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->sig_countChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 1: _t->slot_countChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (XObject::*)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&XObject::sig_countChanged)) {
*result = 0;
return;
}
}
}
else if (_c == QMetaObject::ReadProperty) {
auto *_t = static_cast<XObject *>(_o);
Q_UNUSED(_t)
void *_v = _a[0];
switch (_id) {
case 0: *reinterpret_cast< int*>(_v) = _t->getCount(); break;
default: break;
}
} else if (_c == QMetaObject::WriteProperty) {
auto *_t = static_cast<XObject *>(_o);
Q_UNUSED(_t)
void *_v = _a[0];
switch (_id) {
case 0: _t->setCount(*reinterpret_cast< int*>(_v)); break;
default: break;
}
} else if (_c == QMetaObject::ResetProperty) {
}
}
QT_INIT_METAOBJECT const QMetaObject XObject::staticMetaObject = { {
QMetaObject::SuperData::link<QObject::staticMetaObject>(),
qt_meta_stringdata_XObject.data,
qt_meta_data_XObject,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *XObject::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *XObject::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_XObject.stringdata0))
return static_cast<void*>(this);
return QObject::qt_metacast(_clname);
}
int XObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 2)
qt_static_metacall(this, _c, _id, _a);
_id -= 2;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 2)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 2;
}
else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
|| _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {
qt_static_metacall(this, _c, _id, _a);
_id -= 1;
} else if (_c == QMetaObject::QueryPropertyDesignable) {
_id -= 1;
} else if (_c == QMetaObject::QueryPropertyScriptable) {
_id -= 1;
} else if (_c == QMetaObject::QueryPropertyStored) {
_id -= 1;
} else if (_c == QMetaObject::QueryPropertyEditable) {
_id -= 1;
} else if (_c == QMetaObject::QueryPropertyUser) {
_id -= 1;
}
return _id;
}
// SIGNAL 0
void XObject::sig_countChanged(int _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE
文件头部
1
2
3
4
5
6
7/****************************************************************************
** Meta object code from reading C++ file 'XObject.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.14.2)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/这部分说明了这个文件是从XObject.h读取并生成的元对象代码,生成是由特定版本的Qt元对象编译器完成的。该文件是自动生成的,用户不应手动编辑它。
包含的头文件
1
2
3
4这里包含了必要的库和头文件:
memory用于智能指针。
XObject.h是实际的类定义文件。
qbytearray.h和qmetatype.h是Qt提供的用于处理字节数组和元类型的头文件。MOC版本检查
1
2
3
4
5
6
7
这部分检查是否包含了
- 元信息结构定义了一个结构体qt_meta_stringdata_XObject_t,用于存储与XObject类相关的字符串数据和元信息。
1
2
3
4
5
6
7QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_XObject_t {
QByteArrayData data[6];
char stringdata0[54];
}; - 字符串数据初始化该宏用于初始化字符串数据,qt_meta_stringdata_XObject中包含了信号、槽和属性的名称。
1
2
3
4
5 - 元数据数组
cpp
static const uint qt_meta_data_XObject[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
2, 14, // methods
1, 30, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 1, 24, 2, 0x06 /* Public /,
// slots: name, argc, parameters, tag, flags
4, 1, 27, 2, 0x0a / Public */,
// signals: parameters
QMetaType::Void, QMetaType::Int, 3,
// slots: parameters
QMetaType::Void, QMetaType::Int, 3,
// properties: name, type, flags
5, QMetaType::Int, 0x00095103,
0 // eod
};
这里定义了qt_meta_data_XObject数组,包含了有关XObject类的元数据信息,包括:
版本号、类名、类信息、方法数量、属性数量、信号数量等。
信号和槽的名称、参数数量、参数类型等信息。
此外,还有属性的名称、类型和标志。
7. 静态元调用函数
cpp
void XObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
该函数用于处理信号和槽的调用。
根据调用类型(_c),它可以调用信号或槽,或者读取和写入属性。
8. metaObject和qt_metacast方法
cpp
const QMetaObject *XObject::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *XObject::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_XObject.stringdata0))
return static_cast<void*>(this);
return QObject::qt_metacast(_clname);
}
metaObject返回该类的元对象信息。
qt_metacast用于支持动态类型转换。
9. qt_metacall方法
cpp
int XObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 2)
qt_static_metacall(this, _c, _id, _a);
_id -= 2;
}
…
return _id;
}
qt_metacall用于处理元对象调用,包括信号和槽的调用。
10. 信号的实现
1 | // SIGNAL 0 |
这个函数实现了sig_countChanged信号的激活,负责触发信号并传递参数。