qt源码学习---QMetaObject(三)

本篇主要解释下QMetaMethod中剩余的一些成员函数,如invoke函数、invokeOnGadget函数等

1、首先看下invoke函数的源码:

qt源码学习---QMetaObject(三)_第1张图片

 qt源码学习---QMetaObject(三)_第2张图片

 此处主要是判断QMetaMethod中的QMetaObject对象和传入的QObject对象是否为空及检查传入的返回值类型是否为空或者是否与QMetaMethod的返回值类型是否匹配;QMetaObject::normalizedType的作用主要是去除前后多余的空格及去除const关键字;

qt源码学习---QMetaObject(三)_第3张图片

 此处判断函数对应的参数类型及数量是否匹配;

qt源码学习---QMetaObject(三)_第4张图片

 如果传入的connectionType值是自动类型,将根据当前线程和object的线程是否是同一个线程判断是使用直接连接还是队列连接的形式;

qt源码学习---QMetaObject(三)_第5张图片

 首先解释下idx_relative,其方法在其自身中的偏移量;idx_offset指的是QMetaObject相对于其父类的偏移量,之前已详细解释器关系;

如果sattic_metacall不为空,则直接调用static_metacall指向的函数处理;

如果static_metacall为空,则使用QMetaObject的静态方法metacall函数处理;

那么static_metacall是什么时候赋初值的呢?

熟悉qt的都知道,凡是使用Q_OBJECT宏声明的对象,其会生成对应的moc文件,还以ReflectDemo为例,其对应的moc文件内容为:

qt源码学习---QMetaObject(三)_第6张图片

 可以看到在对应的moc文件中,会对static_metacall进行赋值,也就是会执行qt_static_metacall函数;(qt如何生成moc文件呢?在qt的源码中有一个moc工程文件,在执行编译过程中,会先执行moc程序,moc程序扫描源程序,逐个输出其对应的moc文件,具体位置:src-->qtbase-->src-->tools-->moc);

静态函数metacall的实现如下:

qt源码学习---QMetaObject(三)_第7张图片

 由于object->d_ptr->metaObject此对象为空,所以会执行qt_metacall函数;其在moc文件中实现类似于:

qt源码学习---QMetaObject(三)_第8张图片

_id = QObject::qt_metacall()函数的实现方式由于需要编译源码才能看到其内容,所以只能通过其生成moc文件的源码来推测(编译源码时间太长):

qt源码学习---QMetaObject(三)_第9张图片

 通过以上源码可知,如果是QObject对象,则不会有_id = QObject::qt_metacall这条语句;

QObject中的invokeMethod数量为5(可以通过qobject.h文件查看其声明的信号、槽、Q_INVOKABLE方法可知),那么_id = QObject::qt_metacall这条语句,其实就是_id = _id - methodOffset()的作用(如果调用的是自己的方法而不是父类的方法),即此方法在本类中的偏移量;如果_id < 0,说明调用的是父类的方法;

接下来继续看invoke中剩余的代码:

qt源码学习---QMetaObject(三)_第10张图片

 首先检测,传入的参数及返回值类型是否是基本类型或者是注册到元对象系统中的类型,如果不是则返回错误;最后通过postEvent,抛出QMetaCallEvent事件;其在对应的QObject中有接收MetaCall的事件,具体在QObject中的event函数中有其调用:

qt源码学习---QMetaObject(三)_第11张图片

 最后为队列阻塞BlockingQueuedConnection的方式调用:

qt源码学习---QMetaObject(三)_第12张图片

 其主要也是使用postEvent方式抛出MetaCall事件;但是有一个需要注意的地方,同一个线程使用应用程序由于semaphore的使用会被阻塞,只能在多线程中使用;

2、invokeOnGadget函数说明:

qt源码学习---QMetaObject(三)_第13张图片 qt源码学习---QMetaObject(三)_第14张图片

函数前半部分主要是返回类型及参数的判断和invoke函数类似;其主要是通过static_metacall方法进行调用,其是同步执行的;

3、fromSignal函数:

 qt源码学习---QMetaObject(三)_第15张图片

 调用fromSignal函数时会判断此类是否声明Q_OBJECT宏,其主要是通过模板在编译时进行判断;

qt源码学习---QMetaObject(三)_第16张图片

 可以看到HasQ_OBJECT_Macro结构体声明了2个模板方法,一个是返回char类型的模板方法,一个是返回int型的特例化的模板方法;假设ReflectDemo继承自QOBject但是没有声明Q_OBJECT宏,那么当编译sizeof(test(&Object::qt_metacall)) == sizeof(int)语句时,由于ReflectDemo中没有定义qt_metacall方法,所以会找到父类QObject中的qt_metacall方法;因此static test(int (T::*)(QMetaObject::Call, int, void **))语句会被编译,其返回值为char型,因此sizeof(char) != sizeof(int),所以此时Value为false,编译时会输出错误信息;

QtPrivate::FunctionPointer的声明如下:

qt源码学习---QMetaObject(三)_第17张图片

fromSignal的使用一般为:QMetaMethod::fromSignal(&ReflectDemo::test);

FunctionPointer为模板,其会自动推算出Obj、Ret、Args的类型,此处Obj表示ReflectDemo类型;

4、fromSignalImp函数实现如下:

qt源码学习---QMetaObject(三)_第18张图片

 岂会调用QMetaObject的static_metacall函数,static_metacall函数在moc文件中定义,

qt源码学习---QMetaObject(三)_第19张图片

 在static_metacall函数中是通过函数地址进行比较,如果地址相同,则更改result的值即fromSignalImpl中i的值;所以当i>=0时,即说明找到对应的metaMethod,并将其返回;

注:1、此图和上图不同是由于ReflectDemo中增加2个信号,主要为了说明此方法;

        2、信号和槽在QMetaObject的data指针中存储位置比Q_INVOKABLE标记的方法要靠前,data中先存储信号对应的偏移量,槽、Q_INVOKABLE标记的方法;

        3、在QMetaObject中信号即方法,在moc文件中实现;如下:

qt源码学习---QMetaObject(三)_第20张图片

 ReflectDemo完整定义如下:

qt源码学习---QMetaObject(三)_第21张图片

 

 

你可能感兴趣的:(qt,学习)