QVariant调用一个存储对象的函数,定义在项目的主体部分

QVariant call a function of a stored object, defined in the main part of a project

我的项目的公共部分(创建一个库)目前拥有一个接口 Class 类型:

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

现在我也想在 Class 上使用该程序,其中包含 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>
    }
  }
  };
}

现在添加到列表(在 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 和 4 ("schouldSegFault"),而不使用仅在 lib 外部定义的内容。

我已经尝试过:

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

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

Edit/for 谁添加了“Proper way to check QObject derived class type in Qt” 我的程序中有一个问题 class 继承自另一个 QObjectclass 所以我无法破解对于我的接口的继承(即使定义为 Q_Gadget)使用继承,因为它只适用于第一个元素。

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

虽然我希望在这种情况下可以避免使用类型的全局注册表。

只需尝试使用 QVariant::value 并查看 QVariant 中的值是否可以转换为您的目标 class。这是一个最小的例子:

#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 中的 class 派生自 QObject 并具有 Q_OBJECT 宏。
  2. 迭代地图时,使用 QVariant::value() 并尝试将包含的值转换为您的目标 class。在示例中,我使用 QVariant::value<MyQObjectClass*>() - 根据文档,如果值可以转换为 returns 包含的 MyQObjectClass* 实例,或者 - 如果 QVariant 包含基本值或小工具 - 默认构造值。在指针的情况下,这将是一个空指针,因此只需检查返回的值是否为空。就是这样。

切勿直接在 Qvariant::data() 上工作。

更新

正如备注:Qobject class 将复制构造函数和赋值运算符声明为私有的:

来自official documentation

QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.

因此,您不能复制 QObject 的实例(因此您不能将它们存储在 QVariant 中)。相反,您传递指向 QObject 个实例的指针。

更新#2

如果您的接口 class 不能直接从 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. 定义您的界面 class 并使用 Q_DECLARE_INTERFACE 宏将其注册到 Qt。
  2. 声明您的具体 classes,派生自 QObject 和您的界面 class。此外,您需要使用 Q_INTERFACES 宏将界面部分告诉 Qt。
  3. 检查地图中的值时,首先尝试通过 QVariant::value() 转换为 QObject*。如果成功,您可以尝试 qobject_cast 到您的界面 class.

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