Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to serialize/deserialize a Qlist<MyClass*>

How to serialize/deserialize a Qlist<MyClass*>

Scheduled Pinned Locked Moved General and Desktop
10 Posts 4 Posters 6.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    bereid
    wrote on last edited by
    #1

    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!

    1 Reply Last reply
    0
    • A Offline
      A Offline
      alex_malyu
      wrote on last edited by alex_malyu
      #2

      This may help:
      http://www.ics.com/blog/object-serialization-and-persistence-using-qdatastream

      B 1 Reply Last reply
      3
      • TheBadgerT Offline
        TheBadgerT Offline
        TheBadger
        wrote on last edited by TheBadger
        #3

        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)


        Check out my SpellChecker Plugin for Qt Creator @ https://github.com/CJCombrink/SpellChecker-Plugin

        B 1 Reply Last reply
        3
        • jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

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

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          TheBadgerT 1 Reply Last reply
          2
          • jsulmJ jsulm

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

            TheBadgerT Offline
            TheBadgerT Offline
            TheBadger
            wrote on last edited by
            #5

            @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).


            Check out my SpellChecker Plugin for Qt Creator @ https://github.com/CJCombrink/SpellChecker-Plugin

            jsulmJ 1 Reply Last reply
            2
            • TheBadgerT TheBadger

              @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).

              jsulmJ Offline
              jsulmJ Offline
              jsulm
              Lifetime Qt Champion
              wrote on last edited by
              #6

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

              https://forum.qt.io/topic/113070/qt-code-of-conduct

              B 1 Reply Last reply
              2
              • TheBadgerT TheBadger

                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)

                B Offline
                B Offline
                bereid
                wrote on last edited by
                #7

                @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;
                }
                
                1 Reply Last reply
                1
                • A alex_malyu

                  This may help:
                  http://www.ics.com/blog/object-serialization-and-persistence-using-qdatastream

                  B Offline
                  B Offline
                  bereid
                  wrote on last edited by
                  #8

                  @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.

                  A 1 Reply Last reply
                  0
                  • jsulmJ jsulm

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

                    B Offline
                    B Offline
                    bereid
                    wrote on last edited by
                    #9

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

                    1 Reply Last reply
                    0
                    • B bereid

                      @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.

                      A Offline
                      A Offline
                      alex_malyu
                      wrote on last edited by alex_malyu
                      #10

                      @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 :)

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved