How do I do qobject2variant in a nested QObject container?

  • Hi,

    I have a class "Person" that the declaration is like this:

    #include <QObject>

    class Person : public QObject

    Q_PROPERTY(QString name READ getName WRITE setName)
    Q_PROPERTY(QString firstLastName READ getFirstLastName WRITE setFirstLastName)
    Q_PROPERTY(QString secondLastName READ getSecondLastName WRITE setSecondLastName)
    Q_PROPERTY(qint32 age READ getAge WRITE setAge)

    explicit Person(QObject *parent = 0);
    void setName(QString name);
    void setFirstLastName(QString firstLastName);
    void setSecondLastName(QString secondLastName);
    void setAge(qint32 age);
    QString getName();
    QString getFirstLastName();
    QString getSecondLastName();
    qint32 getAge();

    QString mName;
    QString mFirstLastName;
    QString mSecondLastName;
    qint32 mAge;

    Now, if I do this:

    Person p;
    QVariant variant = QObjectHelper::qobject2qvariant(&p);

    The properties in that class are converted into a QVariant.

    But what if I declare a class "Personnel" where I also want it to have the ability to be converted to QVariant, and inside it, the property personList to be turned into a QVariantList, I want to have a list of Person objects, if I declare it like this:

    #include <QObject>
    #include "Person.h"

    class Personnel : public QObject

    Q_PROPERTY(QString group READ getGroup WRITE setGroup)
    Q_PROPERTY(QList<Person> personList READ getPersonList WRITE setPersonList)

    explicit Personnel(QObject *parent = 0);
    void setGroup(QString group);
    void setPersonList(QList<Person> personList);
    QString getGroup();
    QList<Person> getPersonList();

    QList<Person> mPersonList;
    QString mGroup;

    I get an error:
    Person.h(31) : error C2248: 'QObject::QObject' : cannot access private member declared in class 'QObject'

    What do I have to change to be able to convert Personnel to QVariant and the personList inside it to be turned into a QVariantList of Person as QVariant automatically? I want to achieve this so I can convert Personnel into a QVariant and serialize it to disk later and then with QObjectHelper::qvariant2qobject deserialize it back to objects.

  • QObject and subclasses cannot be used as values (copy constructor and assignment operator are private). You try to do this in line 19 and 16 of your snippet. You can only store pointers to QObjects in a QList:

    QList<Person *> mPersonList;

  • The problem with QObject pointers is having to destroy them myself right? I would have to use QPointer or something like that so I don't get memory leaks.
    But if I try QList<QPointer<Person> >, I still get the same error, so how would I use smart pointers in my code?

  • [quote author="Raul" date="1329789941"]The problem with QObject pointers is having to destroy them myself right?[/quote]
    It depends. If they have a parent, they are automatically destroyed as soon as the parent is destroyed. So if you pass the Personnel object as the parent to Person objects you add to the QList they are automatically destroyed as soon as the Personnel object is destroyed.

    [quote author="Raul" date="1329789941"]I would have to use QPointer or something like that so I don't get memory leaks.[/quote]
    No, you just have to <code>delete</code> them, for example as you take them out of the list or in the destructor.

    Smart pointers can do this for you (semi-)automatically, but they are neither needed nor a guarantee that your application doesn't leak memory.

    In addition, QPointer won't delete the object for you (it just ensures that it isn't dangling). QSharedPointer or QScopedPointer is what you are looking for.

    [quote author="Raul" date="1329789941"]But if I try QList<QPointer<Person> >, I still get the same error, so how would I use smart pointers in my code?[/quote]
    Well, this should work.

  • Ok so if I create personnel, and set it as personList parent, and then each person in the list as personnel children as well, that would mean that all of them would get automatically destroyed right?

    That way I don't have to worry about memory leaks, I'll do some tests on it and let you know how goes.


  • You can't set a parent for QList, as it doesn't inherit QObject, but you don't need to, because as a member of Personnel it is automatically destructed along with the rest of the class anyways. All you have to do is to pass Personnel as parent to Person.

    Alternatively (or additionally) you can iterate over the list passed to setPersonList() and explicitly set the parent for each Person to this (so their relationship is guaranteed).
    int main(int argc, char argv[])
    personnel = new Personnel;

    QList<Person*> personList;
    personList.append(new Person(personnel));
    personList.append(new Person(personnel));
    personList.append(new Person(personnel));
    delete personnel; // personnel is deleted
                      // personnel.mPersonList is deleted 
                      //    because it is a member of personnel
                      // every Person in personnel.mPersonList is deleted 
                      //    because their parent (personnel) is deleted

    An alternative would be to just delete the objects stored in mPersonList in the destructor of Personnel.
    qDeleteAll(mPersonList.begin(), mPersonList.end()); // walks through the list and
    // calls delete for every member
    The most important things is that you have a well-defined ownership, which means you have to define who owns the Person objects and is therefor responsible for deleting.

    This could be either the object who created the Person objects - main() in our example - or it is the object managing the Person objects - personnel in our example. Both versions have their advantages and disadvantages. For example, #1 enables you to pass the same list of objects to multiple Personnel objects, #2 ensures that the objects are deleted when their "managing class" is deleted.

    It is just a matter of definition. Make sure you have one and you document it.

  • Wow great explanation! thanks a lot!, so then, from what you say, also seeing that QList is not a QObject, calling object2variant wouldn't actually convert QList to a QVariantList and all of the persons inside the QList wouldn't be converted to a qvariant?

    If that is so then, It would be better for me to learn more about QObject and it's metaobject model, and implement some sort of memento pattern for serialization instead of trying to do it through a qvariant?

  • Just let me get this right: We are talking about QJson here and you want to serialize a Personnel object using QObjectHelper::qobject2qvariant to either use it as a memento or to convert it to JSON later on.

    I don't think QJson supports user datatypes (like Person*), so you will either have to add support for it to QJson or you create your own serialization.

    And learning about QObject and the metaobject system is always a good idea ;-)

  • I'm actually using XmlStream rather than QJson... yes QJson could work as a JSON serializer, but what I want to achieve is to first set a canonical object based on QVariant that could be serialized or deserialized into any format, either XML, JSON, or whatever codec I decide to use or implement.

  • hi,
    Can i show above model to qml. i tried but i am unable to display inner class element. have any idea about to print this in qml.
    Thanks in advance.

  • Sorry, I wouldn't know how to do something like that in QML.

    But still, I think it would be better practice if you did this kind of logic in C++, and then just pass the object to QML back and forth, because QML is not designed to do this kind of work, QML is mostly UI related only.

  • Hi Raul,
    I created this logic in C++ (object).
    and now i want to pass this object to nested list view.
    see this post

    Any Idea about this

Log in to reply

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.