Qt 之 qobject_cast 分析及QTBUG 20616
2013-01-18 11:10
295 查看
适用范围
qobject_cast 用于两种情况:QObject 及其派生类,且定义时使用了Q_OBJECT
Q_DECLARE_INTERFACE 声明的接口类(不需要是QObject的派生类)
问题:如果既是QObject派生类,又用Q_DECLARE_INTERFACE声明为接口会怎么样?QTBUG 20616报告的就是这样一个问题。
QObject的派生类
首先看看qobject_cast的manual:T qobject_cast ( QObject * object ) Returns the given object cast to type T if the object is of type T (or of a subclass); otherwise returns 0. If object is 0 then it will also return 0. The class T must inherit (directly or indirectly) QObject and be declared with the Q_OBJECT macro. ...
恩,要求很明确,必须继承自QObject,且包含有Q_OBJECT宏 (即需要moc生成metaobject信息)。
源码
看看源码(来自qobject.h,注意,随Qt的版本不同,你看到的源码可能会有很大的差异):template <class T> inline T qobject_cast(QObject *object) { return static_cast<T>(reinterpret_cast<T>(object)->staticMetaObject.cast(object)); }
注:
还有一个对应的template <class T> inline T qobject_cast(const QObject *),由于代码基本完全一样,本文中直接忽略。
先前(比如Qt4.6.3)曾用过 return static_cast<T>(((T)0)->staticMetaObject.cast(object)); 这种写法。
不管怎么样,转型的操作是通过 static_cast 来实现的,而 QMetaObject::cast 只是不过借助 metaobject 信息来确定对象object是否继承自 T
/*! \internal Returns \a obj if object \a obj inherits from this meta-object; otherwise returns 0. */ QObject *QMetaObject::cast(QObject *obj) const { if (obj) { const QMetaObject *m = obj->metaObject(); do { if (m == this) return obj; } while ((m = m->d.superdata)); } return 0; }
inherits
如何我们看moc生成的元对象文件,比如,对于一个继承子QWidget的类HLed:moc_hled.cppvoid *HLed::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_HLed)) return static_cast<void*>(const_cast< HLed*>(this)); return QWidget::qt_metacast(_clname); }
这个东西看起来和转型动作关系很大啊,可是前面的qobject_cast却没有用到它,那么它用在哪儿呢?
似乎只用在inherits中(fixme ?)
class QObject { ... inline bool inherits(const char *classname) const { return const_cast<QObject *>(this)->qt_metacast(classname) != 0; } ...
注意1:qt_metacast 中比较的是类名和qt_meta_stringdata_HLed这样字符串,后者'\0'前面部分其实就是类名。
static const char qt_meta_stringdata_HLed[] = { "HLed\0\0toggle()\0on()\0off()\0blink()\0" "blinkToggle()\0" };
注意2:后面我们会再次遇到qt_metacast,注意与此处的区别。
Q_DECLARE_INTERFACE声明的接口
还是看qobject_cast的manualT qobject_cast ( QObject * object ) qobject_cast() can also be used in conjunction with interfaces;
接口往往不是QObject的派生类,那么它又是如何工作的呢?比如:
Q_DECLARE_INTERFACE(MathInterface, "com.example.Plugin.MathInterface/0.1");
源码
#ifndef Q_MOC_RUN # define Q_DECLARE_INTERFACE(IFace, IId) \ template <> inline const char *qobject_interface_iid<IFace *>() \ { return IId; } \ template <> inline IFace *qobject_cast<IFace *>(QObject *object) \ { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : 0)); } \ template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \ { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : 0)); } #endif // Q_MOC_RUN
对于接口类,此处特化了模板函数。使得 qobject_cast 对于接口类型可用。
特化的函数调用了 qt_metacast 函数。但是,此处传递给qt_metacast函数的参数,不是类名,而是声明接口是用的IId
注意,该宏被Q_MOC_RUN包围,moc预处理时,该宏不会简单地被展开(也不会单独为其生成什么代码)
Q_INTERFACES
像我们在插件学习二中所涉及到的,插件接口定义好以后,需要使用该接口的类需要使用 Q_INTERFACES 指定该接口。不同于 Q_DECLARE_INTERFACE,Q_INTERFACES 是一个纯粹的需要moc进行处理的宏,moc解析其指定的接口,然后判断该接口是否已经使用 Q_DECLARE_INTERFACE 进行过声明。
最后moc生成包含元对象信息的文件,其中包括类似下面的内容:
void *Plugin1::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_Plugin1)) return static_cast<void*>(const_cast< Plugin1*>(this)); if (!strcmp(_clname, "MathInterface")) return static_cast< MathInterface*>(const_cast< Plugin1*>(this)); if (!strcmp(_clname, "com.example.Plugin.MathInterface/0.1")) return static_cast< MathInterface*>(const_cast< Plugin1*>(this)); return QObject::qt_metacast(_clname); }
这样一来,qobject_cast 对与接口就可以正常工作了,而且不影响原来的QObject类型的转换动作包括inherits函数
QTBUG 20616
前几天刚好查些东西,并写了QObject派生类作为Qt 插件的Interface,看来这样用没什么问题啊,为什么会有bug!!??刚看到这个bug时有点蒙,不过现在理解它应该没什么问题了吧? bug提交者给的例子很简单:
interfaceclass.h
#include <QObject> class InterfaceClass : public QObject { Q_OBJECT public: InterfaceClass() {} }; Q_DECLARE_INTERFACE(InterfaceClass, "com.nokia.qt.broken.interfaceclass/-1");
main.cpp
#include "interfaceclass.h" #include <QApplication> #include <QDebug> int main(int argc, char **argv) { QApplication app(argc, argv); InterfaceClass interfaceClass; qDebug() << "This pointer should not be \"QObject(0x0)\":"<<qobject_cast<InterfaceClass*>(&interfaceClass); return 0; }
原因
将一个QObject派生类声明为接口(通过Q_DECLARE_INTERFACE),但是它并没有将其作为接口来使用(如果我们将接口都作为virtual类来使用,也就没这种问题了),而是作为一个普通的类来使用。前面可知,Q_DECLARE_INTERFACE 特化了 qobject_cast,该特化后的 qobject_cast 需要 qt_metacast 的参与,而 qt_metacast 中接口相关的信息是通过 Q_INTERFACES 引入的。
这样一来,
默认的 qobject_cast 本可以工作,但被屏蔽了
特化的 qobject_cast 由于缺少信息,无法工作
如何解决
不知道官方最终会如何解决,个人觉得既然特化造成的问题,让特化后的函数保留原来的功能就可以了。如果moc看到一个接口继承自QObject且含有Q_OBJECT,生成如下代码:
void *InterfaceClass::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_InterfaceClass)) return static_cast<void*>(const_cast< InterfaceClass*>(this)); if (!strcmp(_clname, "com.nokia.qt.broken.interfaceclass/-1")) return static_cast<InterfaceClass*>(const_cast<InterfaceClass*>(this)); return QObject::qt_metacast(_clname); }
而不是
void *InterfaceClass::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_InterfaceClass)) return static_cast<void*>(const_cast< InterfaceClass*>(this)); (const_cast<InterfaceClass*>(this)); return QObject::qt_metacast(_clname); }
那么问题将可以解决。可是问题在于:
moc 如何知道该类被声明为了接口,Q_DECLARE_INTERFACE 可能和类定义并不再同一个文件内。需要为它自己引入一个 Q_INTERFACES 么?
还是说我们直接修改特化的 qobject_cast 函数,当转换失败时,用默认的方法再转换一次?
template <> inline IFace *qobject_cast<IFace *>(QObject *object) \ {IFace * face = reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : 0)); \ return face ? face : static_cast<IFace*>(reinterpret_cast<IFace*>(object)->staticMetaObject.cast(object));}
参考
https://bugreports.qt.nokia.com/browse/QTBUG-20616 /content/839485.html
相关文章推荐
- Qt 之 qobject_cast 分析及QTBUG 20616
- QT源码分析: qobject_cast
- Qt: 内建对话框(各种对话框都有了,且用到了qobject_cast解析sender的技术)
- Qt程序调试“QWidget : Must construct a Qapplication before a Qwidget"BUG分析
- Qt源码分析之QObject
- Qt之qobject_cast的描述
- Qt源码分析之QObject
- QT编译出错in Q_INTERFACES. qobject_cast to QGraphicsItem will not work!
- Qt源码分析之QObject
- The Annotated Qt之QObject分析(1)
- [QT][源码分析]QObject::connect
- Qt-qobject_cast用法
- 详解 Qt 源码分析 QObject(1)
- Qt源码分析之QObject
- Qt: qobject_cast<QPushButton*>(sender()) 简化信号与槽的编写
- Qt分析:Qt中的两种定时器(可是QObject为什么要提高定时器呢,没必要啊。。。)
- [QT][源码分析]QObject::connect
- Qt源码分析之QObject (转)
- Qt源码分析之QObject
- Qt源码分析之QObject