Qt Meta Object system 學習

原文地址:http://blog.csdn.net/ilvu999/article/details/8049908html

 

使用 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 程序對該宏的處理

宏定義app

 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函數async

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 處理ide

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, &currentSender);
 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, &currentSender, 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, &currentSender, 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, &currentSender, 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, &currentSender);
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, &currentSender, previousSender);
20                 QT_RETHROW;
21             }
22 #endif
23             QObjectPrivate::resetCurrentSender(this, &currentSender, 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 }

 

參考

 

基本理解

  • 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 }
相關文章
相關標籤/搜索