1

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.

edisn
  • 58
  • 11
  • 1
    Possible duplicate of [Proper way to check QObject derived class type in Qt](https://stackoverflow.com/questions/1537080/proper-way-to-check-qobject-derived-class-type-in-qt) – Mohammad Kanan May 19 '18 at 10:23
  • 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 May 19 '18 at 10:27
  • They call that part a "Question" for a reason ;) – scopchanov May 19 '18 at 11:42
  • **STOP** `QObject`s 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 May 19 '18 at 21:13
  • `QVariant::fromValue(dC)` does call `CanClass(const CanClass&d){TST="Clone";}` so atleast it is a copy of the original data. – edisn May 20 '18 at 23:02

2 Answers2

2

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.
Martin Höher
  • 715
  • 5
  • 9
  • My problem is that i opnly know the abstract Baseclass in the lib Part, therfore i cant use the vallue function. It wont convert my stored object to a QObject, neither can it be used for the abstract base Class.) – edisn May 19 '18 at 11:49
  • PS: The CanBeAddedToGroup dosent inherit from QObject, since in the actual programm the DataClass would inherit from another QObject derived class. – edisn May 19 '18 at 11:51
  • Why not simply deriving your base class from `QObject`? – Martin Höher May 19 '18 at 11:51
  • If you really cannot let your interface derive from QObject, the consider Qt's plugin mechanism instead. I've updated my answer accordingly - maybe that helps ;-) – Martin Höher May 19 '18 at 12:04
  • If CanBeAddedToGroup is a QObject, DataClass would inherit 2 QObjects which as far as i know only has problems. DataClass on the other hand is outside of my Part of the code so i cant use it in the cast. BUT: Your updates seem to already includ a way: – edisn May 19 '18 at 12:08
  • Most importatnly i have to change the part wher i add the data into the Map/Lists from ["KEY"]=QVariant::fromValue(data) to, ["KEY"]=QVariant::fromValue(new DataClass(data)) so i can use value.value() after that i schould be able to use dynamic_cast on the return. – edisn May 19 '18 at 12:12
  • PS: Since the parts are linked statically i schould be able to avoide most of the Plugin stuff, like MyInterfaceClass_IID. – edisn May 19 '18 at 12:13
  • If you can link statically, yes. However if you opt to use the plug-in approach, it would potentially make your app more flexible for the future (e.g. if you switch to dynamic linking or if you in fact want to use plugins at some point in time). – Martin Höher May 19 '18 at 12:16
  • Now i only have to make sure i realy get a coppy on the added value (so other parts of the system wont delete or modyfy the data), but i can atleast make sure that when what is passed to be addet is a QObject* i chack if ther is no other parrent and then set it to the Class containing the map. – edisn May 19 '18 at 12:18
  • We are currently thinking of switching the app-core to a (static) lib so we can acces its functions for tests, but plugins are not planed yet. But i will try this examples if i have the time. – edisn May 19 '18 at 12:24
0

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.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Only other option would be to give the class a ".copy()" function, or the ability to be set from a instnce of itself (operator =). When in active use we need signals on the setter/getter. – edisn May 20 '18 at 19:50
  • It also seems to be possible to move the data to an internal structure, which does nothinmg than holding it (while not active) and use it to vreate the acrive `QObject` using this data. But i would need a better way to add `QMetaType::registerConverter();` – edisn May 20 '18 at 20:09
  • Persuming i add `DataType*` to the `QMap` used for storage. I would be at a loss when `DataType` is added. – edisn May 20 '18 at 20:11
  • PS: This is because i work on the part defining the `Capability` class, which is statically linked into the part defining `DataType` – edisn May 20 '18 at 20:14