Qt Meta Object system 学习

使用 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 程序对该宏的处理

宏定义

#define Q_OBJECT \
public: \
    Q_OBJECT_CHECK \
    static const QMetaObject staticMetaObject; \
    Q_OBJECT_GETSTATICMETAOBJECT \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    QT_TR_FUNCTIONS \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
    Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
    Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);

而宏 QT_TR_FUNCTIONS 将展开为我们使程序国际化经常使用的 tr 与 trUtf8函数

#  define QT_TR_FUNCTIONS \
    static inline QString tr(const char *s, const char *c = 0) \
        { return staticMetaObject.tr(s, c); } \
    static inline QString trUtf8(const char *s, const char *c = 0) \
        { return staticMetaObject.trUtf8(s, c); } \
    static inline QString tr(const char *s, const char *c, int n) \
        { return staticMetaObject.tr(s, c, n); } \
    static inline QString trUtf8(const char *s, const char *c, int n) \
        { 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

扫描二维码关注公众号,回复: 2326583 查看本文章

既然 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 定义的一个类,我们略过其它,只看其数据成员

struct Q_CORE_EXPORT QMetaObject
{
    const char *className() const;
    const QMetaObject *superClass() const;

    QObject *cast(QObject *obj) const;
    const QObject *cast(const QObject *obj) const;

#ifndef QT_NO_TRANSLATION
    // ### Qt 4: Merge overloads
    QString tr(const char *s, const char *c) const;
    QString trUtf8(const char *s, const char *c) const;
    QString tr(const char *s, const char *c, int n) const;
    QString trUtf8(const char *s, const char *c, int n) const;
#endif // QT_NO_TRANSLATION

    int methodOffset() const;
    int enumeratorOffset() const;
    int propertyOffset() const;
    int classInfoOffset() const;

    int constructorCount() const;
    int methodCount() const;
    int enumeratorCount() const;
    int propertyCount() const;
    int classInfoCount() const;

    int indexOfConstructor(const char *constructor) const;
    int indexOfMethod(const char *method) const;
    int indexOfSignal(const char *signal) const;
    int indexOfSlot(const char *slot) const;
    int indexOfEnumerator(const char *name) const;
    int indexOfProperty(const char *name) const;
    int indexOfClassInfo(const char *name) const;

    QMetaMethod constructor(int index) const;
    QMetaMethod method(int index) const;
    QMetaEnum enumerator(int index) const;
    QMetaProperty property(int index) const;
    QMetaClassInfo classInfo(int index) const;
    QMetaProperty userProperty() const;

    static bool checkConnectArgs(const char *signal, const char *method);
    static QByteArray normalizedSignature(const char *method);
    static QByteArray normalizedType(const char *type);

    // internal index-based connect
    static bool connect(const QObject *sender, int signal_index,
                        const QObject *receiver, int method_index,
                        int type = 0, int *types = 0);
    // internal index-based disconnect
    static bool disconnect(const QObject *sender, int signal_index,
                           const QObject *receiver, int method_index);
    static bool disconnectOne(const QObject *sender, int signal_index,
                              const QObject *receiver, int method_index);
    // internal slot-name based connect
    static void connectSlotsByName(QObject *o);

    // internal index-based signal activation
    static void activate(QObject *sender, int signal_index, void **argv);  //obsolete
    static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
    static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete

    // internal guarded pointers
    static void addGuard(QObject **ptr);
    static void removeGuard(QObject **ptr);
    static void changeGuard(QObject **ptr, QObject *o);

    static bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument());

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
                val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType type,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
                                 val3, val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
                val1, val2, val3, val4, val5, val6, val7, val8, val9);
    }

    QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),
                         QGenericArgument val1 = QGenericArgument(),
                         QGenericArgument val2 = QGenericArgument(),
                         QGenericArgument val3 = QGenericArgument(),
                         QGenericArgument val4 = QGenericArgument(),
                         QGenericArgument val5 = QGenericArgument(),
                         QGenericArgument val6 = QGenericArgument(),
                         QGenericArgument val7 = QGenericArgument(),
                         QGenericArgument val8 = QGenericArgument(),
                         QGenericArgument val9 = QGenericArgument()) const;

    enum Call {
        InvokeMetaMethod,
        ReadProperty,
        WriteProperty,
        ResetProperty,
        QueryPropertyDesignable,
        QueryPropertyScriptable,
        QueryPropertyStored,
        QueryPropertyEditable,
        QueryPropertyUser,
        CreateInstance
    };

    int static_metacall(Call, int, void **) const;
    static int metacall(QObject *, Call, int, void **);

#ifdef QT3_SUPPORT
    QT3_SUPPORT const char *superClassName() const;
#endif

    struct { // private data
        const QMetaObject *superdata;
        const char *stringdata;
        const uint *data;
        const void *extradata;
    } d;
};

其中呢,

  • superdata,指向父类的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字符串中提取信息

static const uint qt_meta_data_HPixmapScene[] = {

// content:
4, // revision
0, // classname
0, 0, // classinfo
2, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount

// slots: signature, parameters, type, tag, flags
18, 14, 13, 13, 0x0a,
41, 37, 13, 13, 0x0a,

0 // eod
};

对比QMetaObject用到的数据结构 QMetaObjectPrivate,看看,是不是豁然开朗了:uint 数组中的第一段 就对应这个结构体

struct QMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData; //since revision 2
    int flags; //since revision 3
    int signalCount; //since revision 4
    // revision 5 introduces changes in normalized signatures, no new members
    // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself

    static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject)
    { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }

    static int indexOfSignalRelative(const QMetaObject **baseObject,
                                     const char* name,
                                     bool normalizeStringData);
    static int indexOfSlotRelative(const QMetaObject **m,
                           const char *slot,
                           bool normalizeStringData);
    static int originalClone(const QMetaObject *obj, int local_method_index);

#ifndef QT_NO_QOBJECT
    //defined in qobject.cpp
    enum DisconnectType { DisconnectAll, DisconnectOne };
    static void memberIndexes(const QObject *obj, const QMetaMethod &member,
                              int *signalIndex, int *methodIndex);
    static bool connect(const QObject *sender, int signal_index,
                        const QObject *receiver, int method_index_relative,
                        const QMetaObject *rmeta = 0,
                        int type = 0, int *types = 0);
    static bool disconnect(const QObject *sender, int signal_index,
                           const QObject *receiver, int method_index,
                           DisconnectType = DisconnectAll);
    static inline bool disconnectHelper(QObjectPrivate::Connection *c,
                                        const QObject *receiver, int method_index,
                                        QMutex *senderMutex, DisconnectType);
#endif
};

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属性系统(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 宏进行声明的

Q_PROPERTY(type name  READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[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 中可以看到该宏定义为空

 #define Q_PROPERTY(TEXT)

也就是是说该宏仅对 moc 有意义,moc处理后生成的代码在 xxx.moc 或 moc_xxx.cpp 中。

moc

看一个例子:

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系统中。
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)

type

type of the property

fget

getter function

fset

None

setter function

freset

None

function used to reset the value of the property to its default value (only in C++)

fdel

None

function for del'ing the property (only in Python)

doc

None

docstring of the property

designable

True

value of Qt DESIGNABLE flag

scriptable

True

value of Qt SCRIPTABLE flag

stored

True

value of Qt STORED flag

user

False

value of Qt USER flag

constant

False

value of Qt CONSTANT flag

final

False

value of Qt FINAL flag

使用举例:

class MyObject(QObject):
  def __init__(self,startval=42):
    self.ppval = startval
  def readPP(self):
    return self.ppval
  def setPP(self,val):
    self.ppval = val
  pp = pyqtProperty(int, readPP, setPP)

obj = MyObject()
obj.pp = 47
print obj.pp
  • PySide用Property来取代pyqtProPerty

  • PySide一开始出现一个失误,用的是QProperty而不是Property

  • PyQt4 为 fget 提供了默认的None,由于Qt要求READ函数必须有,故PySide不为其提供默认值

元对象系统

bool QObject::connect(const QObject *sender, const char *signal,
                      const QObject *receiver, const char *method,
                      Qt::ConnectionType type)
{
    {
        const void *cbdata[] = { sender, signal, receiver, method, &type };
        if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
            return true;
    }
 
    。。。。。。
}

    这个复合语句的作用看不太懂,QInternal类的定义在 src/corelib/global/qnamespace.h 中,类成员的实现在 src/corelib/global/qglobal.cpp 中。

bool QInternal::activateCallbacks(Callback cb, void **parameters)
{
    Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");

    QInternal_CallBackTable *cbt = global_callback_table();
    if (cbt && cb < cbt->callbacks.size()) {
        QList<qInternalCallback> callbacks = cbt->callbacks[cb];
        bool ret = false;
        for (int i=0; i<callbacks.size(); ++i)
            ret |= (callbacks.at(i))(parameters);
        return ret;
    }
    return false;
}

       然后检测4个参数是否都非空

if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(null)",
                 (signal && *signal) ? signal+1 : "(null)",
                 receiver ? receiver->metaObject()->className() : "(null)",
                 (method && *method) ? method+1 : "(null)");
        return false;
    }

     然后

      QByteArray tmp_signal_name;
2 
3     if (!check_signal_macro(sender, signal, "connect", "bind"))
4         return false;
5     const QMetaObject *smeta = sender->metaObject();
  • 检测与信号连接的“信号或槽”是否符合格式,然后从元对象系统中获得相应的索引

检测信号与槽的参数是否匹配,对与Queued连接,检测参数是否注册到元对象系统

++signal; //skip code
    int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
    if (signal_index < 0) {
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
    }
    if (signal_index < 0) {
        // re-use tmp_signal_name and signal from above

        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
    }
    if (signal_index < 0) {
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return false;
    }
    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    int signalOffset, methodOffset;
    computeOffsets(smeta, &signalOffset, &methodOffset);
    int signal_absolute_index = signal_index + methodOffset;
    signal_index += signalOffset;

    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))
        return false;
    const char *method_arg = method;
    ++method; // skip code

    const QMetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    switch (membcode) {
    case QSLOT_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
        break;
    case QSIGNAL_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
        break;
    }

    if (method_index_relative < 0) {
        // check for normalized methods
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();

        // rmeta may have been modified above
        rmeta = receiver->metaObject();
        switch (membcode) {
        case QSLOT_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
            if (method_index_relative < 0)
                method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
            break;
        case QSIGNAL_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
            if (method_index_relative < 0)
                method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
            break;
        }
    }

    if (method_index_relative < 0) {
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return false;
    }

    if (!QMetaObject::checkConnectArgs(signal, method)) {
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return false;
    }

    int *types = 0;
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
        return false;

#ifndef QT_NO_DEBUG
    if (warnCompat) {
        QMetaMethod smethod = smeta->method(signal_absolute_index);
        QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
        check_and_warn_compat(smeta, smethod, rmeta, rmethod);
    }
#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 ,如果是,则先搜索已有的连接,找到则函数返回

bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    QObjectPrivate::StaticMetaCallFunction callFunction =
        (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
        ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

    if (type & Qt::UniqueConnection) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (c2->receiver == receiver && c2->method() == method_index_absolute)
                    return false;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

创建连接

QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
    c->sender = s;
    c->receiver = r;
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->argumentTypes = types;
    c->nextConnectionList = 0;
    c->callFunction = callFunction;

    QT_TRY {
        QObjectPrivate::get(s)->addConnection(signal_index, c);
    } QT_CATCH(...) {
        delete c;
        QT_RETHROW;
    }

    c->prev = &(QObjectPrivate::get(r)->senders);
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;

    QObjectPrivate *const sender_d = QObjectPrivate::get(s);
    if (signal_index < 0) {
        sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
    } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
        sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
    }

    return true;
}

QMetaObject::activate

    信号的定义体在 moc 生成的文件内,在定义体中会调用 QMetaObject::activate 函数来实现信号的功能。首先判断该信号有无被链接,是否是block设置(具体含义?)

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
                           void **argv)
{
    int signalOffset;
    int methodOffset;
    computeOffsets(m, &signalOffset, &methodOffset);

    int signal_index = signalOffset + local_signal_index;

    if (!sender->d_func()->isSignalConnected(signal_index))
        return; // nothing connected to these signals, and no spy

    if (sender->d_func()->blockSig)
        return;

和signal spy有关的一些代码

int signal_absolute_index = methodOffset + local_signal_index;

    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,
                                                         argv ? argv : empty_argv);
    }

    Qt::HANDLE currentThreadId = QThread::currentThreadId();

    QMutexLocker locker(signalSlotLock(sender));
    QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists) {
        locker.unlock();
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
        return;
    }
    ++connectionLists->inUse;

用双重循环依次处理每一个信号的每一个连接

const QObjectPrivate::ConnectionList *list;
    if (signal_index < connectionLists->count())
        list = &connectionLists->at(signal_index);
    else
        list = &connectionLists->allsignals;

    do {
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;

        do {
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;
            const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;

            // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) { //对queued的连接单独处理
                queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                continue;
#ifndef QT_NO_THREAD
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {
                locker.unlock();
                if (receiverInSameThread) {
                    qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
                    "Sender is %s(%p), receiver is %s(%p)",
                    sender->metaObject()->className(), sender,
                    receiver->metaObject()->className(), receiver);
                }
                QSemaphore semaphore;
                QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
                                                                         c->callFunction,
                                                                         sender, signal_absolute_index,
                                                                         0, 0,
                                                                         argv ? argv : empty_argv,
                                                                         &semaphore));
                semaphore.acquire();
                locker.relock();
                continue;
#endif
            }
            //对其他连接,继续(发送与接收者在同一线程,则设置当前发送者,QObject::sender() 需要该功能)
            QObjectPrivate::Sender currentSender;
            QObjectPrivate::Sender *previousSender = 0;
            if (receiverInSameThread) {
                currentSender.sender = sender;
                currentSender.signal = signal_absolute_index;
                currentSender.ref = 1;
                previousSender = QObjectPrivate::setCurrentSender(receiver, &currentSender);
            }
            const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
            const int method_relative = c->method_relative;
            if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                //we compare the vtable to make sure we are not in the destructor of the object.
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);

#if defined(QT_NO_EXCEPTIONS)
                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
#else
                QT_TRY {
                    callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
                } QT_CATCH(...) {
                    locker.relock();
                    if (receiverInSameThread)
                        QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);

                    --connectionLists->inUse;
                    Q_ASSERT(connectionLists->inUse >= 0);
                    if (connectionLists->orphaned && !connectionLists->inUse)
                        delete connectionLists;
                    QT_RETHROW;
                }
#endif
                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());
                locker.relock();
            } else {
                const int method = method_relative + c->method_offset;
                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                    qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                                method,
                                                                argv ? argv : empty_argv);
                }
                //调用metacall
#if defined(QT_NO_EXCEPTIONS)
                metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
                QT_TRY {
                    metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
                } QT_CATCH(...) {
                    locker.relock();
                    if (receiverInSameThread)
                        QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);

                    --connectionLists->inUse;
                    Q_ASSERT(connectionLists->inUse >= 0);
                    if (connectionLists->orphaned && !connectionLists->inUse)
                        delete connectionLists;
                    QT_RETHROW;
                }
#endif

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();
            }

            if (receiverInSameThread)
                QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));

    --connectionLists->inUse;
    Q_ASSERT(connectionLists->inUse >= 0);
    if (connectionLists->orphaned) {
        if (!connectionLists->inUse)
            delete connectionLists;
    } else if (connectionLists->dirty) {
        sender->d_func()->cleanConnectionLists();
    }

    locker.unlock();

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);

}

接下来看metacall代码

int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{
    if (QMetaObject *mo = object->d_ptr->metaObject)
        return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
    else
        return object->qt_metacall(cl, idx, argv);
}

前面的 QAbstractDynamicMetaObject 部分不清楚作用,后面的 qt_metacall 比较好理解了(代码在moc生成的文件中)

queued_activate

首先是参数检测,然后是构建 QMetaCallEvent 事件,并post该事件

static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
    if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
        QMetaMethod m = sender->metaObject()->method(signal);
        int *tmp = queuedConnectionTypes(m.parameterTypes());
        if (!tmp) // cannot queue arguments
            tmp = &DIRECT_CONNECTION_ONLY;
        if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
            if (tmp != &DIRECT_CONNECTION_ONLY)
                delete [] tmp;
        }
    }
    if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
        return;
    int nargs = 1; // include return type
    while (c->argumentTypes[nargs-1])
        ++nargs;
    int *types = (int *) qMalloc(nargs*sizeof(int));
    Q_CHECK_PTR(types);
    void **args = (void **) qMalloc(nargs*sizeof(void *));
    Q_CHECK_PTR(args);
    types[0] = 0; // return type
    args[0] = 0; // return value
    for (int n = 1; n < nargs; ++n)
        args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
    QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
                                                                c->method_relative,
                                                                c->callFunction,
                                                                sender, signal, nargs,
                                                                types, args));
}

blocking_activate

首先,检测是否是同一线程,然后发送事件 QMetaCallEvent

既然和事件有关系,就看看事件是怎么回事

case QEvent::MetaCall:
        {
#ifdef QT_JAMBI_BUILD
            d_func()->inEventHandler = false;
#endif
            QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
            QObjectPrivate::Sender currentSender;
            currentSender.sender = const_cast<QObject*>(mce->sender());
            currentSender.signal = mce->signalId();
            currentSender.ref = 1;
            QObjectPrivate::Sender * const previousSender =
                QObjectPrivate::setCurrentSender(this, &currentSender);
#if defined(QT_NO_EXCEPTIONS)
            mce->placeMetaCall(this);
#else
            QT_TRY {
                mce->placeMetaCall(this);
            } QT_CATCH(...) {
                QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
                QT_RETHROW;
            }
#endif
            QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
            break;
        }

可以看到,这儿调用了 placeMetaCall

void QMetaCallEvent::placeMetaCall(QObject *object)
{
    if (callFunction_) {
        callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
    } else {
        QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
    }
}

基本理解

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

这两个东西真难理清,不妨看看源码吧。

 Q_DECLARE_METATYPE

代码来源:src/corelib/kernel/qmetatype.h

#define Q_DECLARE_METATYPE(TYPE)                                        \
    QT_BEGIN_NAMESPACE                                                  \
    template <>                                                         \
    struct QMetaTypeId< TYPE >                                          \
    {                                                                   \
        enum { Defined = 1 };                                           \
        static int qt_metatype_id()                                     \
            {                                                           \
                static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
                if (!metatype_id)                                       \
                    metatype_id = qRegisterMetaType< TYPE >(#TYPE,      \
                               reinterpret_cast< TYPE *>(quintptr(-1))); \
                return metatype_id;                                     \
            }                                                           \
    };                                                                  \
    QT_END_NAMESPACE
  • 宏展开是一个在Qt的命名空间中的一个类模板的特化 QMetaTypeId<TYPE>

  • 该类含一个enum和一个返回!QMetaType的id的成员函数

qRegisterMetaType(const char *typeName)

代码来源:src/corelib/kernel/qmetatype.h

template <typename T>
int qRegisterMetaType(const char *typeName
#ifndef qdoc
    , T * dummy = 0
#endif
)
{
    const int typedefOf = dummy ? -1 : QtPrivate::QMetaTypeIdHelper<T>::qt_metatype_id();
    if (typedefOf != -1)
        return QMetaType::registerTypedef(typeName, typedefOf);

    typedef void*(*ConstructPtr)(const T*);
    ConstructPtr cptr = qMetaTypeConstructHelper<T>;
    typedef void(*DeletePtr)(T*);
    DeletePtr dptr = qMetaTypeDeleteHelper<T>;

    return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr),
                                   reinterpret_cast<QMetaType::Constructor>(cptr));
}
  • 该函数的核心就是调用了registerType 函数
  • 两个Helper模板函数分别对构造和析构函数进行封装

registerType

代码来源:src/corelib/kernel/qmetatype.cpp

int QMetaType::registerType(const char *typeName, Destructor destructor,
                            Constructor constructor)
{
    QVector<QCustomTypeInfo> *ct = customTypes();
    if (!ct || !typeName || !destructor || !constructor)
        return -1;

#ifdef QT_NO_QOBJECT
    NS(QByteArray) normalizedTypeName = typeName;
#else
    NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName);
#endif

    int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
                                  normalizedTypeName.size());

    if (!idx) {
        QWriteLocker locker(customTypesLock());
        idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
                                           normalizedTypeName.size());
        if (!idx) {
            QCustomTypeInfo inf;
            inf.typeName = normalizedTypeName;
            inf.constr = constructor;
            inf.destr = destructor;
            inf.alias = -1;
            idx = ct->size() + User;
            ct->append(inf);
        }
    }
    return idx;
}

函数功能:

  • 根据类型名查找其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中相应的项么?源码告诉我们,不是的。

    实际是查找到相应的项,清空该项的内容

void QMetaType::unregisterType(const char *typeName)
{
    QVector<QCustomTypeInfo> *ct = customTypes();
    if (!ct || !typeName)
        return;

#ifdef QT_NO_QOBJECT
    NS(QByteArray) normalizedTypeName = typeName;
#else
    NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName);
#endif
    QWriteLocker locker(customTypesLock());
    for (int v = 0; v < ct->count(); ++v) {
        if (ct->at(v).typeName == typeName) {
            QCustomTypeInfo &inf = (*ct)[v];
            inf.typeName.clear();
            inf.constr = 0;
            inf.destr = 0;
            inf.alias = -1;
        }
    }
}

Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC

这两个宏在Qt的源码中随处可见,重要性不言而喻。在 部落格的 Inside Qt Series 系列文章中,他用了3篇文章来讲这个问题。

因为 QObject 本身比较复杂,这两个宏和一个复杂的东西搅和到一块,还真是不好理解。不过幸好,这个两个宏和QObject 没有必然的联系。故接下来,忘记 QObject,看一个普通的C++的类

例子

类 QtServiceController 定义:

class QtServiceController
{
 Q_DECLARE_PRIVATE(QtServiceController)
public:
 QtServiceController(const QString &name);
 //省略其他
private:
 QtServiceControllerPrivate *d_ptr;
};

类 QtServiceControllerPrivate 定义:

class QtServiceControllerPrivate
{
    Q_DECLARE_PUBLIC(QtServiceController)
public:
    QString serviceName;
    QtServiceController *q_ptr;
};

将所有的private数据成员,独立出来放于一个独立的私有的数据对象中。这一点是比较好理解的,那么这两个宏在这起什么作用呢?

注意:上面定义的两个指针 d_ptr, q_ptr。

宏定义

宏定义在 QtGlobal(即qglobal.h)头文件中:

#define Q_DECLARE_PRIVATE(Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
    friend class Class##Private;

#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } \
    friend class Class##Private;

#define Q_DECLARE_PUBLIC(Class)                                    \
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
    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的原因。

QMetaObject::invokeMethod

  • 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:

测试代码

#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
class Test : public QObject
{
Q_OBJECT
public:
Test(QObject * parent):QObject(parent)
{
connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString)));
QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor"));
}
Q_INVOKABLE void method1(const QString& t)
{
qDebug()<<"from method:"<<t;
}

signals:
void sig1(const QString& t);

public slots:
void slot1(const QString& t)
{
qDebug()<<"from slot:"<<t;
}
};
 #include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test obj(0);
QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello"));
QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello"));
    QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT"));
QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD"));
return a.exec();
}

确实如他人所说,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)

源码:

bool QMetaObject::invokeMethod(QObject *obj,
                               const char *member,
                               Qt::ConnectionType type,
                               QGenericReturnArgument ret,
                               QGenericArgument val0,
                               QGenericArgument val1,
                               QGenericArgument val2,
                               QGenericArgument val3,
                               QGenericArgument val4,
                               QGenericArgument val5,
                               QGenericArgument val6,
                               QGenericArgument val7,
                               QGenericArgument val8,
                               QGenericArgument val9)
{
    if (!obj)
        return false;

    QVarLengthArray<char, 512> sig;
    int len = qstrlen(member);
    if (len <= 0)
        return false;
       //生成函数原型字符串(从这儿可以看到书中方法不工作的原因)
    sig.append(member, len);
    sig.append('(');

    const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(),
                               val4.name(), val5.name(), val6.name(), val7.name(), val8.name(),
                               val9.name()};

    int paramCount;
    for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
        len = qstrlen(typeNames[paramCount]);
        if (len <= 0)
            break;
        sig.append(typeNames[paramCount], len);
        sig.append(',');
    }
    if (paramCount == 1)
        sig.append(')'); // no parameters
    else
        sig[sig.size() - 1] = ')';
    sig.append('\0');
    //在元对象系统中看该函数信息是否存在
    int idx = obj->metaObject()->indexOfMethod(sig.constData());
    if (idx < 0) {
        QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
        idx = obj->metaObject()->indexOfMethod(norm.constData());
    }

    if (idx < 0 || idx >= obj->metaObject()->methodCount()) {
        qWarning("QMetaObject::invokeMethod: No such method %s::%s",
                 obj->metaObject()->className(), sig.constData());
        return false;
    }
       //获得相应的 QMetaMethod,调用其 invoke 方法
    QMetaMethod method = obj->metaObject()->method(idx);
    return method.invoke(obj, type, ret,
                         val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}

接着看看它的源码,首先:

* 如果指定了返回值,检查返回值的类型是否和QMetaMethod 的中的一致

bool QMetaMethod::invoke(QObject *object,
                         Qt::ConnectionType connectionType,
                         QGenericReturnArgument returnValue,
                         QGenericArgument val0,
                         QGenericArgument val1,
                         QGenericArgument val2,
                         QGenericArgument val3,
                         QGenericArgument val4,
                         QGenericArgument val5,
                         QGenericArgument val6,
                         QGenericArgument val7,
                         QGenericArgument val8,
                         QGenericArgument val9) const
{
    if (!object || !mobj)
        return false;

    Q_ASSERT(mobj->cast(object));

    // check return type
    if (returnValue.data()) {
        const char *retType = typeName();
        if (qstrcmp(returnValue.name(), retType) != 0) {
            // normalize the return value as well
            // the trick here is to make a function signature out of the return type
            // so that we can call normalizedSignature() and avoid duplicating code
            QByteArray unnormalized;
            int len = qstrlen(returnValue.name());

            unnormalized.reserve(len + 3);
            unnormalized = "_(";        // the function is called "_"
            unnormalized.append(returnValue.name());
            unnormalized.append(')');

            QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());
            normalized.truncate(normalized.length() - 1); // drop the ending ')'

            if (qstrcmp(normalized.constData() + 2, retType) != 0)
                return false;
        }
    }

    // check argument count (we don't allow invoking a method if given too few arguments)
    const char *typeNames[] = {
        returnValue.name(),
        val0.name(),
        val1.name(),
        val2.name(),
        val3.name(),
        val4.name(),
        val5.name(),
        val6.name(),
        val7.name(),
        val8.name(),
        val9.name()
    };
    int paramCount;
    for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
        if (qstrlen(typeNames[paramCount]) <= 0)
            break;
    }
    int metaMethodArgumentCount = 0;
    {
        // based on QMetaObject::parameterNames()
        const char *names = mobj->d.stringdata + mobj->d.data[handle + 1];
        if (*names == 0) {
            // do we have one or zero arguments?
            const char *signature = mobj->d.stringdata + mobj->d.data[handle];
            while (*signature && *signature != '(')
                ++signature;
            if (*++signature != ')')
                ++metaMethodArgumentCount;
        } else {
            --names;
            do {
                ++names;
                while (*names && *names != ',')
                    ++names;
                ++metaMethodArgumentCount;
            } while (*names);
        }
    }
    if (paramCount <= metaMethodArgumentCount)
        return false;

        //为了利用现有的代码来规范化这儿返回值的类型,这儿构造了一个函数_(typeOfReturn)。
          //检查参数个数,传递的参数是否不少于需要的参数
          //检查Connection的类型,处理AutoConnection
    // check connection type
    QThread *currentThread = QThread::currentThread();
    QThread *objectThread = object->thread();
    if (connectionType == Qt::AutoConnection) {
        connectionType = currentThread == objectThread
                         ? Qt::DirectConnection
                         : Qt::QueuedConnection;
    }

#ifdef QT_NO_THREAD
    if (connectionType == Qt::BlockingQueuedConnection) {
        connectionType = Qt::DirectConnection;
    }
#endif

    // invoke!
    void *param[] = {
        returnValue.data(),
        val0.data(),
        val1.data(),
        val2.data(),
        val3.data(),
        val4.data(),
        val5.data(),
        val6.data(),
        val7.data(),
        val8.data(),
        val9.data()
    };
    // recompute the methodIndex by reversing the arithmetic in QMetaObject::property()
    int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5);
    int idx_offset =  mobj->methodOffset();
    QObjectPrivate::StaticMetaCallFunction callFunction =
        (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata)
        ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0;
    //对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall
    if (connectionType == Qt::DirectConnection) {
        if (callFunction) {
            callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
            return true;
        } else {
            return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0;
        }
    } else if (connectionType == Qt::QueuedConnection) { //对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中
        if (returnValue.data()) {
            qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
                     "queued connections");
            return false;
        }

        int nargs = 1; // include return type
        void **args = (void **) qMalloc(paramCount * sizeof(void *));
        Q_CHECK_PTR(args);
        int *types = (int *) qMalloc(paramCount * sizeof(int));
        Q_CHECK_PTR(types);
        types[0] = 0; // return type
        args[0] = 0;

        for (int i = 1; i < paramCount; ++i) {
            types[i] = QMetaType::type(typeNames[i]);
            if (types[i]) {
                args[i] = QMetaType::construct(types[i], param[i]);
                ++nargs;
            } else if (param[i]) {
                qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
                         typeNames[i]);
                for (int x = 1; x < i; ++x) {
                    if (types[x] && args[x])
                        QMetaType::destroy(types[x], args[x]);
                }
                qFree(types);
                qFree(args);
                return false;
            }
        }

        QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
                                                        0, -1, nargs, types, args));
    } else { // blocking queued connection
#ifndef QT_NO_THREAD
        if (currentThread == objectThread) {
            qWarning("QMetaMethod::invoke: Dead lock detected in "
                        "BlockingQueuedConnection: Receiver is %s(%p)",
                        mobj->className(), object);
        }
        //对于 bolckedqueued 的连接,使用了信号量
        QSemaphore semaphore;
        QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
                                                        0, -1, 0, 0, param, &semaphore));
        semaphore.acquire();
#endif // QT_NO_THREAD
    }
    return true;
}

猜你喜欢

转载自blog.csdn.net/lengyuezuixue/article/details/81154429