QVariant调用存储对象的函数,该函数在项目的主要部分中定义

时间:2018-05-19 10:19:22

标签: qt qt5 qvariant

我项目的公共部分(crating lib)目前拥有一个类型为的接口Class:

class CanBeAddedToGroup{
public:
void addToGroup(Group& )=0;
}

现在我还想在包含QVariant数据的Class上使用该程序,所以我开始简单:

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>
    }
  }
  };
}

现在添加到列表中的一个数据类型(在lib之外)属于以下类型:

class DataClass: public QObject, public CanBeAddedToGroup{
void addToGroup(Group& );
}
Q_DECLARE_METATYPE(DataClass)

使用“QVariant :: fromValue”将它添加到地图中,现在我需要在“DataContainingClass”中检查数据是否来自QObject,所以我知道static_cast(variant.data() ) 已验证。 然后我可以尝试dynamic_cast对象指向CanBeAddedToGroup,并调用它。

---编辑---: 问题不是:拥有一个QObject并检查它是否继承了另一个QObject,它甚至不检查一个Class是否继承了另一个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&amp; 4(“schouldSegFault”),而不使用仅在lib之外定义的东西。

我已经尝试过:

int tst4 = qRegisterMetaType<CanClass>("CanClass");
QMetaType help2(tst4);

但是help2的MetaObject为0,所以我无法检查QObject的继承。

编辑/为谁添加了“Proper way to check QObject derived class type in Qt”在我的程序中问题是该类继承自另一个QObject类,所以我无法查询我的接口的继承(即使定义为Q_Gadget)使用继承,因为它只适用于第一个元素。

PS:对于每个试图在包含Object而不是指针的QVariant上调用函数的人都可能对这种方法感兴趣: How to support comparisons for QVariant objects containing a custom type? / https://pastebin.com/tNLa0jSa

虽然拥有类型的全球注册表是我希望我能避免的情况。

2 个答案:

答案 0 :(得分:2)

只需尝试使用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

重要部分:

  1. 确保您要存储在QVariant中的课程来自QObject,并且具有Q_OBJECT宏。
  2. 在迭代地图时,使用QVariant::value()并尝试将包含的值转换为目标类。在示例中,我使用QVariant::value<MyQObjectClass*>() - 根据文档,如果值可以转换为它,则返回包含的MyQObjectClass*实例,如果QVariant,则返回包含基本值或小工具 - 默认构造值。在指针的情况下,这将是一个空指针,因此只需检查返回的值是否为null。那就是它。
  3. 永远不要直接使用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"
    

    你需要:

    1. 使用Q_DECLARE_INTERFACE宏定义您的接口类并使用Qt注册它。
    2. 声明您的具体类,派生自QObject和您的接口类。此外,您需要使用Q_INTERFACES宏告诉Qt有关接口部分。
    3. 检查地图中的值时,首先尝试通过QObject*转换为QVariant::value()。如果成功,您可以尝试qobject_cast到您的接口类。

答案 1 :(得分:0)

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