QVariant 调用存储对象的函数,定义在项目的主要部分
Posted
技术标签:
【中文标题】QVariant 调用存储对象的函数,定义在项目的主要部分【英文标题】:QVariant call a function of a stored object, defined in the main part of a project 【发布时间】:2018-05-19 10:19:22 【问题描述】:我的项目的公共部分(创建一个库)当前包含一个类型的接口类:
class CanBeAddedToGroup
public:
void addToGroup(Group& )=0;
现在我还想在包含 QVariant 中数据的类上使用该程序,所以我开始很简单:
class DataContainingClass: public CanBeAddedToGroup
QMap<QString,QVarient> data;
public:
void addToGroup(Group& )
QMap<QString,QVarient>::itterator itter = data.begin();
QMap<QString,QVarient>::itterator end= data.end();
for(;itter !=end;itter ++)
<Handle Data>
;
现在添加到列表中的数据类型之一(在库之外)是以下类型:
class DataClass: public QObject, public CanBeAddedToGroup
void addToGroup(Group& );
Q_DECLARE_METATYPE(DataClass)
它使用“QVariant::fromValue(”添加到地图中,现在我需要在“DataContainingClass”中检查数据是否来自 QObject,所以我知道 static_cast(variant.data() ) 已验证。 然后我可以尝试将 Object 指针动态转换为 CanBeAddedToGroup,然后调用它。
--- 编辑 ---: 问题不在于:拥有一个 QObject 并检查它是否继承了另一个 QObject,甚至不检查一个类是否继承自另一个类,而是要知道我拥有的数据是否实际上是一个 QObject。
最小的例子: 头文件:
#include <QObject>
#include <QDebug>
class DmbClass: public QObject
Q_OBJECT
public:
DmbClass()TST="RealOne";
DmbClass(const DmbClass&d)TST="Clone";
QString TST;
;
Q_DECLARE_METATYPE(DmbClass)
class Tst
public:
virtual void tstFunct()=0;
;
class CanClass: public QObject, public Tst
Q_OBJECT
public:
CanClass()TST="RealOne";
CanClass(const CanClass&d)TST="Clone";
QString TST;
virtual void tstFunct() override
qDebug()<<"tst";
;
Q_DECLARE_METATYPE(CanClass)
class DmbGadget
Q_GADGET
public:
DmbGadget()TST="RealOne";
DmbGadget(const DmbGadget&d)TST="Clone";
QString TST;
;
Q_DECLARE_METATYPE(DmbGadget)
C 文件:
// QObject in QVariant
DmbClass dC;
QVariant varC=QVariant::fromValue(dC);
const void* vPC = varC.data();
DmbClass dCc = varC.value<DmbClass>();
QObject* objC = (QObject*)varC.data();
QObject* schouldWork = objC->parent();
Tst* schouldBeNull = dynamic_cast<Tst*>(objC);
// Object using correct base class in QVariant
CanClass dT;
QVariant varT=QVariant::fromValue(dT);
const void* vPT = varT.data();
CanClass dTc = varC.value<CanClass>();
QObject* objT = (QObject*)varT.data();
QObject* schouldWork2 = objT->parent();
Tst* schouldNotNull = dynamic_cast<Tst*>(objT);
schouldNotNull->tstFunct();
// Q_Gadget in QVariant
DmbGadget dG;
QVariant varG=QVariant::fromValue(dG);
const void* vPG = varG.data();
DmbGadget dGg = varG.value<DmbGadget>();
QObject* objD = (QObject*)varG.data();
//QObject* schouldSegFault = objD->parent();
// base value in QVariant
QVariant var4=4;
const void* vP4 = var4.data();
QObject* obj4 = (QObject*)var4.data();
//QObject* schouldSegFault2 = obj4 ->parent();
我需要一种方法来区分案例 1&2 和 3&4(“schouldSegFault”),而不使用仅在库之外定义的东西。
我已经尝试过:
int tst4 = qRegisterMetaType<CanClass>("CanClass");
QMetaType help2(tst4);
但是 help2 的 MetaObject 为 0,所以我无法检查来自 QObject 的继承。
编辑/为谁添加了“Proper way to check QObject derived class type in Qt”,我的程序中有一个问题是该类继承自另一个QObject
class,所以我不能使用继承来获取我的接口的继承(即使定义为 Q_Gadget),因为它只适用于第一个元素。
PS:对于每个尝试在包含 Object 而不是指针的 QVariant 上调用函数的人可能会对这种方法感兴趣: How to support comparisons for QVariant objects containing a custom type? / https://pastebin.com/tNLa0jSa
虽然我希望在这种情况下避免使用全局类型注册表。
【问题讨论】:
Proper way to check QObject derived class type in Qt的可能重复 只有在我已经知道我有一个 QObject 的情况下才有效,但找出我是否有一个 QObject 正是我的问题。据我所知,QVariant 也可能持有 Q_Gadget 或基本类型。 他们称那部分为“问题”是有原因的;) STOPQObject
s 既不能复制也不能移动。您不能在QVariant
中存储一个。您的代码甚至没有远程执行您认为的操作。这什么都不做:QVariant::fromValue(dC)
.
QVariant::fromValue(dC)
确实调用了CanClass(const CanClass&d)TST="Clone";
,所以至少它是原始数据的副本。
【参考方案1】:
只需尝试使用QVariant::value 并查看QVariant
中的值是否可以转换为您的目标类。这是一个最小的例子:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyQObjectClass : public QObject
Q_OBJECT
public:
explicit MyQObjectClass(QObject *parent = nullptr) : QObject(parent)
void greet() qDebug() << "I am a MyQObjectClass!";
;
Q_DECLARE_METATYPE(MyQObjectClass*)
int main(int, char *[])
MyQObjectClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++)
auto value = iter.value();
// Try to convert to MyQObjectClass*
auto obj = value.value<MyQObjectClass*>();
if (obj != nullptr)
qDebug() << iter.key() << "is an instance of MyQObjectClass";
obj->greet();
continue;
qDebug() << iter.key() << "is not an instance of MyQObjectClass";
#include "main.moc"
运行它应该在控制台上产生以下输出:
"bar" is an instance of MyQObjectClass
I am a MyQObjectClass!
"foo" is not an instance of MyQObjectClass
重要部分:
-
确保要存储在 QVariant 中的类派生自
QObject
并具有 Q_OBJECT
宏。
迭代地图时,使用QVariant::value()
并尝试将包含的值转换为您的目标类。在示例中,我使用 QVariant::value<MyQObjectClass*>()
- 根据文档,如果值可以转换为 MyQObjectClass*
,则返回包含的实例,或者 - 如果 QVariant
包含基本值或小工具,则为这种情况- 默认构造值。在指针的情况下,这将是一个空指针,所以只需检查返回的值是否为空。就是这样。
永远不要直接在Qvariant::data()
上工作。
更新
正如备注:Qobject
类将复制构造函数和赋值运算符声明为私有:
来自official documentation:
QObject 既没有复制构造函数也没有赋值运算符。这是设计使然。实际上,它们是被声明的,但是在一个带有宏 Q_DISABLE_COPY() 的私有部分中。事实上,所有从 QObject(直接或间接)派生的 Qt 类都使用这个宏来声明它们的复制构造函数和赋值运算符是私有的。推理可在 Qt 对象模型页面上关于 Identity vs Value 的讨论中找到。
因此,您无法复制QObject
的实例(因此您无法将它们存储在QVariant
中)。相反,您传递指向QObject
实例的指针。
更新 #2
如果您的接口类不能直接从QObject
派生,您可以考虑改用Qt's plugin mechanism。下面是上面的例子,为了适应这种方法,做了一些修改:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyInterfaceClass
public:
MyInterfaceClass()
virtual ~MyInterfaceClass()
virtual void greet() = 0;
;
#define MyInterfaceClass_IID "org.example.MyInterfaceClass"
Q_DECLARE_INTERFACE(MyInterfaceClass, MyInterfaceClass_IID)
class MyConcreteClass : public QObject, public MyInterfaceClass
Q_OBJECT
Q_INTERFACES(MyInterfaceClass)
public:
MyConcreteClass(QObject *parent = nullptr) : QObject(parent)
void greet() override qDebug() << "I am a MyInterfaceClass!";
;
int main(int, char *[])
MyConcreteClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++)
auto value = iter.value();
// Try to convert to QObject*:
auto obj = value.value<QObject*>();
if (obj != nullptr)
// Try if we can cast to our interface class:
auto ifc = qobject_cast<MyInterfaceClass*>(obj);
if (ifc != nullptr)
qDebug() << iter.key() << "is an instance of MyInterfaceClass";
ifc->greet();
continue;
qDebug() << iter.key() << "is not an instance of MyInterfaceClass";
#include "main.moc"
你需要:
-
定义您的接口类并使用
Q_DECLARE_INTERFACE
宏将其注册到 Qt。
声明您的具体类,派生自QObject
和您的接口类。另外,你需要使用Q_INTERFACES
宏告诉Qt接口部分。
检查地图中的值时,首先尝试通过QVariant::value()
转换为QObject*
。如果成功,您可以尝试qobject_cast
到您的接口类。
【讨论】:
我的问题是我只知道库部分中的抽象基类,所以我不能使用值函数。它不会将我存储的对象转换为 QObject,也不能用于抽象基类。) PS:CanBeAddedToGroup 继承自 QObject,因为在实际程序中 DataClass 将继承自另一个 QObject 派生类。 为什么不简单地从QObject
派生基类?
如果你真的不能让你的接口派生自QObject,可以考虑Qt的插件机制。我已经相应地更新了我的答案 - 也许这会有所帮助;-)
如果 CanBeAddedToGroup 是一个 QObject,DataClass 将继承 2 个 QObject,据我所知只有问题。另一方面,DataClass 在我的部分代码之外,所以我不能在演员阵容中使用它。但是:您的更新似乎已经包含了一种方式:【参考方案2】:
您的设计完全被破坏了:QObject
s 不能用作不受限制的值。它们不能被复制或移动,而您的复制构造函数的实现隐藏了这个基本事实。
【讨论】:
唯一的其他选择是给类一个“.copy()”函数,或者能够从它自己的实例中设置(操作符=)。在积极使用时,我们需要 setter/getter 上的信号。 似乎也可以将数据移动到一个内部结构中,该结构除了持有它(而不是活动的)并使用它来创建 acriveQObject
使用此数据。但我需要一个更好的方法来添加QMetaType::registerConverter<DataType*,Capability*>();
假设我将DataType*
添加到用于存储的QMap<QString,QValue>
。加上DataType
,我会不知所措。
PS:这是因为我在定义Capability
类的部分工作,该类静态链接到定义DataType
的部分以上是关于QVariant 调用存储对象的函数,定义在项目的主要部分的主要内容,如果未能解决你的问题,请参考以下文章