1
votes

The common part (crating a lib) of my project currently holds a interface Class of the type:

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

Now i also wantred to use the programm on a Class containing data in a QVariant, so i started it off simple:

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

Now one of the datatypes addedt to the list (outside the lib) is of the type:

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

And it is added to the map using "QVariant::fromValue(", now i need a way in the "DataContainingClass" to check if the Data is derived from a QObject, so i know static_cast(variant.data()) is valid. Then i could try to dynamic_cast the Object pointer to CanBeAddedToGroup, and call it.

--- EDIT ---: The Problem IS NOT: Having a QObject and check if it inherits another QObject, it is not even checking if a Class inherits from another one, it is to know if the data i have actually IS a QObject.

Minimal Example: Header File:

#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 File:

// 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();

I need a way to distinguisch cases 1&2 from 3&4 ("schouldSegFault"), without using something only defined outside of the lib.

I Already Tryed:

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

But help2 has a MetaObject of 0, so i cant check for the inheriance from QObject.

Edit/for who added "Proper way to check QObject derived class type in Qt" ther was te issue in my programm that the class inherits from another QObjectclass so i cant chack for inheriance of my interface (even when defined as Q_Gadget) using inherits, since it would only be true for the first element.

PS: For everyone trying to call functions on a QVariant containing a Object rather than a pointer might be interested in this approach: How to support comparisons for QVariant objects containing a custom type? / https://pastebin.com/tNLa0jSa

While having a global registry for types is what i wished i could avoid for the case.

2
That only deals if i already know that i have a QObject, but finding out if i have a QObject is exactly my problem. As far as i know QVariant could also be holding a Q_Gadget or a basetype.edisn
They call that part a "Question" for a reason ;)scopchanov
STOP QObjects are neither copyable nor moveable. You cannot store one in a QVariant. Your code doesn't even remotely do what you think it does. This does nothing: QVariant::fromValue(dC).Kuba hasn't forgotten Monica
QVariant::fromValue(dC) does call CanClass(const CanClass&d){TST="Clone";} so atleast it is a copy of the original data.edisn

2 Answers

2
votes

Just try to use QVariant::value and see if the value in a QVariant can be converted to your target class. Here's a minimal example:

#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"

Running it should yield the following output on the console:

"bar" is an instance of MyQObjectClass
I am a MyQObjectClass!
"foo" is not an instance of MyQObjectClass

The important parts:

  1. Make sure the class you want to store in a QVariant derives from QObject and has the Q_OBJECT macro.
  2. When iterating over the map, use QVariant::value() and try to convert the contained value to your target class. In the example, I use QVariant::value<MyQObjectClass*>() - according to the documentation, this either returns the contained instance of MyQObjectClass* if the value can be converted to it or - which is the case if the QVariant contains either basic values or gadgets - a default constructed value. In the case of a pointer this would be a null pointer, so just check if the value returned is null. That's it.

Never work on Qvariant::data() directly.

Update

Just as a remark: The Qobject class declared the copy constructor and assignment operators as private:

From the 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.

Hence, you cannot copy around instances of QObject (and consequentially you cannot store them in QVariant). Instead, you pass around pointers to QObject instances.

Update #2

If your interface class cannot derive directly from QObject, you might consider using Qt's plugin mechanism instead. Here's the above example slightly edited to fit this approach:

#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"

You need to:

  1. Define your interface class and register it with Qt using the Q_DECLARE_INTERFACE macro.
  2. Declare your concrete classes, deriving from QObject and your interface class. In addition, you need to tell Qt about the interface part using the Q_INTERFACES macro.
  3. When checking the values in your map, first try to convert to a QObject* via QVariant::value(). If this succeeds, you can try to qobject_cast to your interface class.
0
votes

Your design is completely broken: QObjects cannot be used as unrestricted values. They cannot be copied nor moved, and your implementations of copy constructors are hiding this fundamental fact.