6、Qt Meta Object system 学习
原文地址:http://blog.csdn.net/ilvu999/article/details/8049908
使用 meta object system
- 继承自 QOject
- 类定义中添加 Q_OBJECT 宏
- 使用 moc 程序对包含该宏的文件进行处理
采用 qmake 进行处理时,如果头文件xxx.h内包含 Q_OBJECT 宏,将生成 moc_xxx.cpp 文件。如果xxx.cpp文件内包含宏,将生成 xxx.moc 文件(这时,我们需要在xxx.cpp文件内添加 #include"xxx.moc")
Q_OBJECT宏
包括两个方面,
- 该宏在C++中的展开,有编译预处理器完成
- moc 程序对该宏的处理
宏定义
1 #define Q_OBJECT \ 2 public: \ 3 Q_OBJECT_CHECK \ 4 static const QMetaObject staticMetaObject; \ 5 Q_OBJECT_GETSTATICMETAOBJECT \ 6 virtual const QMetaObject *metaObject() const; \ 7 virtual void *qt_metacast(const char *); \ 8 QT_TR_FUNCTIONS \ 9 virtual int qt_metacall(QMetaObject::Call, int, void **); \ 10 private: \ 11 Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \ 12 Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
而宏 QT_TR_FUNCTIONS 将展开为我们使程序国际化经常使用的 tr 与 trUtf8函数
1 # define QT_TR_FUNCTIONS \ 2 static inline QString tr(const char *s, const char *c = 0) \ 3 { return staticMetaObject.tr(s, c); } \ 4 static inline QString trUtf8(const char *s, const char *c = 0) \ 5 { return staticMetaObject.trUtf8(s, c); } \ 6 static inline QString tr(const char *s, const char *c, int n) \ 7 { return staticMetaObject.tr(s, c, n); } \ 8 static inline QString trUtf8(const char *s, const char *c, int n) \ 9 { return staticMetaObject.trUtf8(s, c, n); }
moc 处理
Q_OBJECT 为我们添加了这么多成员函数,而这些函数我们有没有自己去实现,那么其定义在哪儿呢? 这就是 moc 为我们做的,自动生成这些成员函数的定义,放于生成的 xxx.moc 或 moc_xxx.cpp 文件内
注意:两个文件的区别(如果你用cmake或其他工具的话,这点可能很重要)
- 生成的 moc_xxx.cpp 中会自动包含 xxx.h 头文件,所以它可以被独立编译成目标文件(.o 或 .obj)
- 生成的 xxx.moc 是不会包含 xxx.cpp 的(要是包含就出问题了,能发现吧?),因此 xxx.moc 中没有类定义,它无法被独立编译,只能被 include 到 xxx.cpp 中。
QMetaObject
既然 Q_OBJECT 展开成与 QMetaObject 有关的成员函数,看一下QMetaObject 都提供哪些常用功能
- className() 返回类的名字
- superClass() 返回父类的 QMetaObject 对象
- method()与methodCount() 提供meta函数信息(包括signals, slots 与 invokable函数)
- enumerator()与 enumeratorCount() 提供enum类型信息
- propertyCount()与 property() 提供属性信息
- constructor()与 constructorCount() 提供 meta-constructors 信息
既然meta object能为我们的类提供这么多信息,那么这些信息存放哪儿了(大家肯定都能猜到秘密在moc生成的文件内,但为清楚起见,我们看一下QMetaObject的定义)。
这是用 struct 定义的一个类,我们略过其它,只看其数据成员
1 struct Q_CORE_EXPORT QMetaObject 2 { 3 const char *className() const; 4 const QMetaObject *superClass() const; 5 6 QObject *cast(QObject *obj) const; 7 const QObject *cast(const QObject *obj) const; 8 9 #ifndef QT_NO_TRANSLATION 10 // ### Qt 4: Merge overloads 11 QString tr(const char *s, const char *c) const; 12 QString trUtf8(const char *s, const char *c) const; 13 QString tr(const char *s, const char *c, int n) const; 14 QString trUtf8(const char *s, const char *c, int n) const; 15 #endif // QT_NO_TRANSLATION 16 17 int methodOffset() const; 18 int enumeratorOffset() const; 19 int propertyOffset() const; 20 int classInfoOffset() const; 21 22 int constructorCount() const; 23 int methodCount() const; 24 int enumeratorCount() const; 25 int propertyCount() const; 26 int classInfoCount() const; 27 28 int indexOfConstructor(const char *constructor) const; 29 int indexOfMethod(const char *method) const; 30 int indexOfSignal(const char *signal) const; 31 int indexOfSlot(const char *slot) const; 32 int indexOfEnumerator(const char *name) const; 33 int indexOfProperty(const char *name) const; 34 int indexOfClassInfo(const char *name) const; 35 36 QMetaMethod constructor(int index) const; 37 QMetaMethod method(int index) const; 38 QMetaEnum enumerator(int index) const; 39 QMetaProperty property(int index) const; 40 QMetaClassInfo classInfo(int index) const; 41 QMetaProperty userProperty() const; 42 43 static bool checkConnectArgs(const char *signal, const char *method); 44 static QByteArray normalizedSignature(const char *method); 45 static QByteArray normalizedType(const char *type); 46 47 // internal index-based connect 48 static bool connect(const QObject *sender, int signal_index, 49 const QObject *receiver, int method_index, 50 int type = 0, int *types = 0); 51 // internal index-based disconnect 52 static bool disconnect(const QObject *sender, int signal_index, 53 const QObject *receiver, int method_index); 54 static bool disconnectOne(const QObject *sender, int signal_index, 55 const QObject *receiver, int method_index); 56 // internal slot-name based connect 57 static void connectSlotsByName(QObject *o); 58 59 // internal index-based signal activation 60 static void activate(QObject *sender, int signal_index, void **argv); //obsolete 61 static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete 62 static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv); 63 static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete 64 65 // internal guarded pointers 66 static void addGuard(QObject **ptr); 67 static void removeGuard(QObject **ptr); 68 static void changeGuard(QObject **ptr, QObject *o); 69 70 static bool invokeMethod(QObject *obj, const char *member, 71 Qt::ConnectionType, 72 QGenericReturnArgument ret, 73 QGenericArgument val0 = QGenericArgument(0), 74 QGenericArgument val1 = QGenericArgument(), 75 QGenericArgument val2 = QGenericArgument(), 76 QGenericArgument val3 = QGenericArgument(), 77 QGenericArgument val4 = QGenericArgument(), 78 QGenericArgument val5 = QGenericArgument(), 79 QGenericArgument val6 = QGenericArgument(), 80 QGenericArgument val7 = QGenericArgument(), 81 QGenericArgument val8 = QGenericArgument(), 82 QGenericArgument val9 = QGenericArgument()); 83 84 static inline bool invokeMethod(QObject *obj, const char *member, 85 QGenericReturnArgument ret, 86 QGenericArgument val0 = QGenericArgument(0), 87 QGenericArgument val1 = QGenericArgument(), 88 QGenericArgument val2 = QGenericArgument(), 89 QGenericArgument val3 = QGenericArgument(), 90 QGenericArgument val4 = QGenericArgument(), 91 QGenericArgument val5 = QGenericArgument(), 92 QGenericArgument val6 = QGenericArgument(), 93 QGenericArgument val7 = QGenericArgument(), 94 QGenericArgument val8 = QGenericArgument(), 95 QGenericArgument val9 = QGenericArgument()) 96 { 97 return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, 98 val4, val5, val6, val7, val8, val9); 99 } 100 101 static inline bool invokeMethod(QObject *obj, const char *member, 102 Qt::ConnectionType type, 103 QGenericArgument val0 = QGenericArgument(0), 104 QGenericArgument val1 = QGenericArgument(), 105 QGenericArgument val2 = QGenericArgument(), 106 QGenericArgument val3 = QGenericArgument(), 107 QGenericArgument val4 = QGenericArgument(), 108 QGenericArgument val5 = QGenericArgument(), 109 QGenericArgument val6 = QGenericArgument(), 110 QGenericArgument val7 = QGenericArgument(), 111 QGenericArgument val8 = QGenericArgument(), 112 QGenericArgument val9 = QGenericArgument()) 113 { 114 return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, 115 val3, val4, val5, val6, val7, val8, val9); 116 } 117 118 static inline bool invokeMethod(QObject *obj, const char *member, 119 QGenericArgument val0 = QGenericArgument(0), 120 QGenericArgument val1 = QGenericArgument(), 121 QGenericArgument val2 = QGenericArgument(), 122 QGenericArgument val3 = QGenericArgument(), 123 QGenericArgument val4 = QGenericArgument(), 124 QGenericArgument val5 = QGenericArgument(), 125 QGenericArgument val6 = QGenericArgument(), 126 QGenericArgument val7 = QGenericArgument(), 127 QGenericArgument val8 = QGenericArgument(), 128 QGenericArgument val9 = QGenericArgument()) 129 { 130 return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, 131 val1, val2, val3, val4, val5, val6, val7, val8, val9); 132 } 133 134 QObject *newInstance(QGenericArgument val0 = QGenericArgument(0), 135 QGenericArgument val1 = QGenericArgument(), 136 QGenericArgument val2 = QGenericArgument(), 137 QGenericArgument val3 = QGenericArgument(), 138 QGenericArgument val4 = QGenericArgument(), 139 QGenericArgument val5 = QGenericArgument(), 140 QGenericArgument val6 = QGenericArgument(), 141 QGenericArgument val7 = QGenericArgument(), 142 QGenericArgument val8 = QGenericArgument(), 143 QGenericArgument val9 = QGenericArgument()) const; 144 145 enum Call { 146 InvokeMetaMethod, 147 ReadProperty, 148 WriteProperty, 149 ResetProperty, 150 QueryPropertyDesignable, 151 QueryPropertyScriptable, 152 QueryPropertyStored, 153 QueryPropertyEditable, 154 QueryPropertyUser, 155 CreateInstance 156 }; 157 158 int static_metacall(Call, int, void **) const; 159 static int metacall(QObject *, Call, int, void **); 160 161 #ifdef QT3_SUPPORT 162 QT3_SUPPORT const char *superClassName() const; 163 #endif 164 165 struct { // private data 166 const QMetaObject *superdata; 167 const char *stringdata; 168 const uint *data; 169 const void *extradata; 170 } d; 171 };
其中呢,
-
uperdata,指向父类的MetaObject,容易理解
- extradata 似乎目前未启用,不用理解
- 剩下两个是什么呢? 如你所想,就在 moc 生成的文件内
moc生成的文件
随便找个 moc 文件出来看看
1 static const uint qt_meta_data_HPixmapScene[] = {...}; 2 static const char qt_meta_stringdata_HPixmapScene[] = {...}; 3 const QMetaObject HPixmapScene::staticMetaObject = { 4 { &QGraphicsScene::staticMetaObject, qt_meta_stringdata_HPixmapScene, 5 qt_meta_data_HPixmapScene, 0 } 6 };
这是一个QGraphicsScene的子类。对比前面QMetaObject的数据成员看看,是不是很简单:
- 先分别定义1个 uint 和 char 的数组,
-
用这两个数组首地址和父类的MetaObject的指针初始化 staticMetaObject
- 这个 staticMetaObject 是我们自己的类的静态成员变量
uint数组
接下来我们可以看看 uint 数组,这个数组中存放的是一些索引值,来指导我们从char字符串中提取信息
1 static const uint qt_meta_data_HPixmapScene[] = { 2 3 // content: 4 4, // revision 5 0, // classname 6 0, 0, // classinfo 7 2, 14, // methods 8 0, 0, // properties 9 0, 0, // enums/sets 10 0, 0, // constructors 11 0, // flags 12 0, // signalCount 13 14 // slots: signature, parameters, type, tag, flags 15 18, 14, 13, 13, 0x0a, 16 41, 37, 13, 13, 0x0a, 17 18 0 // eod 19 };
对比QMetaObject用到的数据结构 QMetaObjectPrivate,看看,是不是豁然开朗了:uint 数组中的第一段 就对应这个结构体
1 struct QMetaObjectPrivate 2 { 3 int revision; 4 int className; 5 int classInfoCount, classInfoData; 6 int methodCount, methodData; 7 int propertyCount, propertyData; 8 int enumeratorCount, enumeratorData; 9 int constructorCount, constructorData; //since revision 2 10 int flags; //since revision 3 11 int signalCount; //since revision 4 12 // revision 5 introduces changes in normalized signatures, no new members 13 // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself 14 15 static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) 16 { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } 17 18 static int indexOfSignalRelative(const QMetaObject **baseObject, 19 const char* name, 20 bool normalizeStringData); 21 static int indexOfSlotRelative(const QMetaObject **m, 22 const char *slot, 23 bool normalizeStringData); 24 static int originalClone(const QMetaObject *obj, int local_method_index); 25 26 #ifndef QT_NO_QOBJECT 27 //defined in qobject.cpp 28 enum DisconnectType { DisconnectAll, DisconnectOne }; 29 static void memberIndexes(const QObject *obj, const QMetaMethod &member, 30 int *signalIndex, int *methodIndex); 31 static bool connect(const QObject *sender, int signal_index, 32 const QObject *receiver, int method_index_relative, 33 const QMetaObject *rmeta = 0, 34 int type = 0, int *types = 0); 35 static bool disconnect(const QObject *sender, int signal_index, 36 const QObject *receiver, int method_index, 37 DisconnectType = DisconnectAll); 38 static inline bool disconnectHelper(QObjectPrivate::Connection *c, 39 const QObject *receiver, int method_index, 40 QMutex *senderMutex, DisconnectType); 41 #endif 42 };
char 数组
配合上面的索引数组,继续看一下:
1 static const char qt_meta_stringdata_HPixmapScene[] = { 2 "HPixmapScene\0\0pix\0setPixmap(QPixmap)\0" 3 "img\0setImage(QImage)\0" 4 };
索引数组中 className 为0,我们看字符数组中从0开始是什么?恩,就是我们类的名字。
我们类的类中还定义的两个slot函数
public slots:
void setPixmap(const QPixmap& pix);
void setImage(const QImage& img);
我们看看是怎么对应的,
- 首先看索引数组 methodCount=2, methodData=14,告诉我们有两个method,数据从索引数组的14开始
-
然后,我们从索引数组的14开始看,{18, 14, 13, 13, 0x0a
- 这儿,18和14对应到我们的char数组了,看一下,分别是setPixmap(QPixmap) 和 pix。怎么样,和我们定义的函数对应上了吧。
- 其实上面有一处不太对,从开始就不太对。什么不对呢?
Qt manual 对此只提了一句,在QObject的manual中,除此之外,似乎再没出现过:要用 meta object system,我们不一定要继承QObject,普通的C++的类也可以部分使用,与此对应,我们不用Q_OBJECT宏,而用Q_GADGET宏,当然,这个只提供部分meta object system 的功能,比如Qt 元对象系统之 "enum自省"
另外:ActiveQt模块中,QAxObject和QAxWidget 定义了信号槽,却没有使用Q_OBJECT,具体可以看 ActiveQt模块学习(三)
另外 moc 生成的文件内有一个 Q_NO_DATA_RELOCATION 宏,无论用 Q_OBJECT 还是 Q_GADGET,我不清楚该宏起什么作用的。
整理思路太累了,写完这个,还不清楚什么时候再写(二)
参考
Qt属性系统(Qt's Property System)
使用
- 使用 Q_PROPERTY 宏来声明属性(即 将属性注册到meta-object系统中)。
- 通过QObject的metaObject即可获得用Q_PROPERTY注册的所有属性
- QMetaObject::property 返回QMetaProperty对象指针
- QMetaObject::propertyCount 属性个数(包含父类定义的属性)
- QMetaObject::propertyOffset 属性的开始索引
- QMetaObject::indexOfProperty 根据属性名得到属性索引
- QMetaObject::userProperty 带USER的属性,无参数(只能有一个?)
- 通过QMetaProperty可以获得属性的的各种信息
- 使用QObjecty可以读取或设置属性
- property
- setProperty
Q_PROPERTY
Qt属性是靠 Q_PROPERTY 宏进行声明的
1 Q_PROPERTY(type name READ getFunction 2 [WRITE setFunction] 3 [RESET resetFunction] 4 [NOTIFY notifySignal] 5 [DESIGNABLE bool] 6 [SCRIPTABLE bool] 7 [STORED bool] 8 [USER bool] 9 [CONSTANT] 10 [FINAL])
例子:
1 Q_PROPERTY(bool focus READ hasFocus) 2 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) 3 Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor) 4 Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)
- READ 该项必须!后接无参的const函数
- WRITE 后接无返回值的但参数函数
- RESET 后接无参无返回值的函数
- NOTIFT 后接单参的信号,属性变化时发射
- DESIGNABLE 布尔量(默认True),是否在designer中出现
- SCRIPTABLE 布尔量(默认True),是否用于脚本
- STORED 布尔量(默认True),是否“不依赖”其他值。比如mimimumWidth 依赖 mimimumSize,故为False
- USER 布尔量(默认False),是否是直接面向用户的。比如QPushButton的 checked 属性,则为 True
- CONSTANT 出现则代表常量。不可有 WRITE 及 NOTIFY
- FINAL 出现则代表不能被派生类重载?
从源代码文件 qobjectdefs.h 中可以看到该宏定义为空
1 #define Q_PROPERTY(TEXT)
也就是是说该宏仅对 moc 有意义,moc处理后生成的代码在 xxx.moc 或 moc_xxx.cpp 中。
moc
看一个例子:
1 Q_PROPERTY(bool colorDialog READ colorDialogEnabled WRITE setColorDialogEnabled)
查看对应的moc文件:
-
索引数据在 static const uint qt_meta_data_QtColorPicker[]
-
字符串数据在 static const char qt_meta_stringdata_QtColorPicker[]
- 并在 qt_metacall 函数中生成有相关的代码
自测如下-->
源码:
结果:
动态属性
通过QObject的setProperty 设置属性时,如果该属性不存在,则为设置为动态属性。动态属性存储在QObject中,而不是其QMetaObject对象中。
类型
- 对于 enum 类型,需要用 Q_ENUMS 或Q_FLAGS 将其注册到 meta-object系统中
- 对自定义类型,如果要用作属性,需要用 Q_DECLARE_METATYPE()将其注册到meta-object系统中。
对 PySide 及 PyQt4
1 QtCore.pyqtProperty(type, fget=None, fset=None, freset=None, fdel=None, doc=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False) 2 3 type 4 5 type of the property 6 7 fget 8 9 getter function 10 11 fset 12 13 None 14 15 setter function 16 17 freset 18 19 None 20 21 function used to reset the value of the property to its default value (only in C++) 22 23 fdel 24 25 None 26 27 function for del'ing the property (only in Python) 28 29 doc 30 31 None 32 33 docstring of the property 34 35 designable 36 37 True 38 39 value of Qt DESIGNABLE flag 40 41 scriptable 42 43 True 44 45 value of Qt SCRIPTABLE flag 46 47 stored 48 49 True 50 51 value of Qt STORED flag 52 53 user 54 55 False 56 57 value of Qt USER flag 58 59 constant 60 61 False 62 63 value of Qt CONSTANT flag 64 65 final 66 67 False 68 69 value of Qt FINAL flag
使用举例:
1 class MyObject(QObject): 2 def __init__(self,startval=42): 3 self.ppval = startval 4 def readPP(self): 5 return self.ppval 6 def setPP(self,val): 7 self.ppval = val 8 pp = pyqtProperty(int, readPP, setPP) 9 10 obj = MyObject() 11 obj.pp = 47 12 print obj.pp
-
PySide用Property来取代pyqtProPerty
-
PySide一开始出现一个失误,用的是QProperty而不是Property
-
PyQt4 为 fget 提供了默认的None,由于Qt要求READ函数必须有,故PySide不为其提供默认值
看 ActiveQt 模块的源码,看到信号槽部分,实在看不懂了,只好回来继续学习 元对象系统 了。
在前面的学习中中简单整理了Q_OBJECT宏 与 moc 生成的文件,并且从中学习了属性系统的宏 Q_PROPERTY。现在该看看源码中信号槽相关的内容了(Qt4.7的源码,其他版本与此可能不同)
1 QObject::connect
非常重要的函数,不是么?
函数一开始是这么一段:
1 bool QObject::connect(const QObject *sender, const char *signal, 2 const QObject *receiver, const char *method, 3 Qt::ConnectionType type) 4 { 5 { 6 const void *cbdata[] = { sender, signal, receiver, method, &type }; 7 if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) 8 return true; 9 } 10 11 。。。。。。 12 }
这个复合语句的作用看不太懂,QInternal类的定义在 src/corelib/global/qnamespace.h 中,类成员的实现在 src/corelib/global/qglobal.cpp 中。
1 bool QInternal::activateCallbacks(Callback cb, void **parameters) 2 { 3 Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id"); 4 5 QInternal_CallBackTable *cbt = global_callback_table(); 6 if (cbt && cb < cbt->callbacks.size()) { 7 QList<qInternalCallback> callbacks = cbt->callbacks[cb]; 8 bool ret = false; 9 for (int i=0; i<callbacks.size(); ++i) 10 ret |= (callbacks.at(i))(parameters); 11 return ret; 12 } 13 return false; 14 }
然后检测4个参数是否都非空
1 if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { 2 qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", 3 sender ? sender->metaObject()->className() : "(null)", 4 (signal && *signal) ? signal+1 : "(null)", 5 receiver ? receiver->metaObject()->className() : "(null)", 6 (method && *method) ? method+1 : "(null)"); 7 return false; 8 }
然后
1 QByteArray tmp_signal_name; 2 3 if (!check_signal_macro(sender, signal, "connect", "bind")) 4 return false; 5 const QMetaObject *smeta = sender->metaObject();
- 检测与信号连接的“信号或槽”是否符合格式,然后从元对象系统中获得相应的索引
检测信号与槽的参数是否匹配,对与Queued连接,检测参数是否注册到元对象系统
1 ++signal; //skip code 2 int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); 3 if (signal_index < 0) { 4 // check for normalized signatures 5 tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); 6 signal = tmp_signal_name.constData() + 1; 7 8 smeta = sender->metaObject(); 9 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); 10 } 11 if (signal_index < 0) { 12 // re-use tmp_signal_name and signal from above 13 14 smeta = sender->metaObject(); 15 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); 16 } 17 if (signal_index < 0) { 18 err_method_notfound(sender, signal_arg, "connect"); 19 err_info_about_objects("connect", sender, receiver); 20 return false; 21 } 22 signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); 23 int signalOffset, methodOffset; 24 computeOffsets(smeta, &signalOffset, &methodOffset); 25 int signal_absolute_index = signal_index + methodOffset; 26 signal_index += signalOffset; 27 28 QByteArray tmp_method_name; 29 int membcode = extract_code(method); 30 31 if (!check_method_code(membcode, receiver, method, "connect")) 32 return false; 33 const char *method_arg = method; 34 ++method; // skip code 35 36 const QMetaObject *rmeta = receiver->metaObject(); 37 int method_index_relative = -1; 38 switch (membcode) { 39 case QSLOT_CODE: 40 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 41 break; 42 case QSIGNAL_CODE: 43 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 44 break; 45 } 46 47 if (method_index_relative < 0) { 48 // check for normalized methods 49 tmp_method_name = QMetaObject::normalizedSignature(method); 50 method = tmp_method_name.constData(); 51 52 // rmeta may have been modified above 53 rmeta = receiver->metaObject(); 54 switch (membcode) { 55 case QSLOT_CODE: 56 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 57 if (method_index_relative < 0) 58 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); 59 break; 60 case QSIGNAL_CODE: 61 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 62 if (method_index_relative < 0) 63 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); 64 break; 65 } 66 } 67 68 if (method_index_relative < 0) { 69 err_method_notfound(receiver, method_arg, "connect"); 70 err_info_about_objects("connect", sender, receiver); 71 return false; 72 } 73 74 if (!QMetaObject::checkConnectArgs(signal, method)) { 75 qWarning("QObject::connect: Incompatible sender/receiver arguments" 76 "\n %s::%s --> %s::%s", 77 sender->metaObject()->className(), signal, 78 receiver->metaObject()->className(), method); 79 return false; 80 } 81 82 int *types = 0; 83 if ((type == Qt::QueuedConnection) 84 && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) 85 return false; 86 87 #ifndef QT_NO_DEBUG 88 if (warnCompat) { 89 QMetaMethod smethod = smeta->method(signal_absolute_index); 90 QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset()); 91 check_and_warn_compat(smeta, smethod, rmeta, rmethod); 92 } 93 #endif
若这一切都通过了,调用 QMetaObjectPrivate 的connect成员。调用成功,则调用 sender 对象的 connectNofify 函数(ActiveQt模块使用了该函数)
1 if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)) 2 return false; 3 const_cast<QObject*>(sender)->connectNotify(signal - 1); 4 return true; 5 }
QMetaObjectPrivate::connect首先,检测是否是 uniqueconnection ,如果是,则先搜索已有的连接,找到则函数返回
1 bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, 2 const QObject *receiver, int method_index, 3 const QMetaObject *rmeta, int type, int *types) 4 { 5 QObject *s = const_cast<QObject *>(sender); 6 QObject *r = const_cast<QObject *>(receiver); 7 8 int method_offset = rmeta ? rmeta->methodOffset() : 0; 9 QObjectPrivate::StaticMetaCallFunction callFunction = 10 (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata) 11 ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0; 12 13 QOrderedMutexLocker locker(signalSlotLock(sender), 14 signalSlotLock(receiver)); 15 16 if (type & Qt::UniqueConnection) { 17 QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; 18 if (connectionLists && connectionLists->count() > signal_index) { 19 const QObjectPrivate::Connection *c2 = 20 (*connectionLists)[signal_index].first; 21 22 int method_index_absolute = method_index + method_offset; 23 24 while (c2) { 25 if (c2->receiver == receiver && c2->method() == method_index_absolute) 26 return false; 27 c2 = c2->nextConnectionList; 28 } 29 } 30 type &= Qt::UniqueConnection - 1; 31 }
创建连接
1 QObjectPrivate::Connection *c = new QObjectPrivate::Connection; 2 c->sender = s; 3 c->receiver = r; 4 c->method_relative = method_index; 5 c->method_offset = method_offset; 6 c->connectionType = type; 7 c->argumentTypes = types; 8 c->nextConnectionList = 0; 9 c->callFunction = callFunction; 10 11 QT_TRY { 12 QObjectPrivate::get(s)->addConnection(signal_index, c); 13 } QT_CATCH(...) { 14 delete c; 15 QT_RETHROW; 16 } 17 18 c->prev = &(QObjectPrivate::get(r)->senders); 19 c->next = *c->prev; 20 *c->prev = c; 21 if (c->next) 22 c->next->prev = &c->next; 23 24 QObjectPrivate *const sender_d = QObjectPrivate::get(s); 25 if (signal_index < 0) { 26 sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; 27 } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { 28 sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); 29 } 30 31 return true; 32 }
其他还有部分代码,看不太懂,暂略(应该不影响理解)
QMetaObject::activate
信号的定义体在 moc 生成的文件内,在定义体中会调用 QMetaObject::activate 函数来实现信号的功能。
首先判断该信号有无被链接,是否是block设置(具体含义?)
1 void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, 2 void **argv) 3 { 4 int signalOffset; 5 int methodOffset; 6 computeOffsets(m, &signalOffset, &methodOffset); 7 8 int signal_index = signalOffset + local_signal_index; 9 10 if (!sender->d_func()->isSignalConnected(signal_index)) 11 return; // nothing connected to these signals, and no spy 12 13 if (sender->d_func()->blockSig) 14 return;
和signal spy有关的一些代码
1 int signal_absolute_index = methodOffset + local_signal_index; 2 3 void *empty_argv[] = { 0 }; 4 if (qt_signal_spy_callback_set.signal_begin_callback != 0) { 5 qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index, 6 argv ? argv : empty_argv); 7 } 8 9 Qt::HANDLE currentThreadId = QThread::currentThreadId(); 10 11 QMutexLocker locker(signalSlotLock(sender)); 12 QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; 13 if (!connectionLists) { 14 locker.unlock(); 15 if (qt_signal_spy_callback_set.signal_end_callback != 0) 16 qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); 17 return; 18 } 19 ++connectionLists->inUse;
用双重循环依次处理每一个信号的每一个连接
1 const QObjectPrivate::ConnectionList *list; 2 if (signal_index < connectionLists->count()) 3 list = &connectionLists->at(signal_index); 4 else 5 list = &connectionLists->allsignals; 6 7 do { 8 QObjectPrivate::Connection *c = list->first; 9 if (!c) continue; 10 // We need to check against last here to ensure that signals added 11 // during the signal emission are not emitted in this emission. 12 QObjectPrivate::Connection *last = list->last; 13 14 do { 15 if (!c->receiver) 16 continue; 17 18 QObject * const receiver = c->receiver; 19 const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId; 20 21 // determine if this connection should be sent immediately or 22 // put into the event queue 23 if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) 24 || (c->connectionType == Qt::QueuedConnection)) { //对queued的连接单独处理 25 queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv); 26 continue; 27 #ifndef QT_NO_THREAD 28 } else if (c->connectionType == Qt::BlockingQueuedConnection) { 29 locker.unlock(); 30 if (receiverInSameThread) { 31 qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " 32 "Sender is %s(%p), receiver is %s(%p)", 33 sender->metaObject()->className(), sender, 34 receiver->metaObject()->className(), receiver); 35 } 36 QSemaphore semaphore; 37 QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative, 38 c->callFunction, 39 sender, signal_absolute_index, 40 0, 0, 41 argv ? argv : empty_argv, 42 &semaphore)); 43 semaphore.acquire(); 44 locker.relock(); 45 continue; 46 #endif 47 } 48 //对其他连接,继续(发送与接收者在同一线程,则设置当前发送者,QObject::sender() 需要该功能) 49 QObjectPrivate::Sender currentSender; 50 QObjectPrivate::Sender *previousSender = 0; 51 if (receiverInSameThread) { 52 currentSender.sender = sender; 53 currentSender.signal = signal_absolute_index; 54 currentSender.ref = 1; 55 previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender); 56 } 57 const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction; 58 const int method_relative = c->method_relative; 59 if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) { 60 //we compare the vtable to make sure we are not in the destructor of the object. 61 locker.unlock(); 62 if (qt_signal_spy_callback_set.slot_begin_callback != 0) 63 qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv); 64 65 #if defined(QT_NO_EXCEPTIONS) 66 callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv); 67 #else 68 QT_TRY { 69 callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv); 70 } QT_CATCH(...) { 71 locker.relock(); 72 if (receiverInSameThread) 73 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); 74 75 --connectionLists->inUse; 76 Q_ASSERT(connectionLists->inUse >= 0); 77 if (connectionLists->orphaned && !connectionLists->inUse) 78 delete connectionLists; 79 QT_RETHROW; 80 } 81 #endif 82 if (qt_signal_spy_callback_set.slot_end_callback != 0) 83 qt_signal_spy_callback_set.slot_end_callback(receiver, c->method()); 84 locker.relock(); 85 } else { 86 const int method = method_relative + c->method_offset; 87 locker.unlock(); 88 89 if (qt_signal_spy_callback_set.slot_begin_callback != 0) { 90 qt_signal_spy_callback_set.slot_begin_callback(receiver, 91 method, 92 argv ? argv : empty_argv); 93 } 94 //调用metacall 95 #if defined(QT_NO_EXCEPTIONS) 96 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); 97 #else 98 QT_TRY { 99 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); 100 } QT_CATCH(...) { 101 locker.relock(); 102 if (receiverInSameThread) 103 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); 104 105 --connectionLists->inUse; 106 Q_ASSERT(connectionLists->inUse >= 0); 107 if (connectionLists->orphaned && !connectionLists->inUse) 108 delete connectionLists; 109 QT_RETHROW; 110 } 111 #endif 112 113 if (qt_signal_spy_callback_set.slot_end_callback != 0) 114 qt_signal_spy_callback_set.slot_end_callback(receiver, method); 115 116 locker.relock(); 117 } 118 119 if (receiverInSameThread) 120 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); 121 122 if (connectionLists->orphaned) 123 break; 124 } while (c != last && (c = c->nextConnectionList) != 0); 125 126 if (connectionLists->orphaned) 127 break; 128 } while (list != &connectionLists->allsignals && 129 //start over for all signals; 130 ((list = &connectionLists->allsignals), true)); 131 132 --connectionLists->inUse; 133 Q_ASSERT(connectionLists->inUse >= 0); 134 if (connectionLists->orphaned) { 135 if (!connectionLists->inUse) 136 delete connectionLists; 137 } else if (connectionLists->dirty) { 138 sender->d_func()->cleanConnectionLists(); 139 } 140 141 locker.unlock(); 142 143 if (qt_signal_spy_callback_set.signal_end_callback != 0) 144 qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); 145 146 }
接下来看metacall代码
1 int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) 2 { 3 if (QMetaObject *mo = object->d_ptr->metaObject) 4 return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv); 5 else 6 return object->qt_metacall(cl, idx, argv); 7 }
前面的 QAbstractDynamicMetaObject 部分不清楚作用,后面的 qt_metacall 比较好理解了(代码在moc生成的文件中)
queued_activate
首先是参数检测,然后是构建 QMetaCallEvent 事件,并post该事件
1 static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) 2 { 3 if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { 4 QMetaMethod m = sender->metaObject()->method(signal); 5 int *tmp = queuedConnectionTypes(m.parameterTypes()); 6 if (!tmp) // cannot queue arguments 7 tmp = &DIRECT_CONNECTION_ONLY; 8 if (!c->argumentTypes.testAndSetOrdered(0, tmp)) { 9 if (tmp != &DIRECT_CONNECTION_ONLY) 10 delete [] tmp; 11 } 12 } 13 if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate 14 return; 15 int nargs = 1; // include return type 16 while (c->argumentTypes[nargs-1]) 17 ++nargs; 18 int *types = (int *) qMalloc(nargs*sizeof(int)); 19 Q_CHECK_PTR(types); 20 void **args = (void **) qMalloc(nargs*sizeof(void *)); 21 Q_CHECK_PTR(args); 22 types[0] = 0; // return type 23 args[0] = 0; // return value 24 for (int n = 1; n < nargs; ++n) 25 args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]); 26 QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset, 27 c->method_relative, 28 c->callFunction, 29 sender, signal, nargs, 30 types, args)); 31 }
blocking_activate
首先,检测是否是同一线程,然后发送事件 QMetaCallEvent
既然和事件有关系,就看看事件是怎么回事
1 case QEvent::MetaCall: 2 { 3 #ifdef QT_JAMBI_BUILD 4 d_func()->inEventHandler = false; 5 #endif 6 QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e); 7 QObjectPrivate::Sender currentSender; 8 currentSender.sender = const_cast<QObject*>(mce->sender()); 9 currentSender.signal = mce->signalId(); 10 currentSender.ref = 1; 11 QObjectPrivate::Sender * const previousSender = 12 QObjectPrivate::setCurrentSender(this, ¤tSender); 13 #if defined(QT_NO_EXCEPTIONS) 14 mce->placeMetaCall(this); 15 #else 16 QT_TRY { 17 mce->placeMetaCall(this); 18 } QT_CATCH(...) { 19 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 20 QT_RETHROW; 21 } 22 #endif 23 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 24 break; 25 }
可以看到,这儿调用了 placeMetaCall
1 void QMetaCallEvent::placeMetaCall(QObject *object) 2 { 3 if (callFunction_) { 4 callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_); 5 } else { 6 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_); 7 } 8 }
参考
-
http://blog.csdn.net/tingsking18/archive/2009/11/12/4800828.aspx
-
http://blog.csdn.net/tingsking18/archive/2009/12/12/4991563.aspx
基本理解
- Q_DECLARE_METATYPE
- 如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。
- 该类型必须有公有的 构造、析构、复制构造 函数
- qRegisterMetaType 必须使用该函数的两种情况
- 如果非QMetaType内置类型要在 Qt 的属性系统中使用
- 如果非QMetaType内置类型要在 queued 信号与槽 中使用
二者关系
二者的代码:
-
Q_DECLARE_METATYPE 展开后是一个特化后的类 QMetaTypeId<TYPE>
-
qRegisterMetaType 将某类型注册中 MetaType 系统中
二者的联系:
-
QMetaTypeId<TYPE>的类中的成员包含对qRegisterMetaType的调用
-
我们知道类中的成员函数并不一定会被调用(即,该宏并不确保类型被注册到MetaType)。
- 通过qRegisterMetaType可以确保类型被注册
两个qRegisterMetaType 的联系
- 无参的qRegisterMetaType函数会通过该成员调用带参数的qRegisterMetaType()
这两个东西真难理清,不妨看看源码吧。
1 Q_DECLARE_METATYPE
代码来源:src/corelib/kernel/qmetatype.h
1 #define Q_DECLARE_METATYPE(TYPE) \ 2 QT_BEGIN_NAMESPACE \ 3 template <> \ 4 struct QMetaTypeId< TYPE > \ 5 { \ 6 enum { Defined = 1 }; \ 7 static int qt_metatype_id() \ 8 { \ 9 static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \ 10 if (!metatype_id) \ 11 metatype_id = qRegisterMetaType< TYPE >(#TYPE, \ 12 reinterpret_cast< TYPE *>(quintptr(-1))); \ 13 return metatype_id; \ 14 } \ 15 }; \ 16 QT_END_NAMESPACE
-
宏展开是一个在Qt的命名空间中的一个类模板的特化 QMetaTypeId<TYPE>
- 该类含一个enum和一个返回!QMetaType的id的成员函数
qRegisterMetaType(const char *typeName)
代码来源:src/corelib/kernel/qmetatype.h
1 template <typename T> 2 int qRegisterMetaType(const char *typeName 3 #ifndef qdoc 4 , T * dummy = 0 5 #endif 6 ) 7 { 8 const int typedefOf = dummy ? -1 : QtPrivate::QMetaTypeIdHelper<T>::qt_metatype_id(); 9 if (typedefOf != -1) 10 return QMetaType::registerTypedef(typeName, typedefOf); 11 12 typedef void*(*ConstructPtr)(const T*); 13 ConstructPtr cptr = qMetaTypeConstructHelper<T>; 14 typedef void(*DeletePtr)(T*); 15 DeletePtr dptr = qMetaTypeDeleteHelper<T>; 16 17 return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr), 18 reinterpret_cast<QMetaType::Constructor>(cptr)); 19 }
- 该函数的核心就是调用了registerType 函数
- 两个Helper模板函数分别对构造和析构函数进行封装
registerType
代码来源:src/corelib/kernel/qmetatype.cpp
1 int QMetaType::registerType(const char *typeName, Destructor destructor, 2 Constructor constructor) 3 { 4 QVector<QCustomTypeInfo> *ct = customTypes(); 5 if (!ct || !typeName || !destructor || !constructor) 6 return -1; 7 8 #ifdef QT_NO_QOBJECT 9 NS(QByteArray) normalizedTypeName = typeName; 10 #else 11 NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); 12 #endif 13 14 int idx = qMetaTypeStaticType(normalizedTypeName.constData(), 15 normalizedTypeName.size()); 16 17 if (!idx) { 18 QWriteLocker locker(customTypesLock()); 19 idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), 20 normalizedTypeName.size()); 21 if (!idx) { 22 QCustomTypeInfo inf; 23 inf.typeName = normalizedTypeName; 24 inf.constr = constructor; 25 inf.destr = destructor; 26 inf.alias = -1; 27 idx = ct->size() + User; 28 ct->append(inf); 29 } 30 } 31 return idx; 32 }
函数功能:
-
根据类型名查找其MetaType类型,如果已存在,则直接返回;否则创建后返回。
- 创建一个 !QCustomTypeInfo 对象
- 该对象包含要类型的构造、析构信息,已经规范化后的类型名
- 该对象存入一个全局的!QVector中
qRegisterMetaType()
看manual,可以知道,qRegisterMetaType 还有一个无参的重载函数。
1 template <typename T> 2 inline int qRegisterMetaType() 3 { 4 return qMetaTypeId(static_cast<T *>(0)); 5 }
- 函数看起来和带参数的那个似乎区别很大(难道不是么?)。
- 手册中告诉我们,执行这个的时候,模板参数T必须用 Q_DECLARE_METATYPE() 声明过
- 能猜到原因吗?注意看前面 Q_DECLARE_METATYPE() 代码,
- 对了。类中的成员函数qt_metatype_id中包含对qRegisterMetaType(typeName)的调用
-
这儿就是辗转调用了这个带参数的qRegisterMetaType函数
unregisterType(const char *typeName)
函数的作用是取消自己先前注册的某个metatype类型。
前面提到注册信息在一个全局的 QVector<QCustomTypeInfo>中,当取消注册的时候是怎么样的呢?直接删除Vector中相应的项么?源码告诉我们,不是的。
实际是查找到相应的项,清空该项的内容
1 void QMetaType::unregisterType(const char *typeName) 2 { 3 QVector<QCustomTypeInfo> *ct = customTypes(); 4 if (!ct || !typeName) 5 return; 6 7 #ifdef QT_NO_QOBJECT 8 NS(QByteArray) normalizedTypeName = typeName; 9 #else 10 NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); 11 #endif 12 QWriteLocker locker(customTypesLock()); 13 for (int v = 0; v < ct->count(); ++v) { 14 if (ct->at(v).typeName == typeName) { 15 QCustomTypeInfo &inf = (*ct)[v]; 16 inf.typeName.clear(); 17 inf.constr = 0; 18 inf.destr = 0; 19 inf.alias = -1; 20 } 21 } 22 }
Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC
这两个宏在Qt的源码中随处可见,重要性不言而喻。在 部落格的 Inside Qt Series 系列文章中,他用了3篇文章来讲这个问题。
因为 QObject 本身比较复杂,这两个宏和一个复杂的东西搅和到一块,还真是不好理解。不过幸好,这个两个宏和QObject 没有必然的联系。故接下来,忘记 QObject,看一个普通的C++的类
例子
类 QtServiceController 定义:
1 class QtServiceController 2 { 3 Q_DECLARE_PRIVATE(QtServiceController) 4 public: 5 QtServiceController(const QString &name); 6 //省略其他 7 private: 8 QtServiceControllerPrivate *d_ptr; 9 };
类 QtServiceControllerPrivate 定义:
1 class QtServiceControllerPrivate 2 { 3 Q_DECLARE_PUBLIC(QtServiceController) 4 public: 5 QString serviceName; 6 QtServiceController *q_ptr; 7 };
将所有的private数据成员,独立出来放于一个独立的私有的数据对象中。这一点是比较好理解的,那么这两个宏在这起什么作用呢?
注意:上面定义的两个指针 d_ptr, q_ptr。
宏定义
宏定义在 QtGlobal(即qglobal.h)头文件中:
1 #define Q_DECLARE_PRIVATE(Class) \ 2 inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \ 3 inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \ 4 friend class Class##Private; 5 6 #define Q_DECLARE_PRIVATE_D(Dptr, Class) \ 7 inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } \ 8 inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } \ 9 friend class Class##Private; 10 11 #define Q_DECLARE_PUBLIC(Class) \ 12 inline Class* q_func() { return static_cast<Class *>(q_ptr); } \ 13 inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \ 14 friend class Class;
这两个宏在这看起来真蛮绕的,因为这个例子太简单了,两个宏的威力发挥不出来。反正核心就是:
-
在 QtServiceController 中通过 d_func() 可以获得 QtServiceControllerPrivate 的指针 d_ptr
-
在 QtServiceControllerPrivate 中通过 q_func() 可以获得 QtServiceController 的指针 q_ptr
Q_D 与 Q_Q
这是另两个Qt源码中随处可见的宏,那么它们有什么用呢?
1 #define Q_D(Class) Class##Private * const d = d_func() 2 #define Q_Q(Class) Class * const q = q_func()
两个宏展开后分别是对 d_func 和 q_func 两个函数的调用,返回值分别赋值给 d 和 q 两个指针变量。
于是:
-
在 QtServiceController 中的成员函数中,我们只需要添加 Q_D(QtServiceController) 宏,在该函数内就可以直接用 d 来指代 d_ptr
-
在 QtServiceControllerPrivate 中的成员函数中,我们只需要添加 Q_Q(QtServiceController)宏,在该函数内就可以直接用 q 来指代 q_ptr
d_ptr与q_ptr
绕这么大圈,为什么不直接用 d_ptr 与 q_ptr 呢。在,在我们的例子中,确实可以直接用,而且会更直接更简单。官方这么用了,或许是为了和其他类保持一致吧。
但在其他情况下,这么做显然是有意义的,因为 d_ptr 与 d,q_ptr 与 q 的类型并不一致(比如QObject系列)。这也是为何宏展开后有cast的原因。
参考
C++ GUI Qt4 编程 一书多线程部分提到invokeMethod的用法
QMetaObject::invokeMethod(label, SLOT(setText(const QString&)), Q_ARG(QString, "Hello"));
而 Qt Manual 中介绍却是
- You only need to pass the name of the signal or slot to this function, not the entire signature. For example, to asynchronously invoke the animateClick() slot on a QPushButton, use the following code:
QMetaObject::invokeMethod(pushButton, "animateClick");
这可怎么办?一个是官方的图书,一个是官方的Manual。是否意味着两种方式都可以呢,还是说Qt的早期版本用的是前者?
查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,结果发现都没有提到前面的用法。是不是书的出错呢?网上搜一下:确实有人抱怨它不工作
测试
本着事实就是的精神,还是先写个程序测试一下:
1 #include <QtCore/QObject> 2 #include <QtCore/QDebug> 3 #include <QtCore/QCoreApplication> 4 class Test : public QObject 5 { 6 Q_OBJECT 7 public: 8 Test(QObject * parent):QObject(parent) 9 { 10 connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString))); 11 QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor")); 12 } 13 Q_INVOKABLE void method1(const QString& t) 14 { 15 qDebug()<<"from method:"<<t; 16 } 17 18 signals: 19 void sig1(const QString& t); 20 21 public slots: 22 void slot1(const QString& t) 23 { 24 qDebug()<<"from slot:"<<t; 25 } 26 }; 27 #include "main.moc" 28 int main(int argc, char *argv[]) 29 { 30 QCoreApplication a(argc, argv); 31 Test obj(0); 32 QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello")); 33 QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello")); 34 QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT")); 35 QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD")); 36 return a.exec(); 37 }
确实如他人所说,SLOT这种用法不工作
1 from slot: "constructor" 2 from slot: "Hello" 3 from method: "Hello" 4 QMetaObject::invokeMethod: No such method Test::1slot1(QString)(QString) 5 QMetaObject::invokeMethod: No such method Test::0method1(QString)(QString)
顺便看看源码吧
1 bool QMetaObject::invokeMethod(QObject *obj, 2 const char *member, 3 Qt::ConnectionType type, 4 QGenericReturnArgument ret, 5 QGenericArgument val0, 6 QGenericArgument val1, 7 QGenericArgument val2, 8 QGenericArgument val3, 9 QGenericArgument val4, 10 QGenericArgument val5, 11 QGenericArgument val6, 12 QGenericArgument val7, 13 QGenericArgument val8, 14 QGenericArgument val9) 15 { 16 if (!obj) 17 return false; 18 19 QVarLengthArray<char, 512> sig; 20 int len = qstrlen(member); 21 if (len <= 0) 22 return false;
//生成函数原型字符串(从这儿可以看到书中方法不工作的原因) 23 sig.append(member, len); 24 sig.append('('); 25 26 const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), 27 val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), 28 val9.name()}; 29 30 int paramCount; 31 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 32 len = qstrlen(typeNames[paramCount]); 33 if (len <= 0) 34 break; 35 sig.append(typeNames[paramCount], len); 36 sig.append(','); 37 } 38 if (paramCount == 1) 39 sig.append(')'); // no parameters 40 else 41 sig[sig.size() - 1] = ')'; 42 sig.append('\0'); 43 //在元对象系统中看该函数信息是否存在 44 int idx = obj->metaObject()->indexOfMethod(sig.constData()); 45 if (idx < 0) { 46 QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); 47 idx = obj->metaObject()->indexOfMethod(norm.constData()); 48 } 49 50 if (idx < 0 || idx >= obj->metaObject()->methodCount()) { 51 qWarning("QMetaObject::invokeMethod: No such method %s::%s", 52 obj->metaObject()->className(), sig.constData()); 53 return false; 54 }
//获得相应的 QMetaMethod,调用其 invoke 方法 55 QMetaMethod method = obj->metaObject()->method(idx); 56 return method.invoke(obj, type, ret, 57 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 58 }
接着看看它的源码,首先:
* 如果指定了返回值,检查返回值的类型是否和QMetaMethod 的中的一致
1 bool QMetaMethod::invoke(QObject *object, 2 Qt::ConnectionType connectionType, 3 QGenericReturnArgument returnValue, 4 QGenericArgument val0, 5 QGenericArgument val1, 6 QGenericArgument val2, 7 QGenericArgument val3, 8 QGenericArgument val4, 9 QGenericArgument val5, 10 QGenericArgument val6, 11 QGenericArgument val7, 12 QGenericArgument val8, 13 QGenericArgument val9) const 14 { 15 if (!object || !mobj) 16 return false; 17 18 Q_ASSERT(mobj->cast(object)); 19 20 // check return type 21 if (returnValue.data()) { 22 const char *retType = typeName(); 23 if (qstrcmp(returnValue.name(), retType) != 0) { 24 // normalize the return value as well 25 // the trick here is to make a function signature out of the return type 26 // so that we can call normalizedSignature() and avoid duplicating code 27 QByteArray unnormalized; 28 int len = qstrlen(returnValue.name()); 29 30 unnormalized.reserve(len + 3); 31 unnormalized = "_("; // the function is called "_" 32 unnormalized.append(returnValue.name()); 33 unnormalized.append(')'); 34 35 QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData()); 36 normalized.truncate(normalized.length() - 1); // drop the ending ')' 37 38 if (qstrcmp(normalized.constData() + 2, retType) != 0) 39 return false; 40 } 41 } 42 43 // check argument count (we don't allow invoking a method if given too few arguments) 44 const char *typeNames[] = { 45 returnValue.name(), 46 val0.name(), 47 val1.name(), 48 val2.name(), 49 val3.name(), 50 val4.name(), 51 val5.name(), 52 val6.name(), 53 val7.name(), 54 val8.name(), 55 val9.name() 56 }; 57 int paramCount; 58 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 59 if (qstrlen(typeNames[paramCount]) <= 0) 60 break; 61 } 62 int metaMethodArgumentCount = 0; 63 { 64 // based on QMetaObject::parameterNames() 65 const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; 66 if (*names == 0) { 67 // do we have one or zero arguments? 68 const char *signature = mobj->d.stringdata + mobj->d.data[handle]; 69 while (*signature && *signature != '(') 70 ++signature; 71 if (*++signature != ')') 72 ++metaMethodArgumentCount; 73 } else { 74 --names; 75 do { 76 ++names; 77 while (*names && *names != ',') 78 ++names; 79 ++metaMethodArgumentCount; 80 } while (*names); 81 } 82 } 83 if (paramCount <= metaMethodArgumentCount) 84 return false; 85
//为了利用现有的代码来规范化这儿返回值的类型,这儿构造了一个函数_(typeOfReturn)。
//检查参数个数,传递的参数是否不少于需要的参数
//检查Connection的类型,处理AutoConnection
86 // check connection type 87 QThread *currentThread = QThread::currentThread(); 88 QThread *objectThread = object->thread(); 89 if (connectionType == Qt::AutoConnection) { 90 connectionType = currentThread == objectThread 91 ? Qt::DirectConnection 92 : Qt::QueuedConnection; 93 } 94 95 #ifdef QT_NO_THREAD 96 if (connectionType == Qt::BlockingQueuedConnection) { 97 connectionType = Qt::DirectConnection; 98 } 99 #endif 100 101 // invoke! 102 void *param[] = { 103 returnValue.data(), 104 val0.data(), 105 val1.data(), 106 val2.data(), 107 val3.data(), 108 val4.data(), 109 val5.data(), 110 val6.data(), 111 val7.data(), 112 val8.data(), 113 val9.data() 114 }; 115 // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() 116 int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5); 117 int idx_offset = mobj->methodOffset(); 118 QObjectPrivate::StaticMetaCallFunction callFunction = 119 (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata) 120 ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0; 121 //对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall 122 if (connectionType == Qt::DirectConnection) { 123 if (callFunction) { 124 callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param); 125 return true; 126 } else { 127 return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0; 128 } 129 } else if (connectionType == Qt::QueuedConnection) { //对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中 130 if (returnValue.data()) { 131 qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " 132 "queued connections"); 133 return false; 134 } 135 136 int nargs = 1; // include return type 137 void **args = (void **) qMalloc(paramCount * sizeof(void *)); 138 Q_CHECK_PTR(args); 139 int *types = (int *) qMalloc(paramCount * sizeof(int)); 140 Q_CHECK_PTR(types); 141 types[0] = 0; // return type 142 args[0] = 0; 143 144 for (int i = 1; i < paramCount; ++i) { 145 types[i] = QMetaType::type(typeNames[i]); 146 if (types[i]) { 147 args[i] = QMetaType::construct(types[i], param[i]); 148 ++nargs; 149 } else if (param[i]) { 150 qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", 151 typeNames[i]); 152 for (int x = 1; x < i; ++x) { 153 if (types[x] && args[x]) 154 QMetaType::destroy(types[x], args[x]); 155 } 156 qFree(types); 157 qFree(args); 158 return false; 159 } 160 } 161 162 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 163 0, -1, nargs, types, args)); 164 } else { // blocking queued connection 165 #ifndef QT_NO_THREAD 166 if (currentThread == objectThread) { 167 qWarning("QMetaMethod::invoke: Dead lock detected in " 168 "BlockingQueuedConnection: Receiver is %s(%p)", 169 mobj->className(), object); 170 } 171 //对于 bolckedqueued 的连接,使用了信号量 172 QSemaphore semaphore; 173 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 174 0, -1, 0, 0, param, &semaphore)); 175 semaphore.acquire(); 176 #endif // QT_NO_THREAD 177 } 178 return true; 179 }