在 QML 中使用 C++-slot,它返回命名空间中的类型
Posted
技术标签:
【中文标题】在 QML 中使用 C++-slot,它返回命名空间中的类型【英文标题】:Use C++-slot in QML which returns type in namespace 【发布时间】:2012-12-29 21:58:06 【问题描述】:我首先要提到的是,在 Qt 5.0.0 beta 1 之前,以下内容运行良好(可能还有 beta 2 和 RC,不知道),但在 Qt 5.0.0 最终版本中失败了。我只想参考在 Qt 5.0.0 最终发布版本中看到的结果。所以这很可能与 Qt5 最近的变化有关。
在 C++ 方面,我在命名空间中有一组类(QObject 派生)(可以使用编译器标志触发;这些类位于单独的库中,并且该库将命名空间的使用作为选项留给图书馆的用户)。一个类,这里是Game
,可能看起来像这样(摘录):
OAE_BEGIN_NAMESPACE
// forward-declarations:
class Player; // Player is just another class in the same library
class Game : public QObject
Q_OBJECT
public:
explicit Game(...);
public slots:
Player *player() const; // <-- the quesion is about such slots
;
OAE_END_NAMESPACE
宏 OAE_BEGIN/END_NAMESPACE
扩展为 namespace OAE_NAMESPACE
... 或什么都没有,就像 Qt 在
<qglobal.h>
中所做的一样,只是在宏名称中将“QT”替换为“OAE”:
#ifndef OAE_NAMESPACE
# define OAE_PREPEND_NAMESPACE(name) ::name
# define OAE_USE_NAMESPACE
# define OAE_BEGIN_NAMESPACE
# define OAE_END_NAMESPACE
# define OAE_BEGIN_INCLUDE_NAMESPACE
# define OAE_END_INCLUDE_NAMESPACE
# define OAE_BEGIN_MOC_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) class name;
# define OAE_FORWARD_DECLARE_STRUCT(name) struct name;
# define OAE_MANGLE_NAMESPACE(name) name
#else /* user namespace */
# define OAE_PREPEND_NAMESPACE(name) ::OAE_NAMESPACE::name
# define OAE_USE_NAMESPACE using namespace ::OAE_NAMESPACE;
# define OAE_BEGIN_NAMESPACE namespace OAE_NAMESPACE
# define OAE_END_NAMESPACE
# define OAE_BEGIN_INCLUDE_NAMESPACE
# define OAE_END_INCLUDE_NAMESPACE namespace OAE_NAMESPACE
# define OAE_BEGIN_MOC_NAMESPACE OAE_USE_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) \
OAE_BEGIN_NAMESPACE class name; OAE_END_NAMESPACE \
using OAE_PREPEND_NAMESPACE(name);
# define OAE_FORWARD_DECLARE_STRUCT(name) \
OAE_BEGIN_NAMESPACE struct name; OAE_END_NAMESPACE \
using OAE_PREPEND_NAMESPACE(name);
# define OAE_MANGLE_NAMESPACE0(x) x
# define OAE_MANGLE_NAMESPACE1(a, b) a##_##b
# define OAE_MANGLE_NAMESPACE2(a, b) OAE_MANGLE_NAMESPACE1(a,b)
# define OAE_MANGLE_NAMESPACE(name) OAE_MANGLE_NAMESPACE2( \
OAE_MANGLE_NAMESPACE0(name), OAE_MANGLE_NAMESPACE0(OAE_NAMESPACE))
namespace OAE_NAMESPACE
# ifndef OAE_BOOTSTRAPPED
# ifndef OAE_NO_USING_NAMESPACE
/*
This expands to a "using OAE_NAMESPACE" also in _header files_.
It is the only way the feature can be used without too much
pain, but if people _really_ do not want it they can add
DEFINES += OAE_NO_USING_NAMESPACE to their .pro files.
*/
OAE_USE_NAMESPACE
# endif
# endif
#endif /* user namespace */
在下文中,当说“启用命名空间”时,我的意思是我声明了宏 OAE_NAMESPACE
,在本例中为 oae
。
除其他外,我从 QML 中为我的应用程序的用户界面访问此类和 Player
类的实例,这些实例由 player()
返回。为此,我按如下方式注册类:
qmlRegisterType<Game>();
qmlRegisterType<Player>();
我为 QML 前端提供了一个指向 Game
实例的指针,在 QML 中称为 theGame
:
view.engine()->rootContext()->setContextProperty("theGame",
QVariant::fromValue<Game*>(game));
在 QML 中,我照常使用它。一个小例子应该打印player()
的指针地址:
Rectangle
width: 100; height: 100
Component.onCompleted: console.log(theGame.player())
我得到以下结果,具体取决于我是否设置了OAE_NAMESPACE
(顺便说一句:我对库和使用它的应用程序使用相同的设置):
禁用命名空间时,一切都按预期工作,并且 QML 打印出指针:
Player(0x10b4ae0)
当启用命名空间(以及using
在 C++ 代码中使用
库,所以我根本不更改代码),QML 无法
了解Game::player()
的返回类型:
Error: Unknown method return type: Player*
当将Game::player()
的返回类型更改为
oae::Player*
,一切正常:
oae::Player(0x10b4ae0)
到目前为止,我的结论是moc
没有考虑我在类周围放置的命名空间。我的第一个猜测是:嘿,moc
不知道我在调用g++
时定义了命名空间,这是我在 .pro 文件中所做的:
DEFINES += OAE_NAMESPACE=oae
但是,当将返回类型更改为 OAE_NAMESPACE::Player*
时,它仍然有效,因此 moc 确实知道 OAE_NAMESPACE
宏,但它既不扩展 OAE_BEGIN/END_NAMESPACE
宏,也不它根本不再解析命名空间。
moc
为 Player * Game::player() const
生成以下“字符串数据”,其中包含方法的返回类型:
当禁用命名空间并使用返回类型Player*
时:
"player\0Player*\0"
当启用命名空间并使用返回类型Player*
时:
"player\0Player*\0"
当启用命名空间并使用返回类型OAE_NAMESPACE::Player*
:
"player\0oae::Player*\0"
另一方面,moc
会在 QMetaObject::className()
返回的类名前面加上命名空间(如果已启用)。
我现在的结论是,当在 QObject 元方法的签名中使用这些类型时,我可以通过编写 OAE_NAMESPACE::ClassName
而不是 ClassName
来解决此问题。 (嗯,有更好的宏OAE_PREPEND_NAMESPACE
)。由于这在代码中看起来很糟糕,而且在我看来甚至是错误的,因为方法已经在命名空间中,有更好的解决方案吗?
现在还有OAE_BEGIN/END_MOC_NAMESPACE
(类似于QT_BEGIN/END_MOC_NAMESPACE
),所以也许我在任何地方都需要这些?我不知道它们在 Qt 中的何处/如何使用,因此我应该在我的库中相应地使用它们,因为我想使用与 Qt 相同的可选命名空间功能。
【问题讨论】:
我将添加一些有关 moc 生成的代码的信息(例如,如何解析插槽的签名)。 使用带有命名空间限定类型名的 qmlRegisterType 会改变什么吗? @mlvjr 感谢您的提示,但我已经尝试过了。实际上,这不应该改变任何东西,因为这是一个 C++ 类型说明符,但这是关于moc
如何解析头文件的。它没有检测到命名空间内的Player
在命名空间外使用时实际上引用了类oae::Player
。我也相信这种行为在 Qt 5.0.0 alpha 和 beta1 中是不同的。
【参考方案1】:
它在 5.0.0a 中真的有效吗?
我浏览了 Qt 5.0.0 的源代码并查看了解析方法的位置,尤其是返回类型(仅供参考,5.0.0\qtbase\src\tools\moc\moc.cpp:L160)并且没有命名空间检查(在论点上都没有,所以player(Player* p)
也不起作用)。
然而,它是为类 def (5.0.0\qtbase\src\tools\moc\moc.cpp:L620 & L635)
我认为“我们”可以称之为错误(或疏忽)
【讨论】:
感谢您的努力。我明天再检查一下。当然错误可能在我这边,我不想排除这种可能性;) 好吧,似乎不也可以在 Qt 5.0.0 beta1 中工作。我刚刚重试了。奇怪的是我记得它有效。感谢您花时间在 moc 代码中找到这些东西,并祝贺您获得赏金!以上是关于在 QML 中使用 C++-slot,它返回命名空间中的类型的主要内容,如果未能解决你的问题,请参考以下文章