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”,我的程序中有一个问题是该类继承自另一个QObjectclass,所以我不能使用继承来获取我的接口的继承(即使定义为 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 或基本类型。 他们称那部分为“问题”是有原因的;) STOP QObjects 既不能复制也不能移动。您不能在QVariant 中存储一个。您的代码甚至没有远程执行您认为的操作。这什么都不做:QVariant::fromValue(dC). QVariant::fromValue(dC) 确实调用了CanClass(const CanClass&amp;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&lt;MyQObjectClass*&gt;() - 根据文档,如果值可以转换为 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】:

您的设计完全被破坏了:QObjects 不能用作不受限制的值。它们不能被复制或移动,而您的复制构造函数的实现隐藏了这个基本事实。

【讨论】:

唯一的其他选择是给类一个“.copy()”函数,或者能够从它自己的实例中设置(操作符=)。在积极使用时,我们需要 setter/getter 上的信号。 似乎也可以将数据移动到一个内部结构中,该结构除了持有它(而不是活动的)并使用它来创建 acrive QObject 使用此数据。但我需要一个更好的方法来添加QMetaType::registerConverter&lt;DataType*,Capability*&gt;(); 假设我将DataType* 添加到用于存储的QMap&lt;QString,QValue&gt;。加上DataType,我会不知所措。 PS:这是因为我在定义Capability 类的部分工作,该类静态链接到定义DataType 的部分

以上是关于QVariant 调用存储对象的函数,定义在项目的主要部分的主要内容,如果未能解决你的问题,请参考以下文章

QVariant实质

QVariant 中的自定义类型转换为空字符串

QVariant.toXXX 函数的函数指针

自定义数据类型使用QVariant转换的方法

QFlags 和 QVariant

转载:QByteArray和QVariant