How to serialize/deserialize a Qlist<MyClass*>



  • I have searched the web and the forum for answers to this question, but have not been able to find information to help me understand how to implement the stream operator functions to achieve this action.

    If I have a class MyClassEntry, and another class MyClass containing a member QList<MyClassEntry*> _myPointerList, and I want to serialize/deserialize MyClass, how must I implement the in/out stream operator functions to satisfy the requirements?

    I am seeking specific details about how to implement these functions:

    QDataStream &operator<<(QDataStream &out, const MyClass &mc);
    QDataStream &operator>>(QDataStream &in, MyClass &mc);

    and/or

    QDataStream &operator<<(QDataStream &out, const MyClassEntry &mce);
    QDataStream &operator>>(QDataStream &in, MyClassEntry &mce);

    I would like to phrase my question more specifically, but at this stage, I am unable to do so. With any feedback, I hope I will be able to pose other more specific questions resulting from things I don't yet understand.

    One of the many things I am confused about relates to whether I must perform the allocation of the members of _myPointerList, or if the higher level stream operator methods will know that because the container holds pointers, it must do the allocation on my behalf.

    Any examples of code performing actions like this will be extremely helpful!

    Thank you for any guidance!





  • Hi,

    My suggestion would be to first implement the serialisation of MyClassEntry and make sure that works:

    QDataStream &operator<<(QDataStream &out, const MyClassEntry &mce);
    QDataStream &operator>>(QDataStream &in, MyClassEntry &mce);
    

    Then implement the serialisation of MyClass.
    When you get to the step to serialise the _myPointerList, first write out the number of items in the list to the stream, then write out each entry.

    On the reading part, read the number of items and then read in each entry.
    Something like (this assumes C++11):

    // Export
    QDataStream &operator<<(QDataStream &out, const MyClass &mc) {
        out << _myPointerList.size();
        for(MyClassEntry* entry: _myPointerList) {
            out << *entry; 
        }
    }
    
    // Import
    QDataStream &operator>>(QDataStream &in, MyClass &mc) {
        int nrEntries;
        in >> nrEntries;
        for(int entry = 0; entry < nrEntries; ++entry) {
            MyClassEntry* newEntry = new MyClassEntry();
            in >> *newEntry; 
            _myPointerList.append(newEntry);
        }
    }
    

    Hope this gets you started

    Edit: fixed import and export based on the comment of @jsulm below (Thanks)


  • Moderators

    It should be "out << *entry" not "out << entry" (else you will store the pointer not the object itself which is useless)



  • @jsulm
    Thanks, I have edited my original post.

    Does this apply to the import as well: in >> *newEntry;

    I have done some exporting of lists, but not on pointers (yet).


  • Moderators

    @TheBadger Yes, forgot about in >> newEntry. Since newEntry is a pointer and you want to read the object you have to dereference the pointer like you did above: in >> *newEntry;



  • @TheBadger

    Thank you VERY much for taking the time to offer that very helpful bit of advice. I have written a short and simple program to prove the concept, and convince myself that I'm able to implement something that works (at least for this very simple case). I'll post the code of my test program below to see if there are any suggestions for improvements to the approach.

    While writing this small test (mostly using your suggestions), I did encounter a couple of curious things that I can't completely understand:

    1. I can fully understand why I would need to implement the stream operators for MyClassEntry, since I have knowledge of its internals, and which members need to be serialized/deserialized, which the standard Qt code does not know. But regarding the higher level MyClass, which contains the QList<MyClassEntry*> elements, I would have thought that the standard Qt code for streaming QList<T> could be employed, rather than having to do as you suggested, and write my own functions to save/restore the number of list elements, and then iterate through them myself, invoking the MyClassEntry* stream operators on each. But when I tried this, I found that it did not work. Is it mainly because my QList elements are pointers, rather than actual objects? I mean, what is the point of the standard QList<T> streaming operators if I must write my own? I would think that given any QList<T> (even it T is a pointer), as long as T implements the appropriate stream operators, then the standard QList stream operators should take care of saving/restoring the number of elements and iterating through them, invoking T's stream operators for each element. What am I missing here?
    2. My test code includes an operation to compare two MyClass instances. Again, I would expect that I would need to write the comparison operators for the lower level MyClassEntry, but that I should be able to use the standard Qt facilities for comparing two QList<T> instances. This also did not work, and I don't quite understand why. I stepped through the code to investigate the low level comparisons, and found that for each element of the QList, stl::equal() is eventually used to perform the comparison. At that point, I inspected the iterators referring to the first element of both containers, and I could see that they do contain the same member contents. Still, the stl code declared them unequal. Perhaps I'll post this as a separate question.

    Anyway, thank you again for your generous help. Here is the code I used to prove the concept:

    main.cpp ———————————————————————————————
    
    #include <QtCore>
    #include <myclass.h>
    
    int main(int argc, char *argv[])
    {
        MyClass mc1;
        mc1.addEntry("one", 1);
        mc1.addEntry("two", 2);
        mc1.addEntry("three", 3);
    
        QFile persistenceFile("persist.bin");
        persistenceFile.open(QIODevice::ReadWrite);
        QDataStream stream(&persistenceFile);
        stream << mc1;
    
        stream.device()->reset();
        MyClass mc2;
        stream >> mc2;
    
        persistenceFile.close();
    
        if (mc2 == mc1)
        {
            qDebug() << "serialization/deserialization succeeded";
        }
        else
        {
            qDebug() << "serialization/deserialization failed";
            mc1.dump("mc1");
            mc2.dump("mc2");
        }
    
        return 0;
    }
    
    myclass.h ———————————————————————————————
    
    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QtCore>
    
    class MyClassEntry;
    
    class MyClass
    {
    public:
        MyClass();
        void addEntry(MyClassEntry *mce);
        void addEntry(QString name, int value);
        void dump(const QString &instanceName) const;
        bool operator==(const MyClass &other);
    
    private:
        friend QDataStream &operator>>(QDataStream &in, MyClass &m);
        friend QDataStream &operator<<(QDataStream &out, const MyClass &m);
    
        QList<MyClassEntry *> _entries;
    };
    
    #endif // MYCLASS_H
    
    myclass.cpp ———————————————————————————————
    
    #include "myclass.h"
    #include "myclassentry.h"
    
    MyClass::MyClass()
    {
    }
    
    void MyClass::addEntry(MyClassEntry *mce)
    {
        _entries.append(mce);
    }
    
    void MyClass::addEntry(QString name, int value)
    {
        _entries.append(new MyClassEntry(name, value));
    }
    
    void MyClass::dump(const QString &instanceName) const
    {
        qDebug() << "MyClass(" << instanceName << ") holds"
                 << _entries.size() << " entries:";
        for (int i = 0; i < _entries.size(); ++i)
        {
            QDebug dbg = qDebug();
            dbg << "   Entry" << i;
            _entries.at(i)->dump(dbg);
        }
    }
    
    bool MyClass::operator==(const MyClass &other)
    {
        bool result = (_entries.size() == other._entries.size());
        for (int i = 0; result && (i < _entries.size()); ++i)
        {
            result = (*_entries.at(i) == *other._entries.at(i));
        }
        return result;
    }
    
    QDataStream &operator>>(QDataStream &in, MyClass &m)
    {
        int numEntries;
        in >> numEntries;
    
        for (int i = 0; i < numEntries; ++i)
        {
            MyClassEntry *mce = new MyClassEntry();
            in >> mce;
            m.addEntry(mce);
        }
        return in;
    }
    
    QDataStream &operator<<(QDataStream &out, const MyClass &m)
    {
        int numEntries = m._entries.size();
        out << numEntries;
        for (int i = 0; i < numEntries; ++i)
        {
            MyClassEntry *mce = m._entries.at(i);
            out << mce;
        }
        return out;
    }
    
    myclassentry.h ———————————————————————————————
    
    #ifndef MYCLASSENTRY_H
    #define MYCLASSENTRY_H
    
    #include <QtCore>
    
    class MyClassEntry
    {
    public:
        MyClassEntry();
        MyClassEntry(QString name, int value) :
            _value(value),
            _name(name)
        {
        }
        void dump(QDebug &dbg);
        bool operator==(const MyClassEntry &other);
    
    private:
        friend QDataStream &operator>>(QDataStream &in, MyClassEntry* mce);
        friend QDataStream &operator<<(QDataStream &out, const MyClassEntry* mce);
    
        int _value;
        QString _name;
    };
    
    #endif // MYCLASSENTRY_H
    
    myclassentry.cpp ———————————————————————————————
    
    #include "myclassentry.h"
    
    MyClassEntry::MyClassEntry()
    {
    }
    
    void MyClassEntry::dump(QDebug &dbg)
    {
        dbg.nospace() << "name=" << _name
                      << " value=" << _value;
    }
    
    bool MyClassEntry::operator==(const MyClassEntry &other)
    {
        return (_value == other._value) && (_name == other._name);
    }
    
    QDataStream &operator>>(QDataStream &in, MyClassEntry* mce)
    {
        in >> mce->_name >> mce->_value;
        return in;
    }
    
    QDataStream &operator<<(QDataStream &out, const MyClassEntry* mce)
    {
        out << mce->_name << mce->_value;
        return out;
    }
    


  • @alex_malyu
    Thank you for that reference, which does contain some interesting information. However, after my first reading of it, I don't see how I can use it to address my main concern, which relates to saving/restoring a QList of pointers. But I have bookmarked the page for future reference.



  • @jsulm Thank you for this helpful contribution to the discussion!



  • @bereid

    Lets see.
    You can't serialize pointers. You serialize objects.
    It means that the only one thing is different - the way you store objects.

    Instead of list<Foo> you have list<Foo*>
    This makes the code difference minimal.
    And difference is in one extra line with operator new.

    And thanks for vote :)


Log in to reply
 

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