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 do I properly serialize and deserialize a QList class in QT using QDatastream?
Forum Updated to NodeBB v4.3 + New Features

How do I properly serialize and deserialize a QList class in QT using QDatastream?

Scheduled Pinned Locked Moved Solved General and Desktop
29 Posts 5 Posters 7.2k Views 2 Watching
  • 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.
  • V VRonin
    18 Jul 2018, 14:45

    The normal way to do it:

    • create 2 pure virtual protected members in Layer that takes a QDataStream& argument and saves/loads the layer from it
    • create QDataStream stream operators for Layer (not Layer*) that do nothing but calling those protected methods
    • serialise something that will tell you what kind of Layer it will be (something like QVariant::userType does)
    • serialise directly by dereferencing a Layer* no need to cast anything

    RasterLayer *layer2 = new RasterLayer; in >> *layer2; is perfectly valid, as long as paintWidget owns the allocated memory

    T Offline
    T Offline
    twodee
    wrote on 18 Jul 2018, 15:06 last edited by
    #7

    @VRonin Hi, thanks for responding. I am getting this error which I usually can avoid using a const somewhere: error: passing ‘const RasterLayer’ as ‘this’ argument discards qualifiers [-fpermissive] : setName(name);

    As you can see in the layer.h, it is an inline method inline void setName(QString &name) { _name = name; } , what should I be doing in this case?

    1 Reply Last reply
    0
    • V Offline
      V Offline
      VRonin
      wrote on 18 Jul 2018, 15:11 last edited by
      #8

      Looks like you are deserialising on a const method. What is the code around that call?

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      T 2 Replies Last reply 18 Jul 2018, 15:19
      0
      • V VRonin
        18 Jul 2018, 15:11

        Looks like you are deserialising on a const method. What is the code around that call?

        T Offline
        T Offline
        twodee
        wrote on 18 Jul 2018, 15:19 last edited by
        #9
        This post is deleted!
        1 Reply Last reply
        0
        • V VRonin
          18 Jul 2018, 15:11

          Looks like you are deserialising on a const method. What is the code around that call?

          T Offline
          T Offline
          twodee
          wrote on 18 Jul 2018, 18:36 last edited by
          #10

          @VRonin I figured the serialization out, now I have to do something like out << *layer; to get it serialized. But how will that allow me to serialize a QList<Layer*> to be serialized as a whole?

          J 1 Reply Last reply 18 Jul 2018, 18:52
          0
          • T twodee
            18 Jul 2018, 18:36

            @VRonin I figured the serialization out, now I have to do something like out << *layer; to get it serialized. But how will that allow me to serialize a QList<Layer*> to be serialized as a whole?

            J Offline
            J Offline
            JonB
            wrote on 18 Jul 2018, 18:52 last edited by JonB
            #11

            @twodee
            Read through threadin this forum https://forum.qt.io/topic/58701/how-to-serialize-deserialize-a-qlist-myclass

            As per http://doc.qt.io/qt-5/datastreamformat.html, QList<T> can be de/serialized. You need to provide the de/serialization for your class T, which is what you have been working on here.

            1 Reply Last reply
            0
            • T Offline
              T Offline
              twodee
              wrote on 18 Jul 2018, 18:55 last edited by
              #12
              This post is deleted!
              1 Reply Last reply
              0
              • T Offline
                T Offline
                twodee
                wrote on 18 Jul 2018, 19:00 last edited by twodee
                #13

                As the post said it's about one line with the operator new, could you tell me what that line is?

                 QList<Layer*> layers = paintWidget->getItems(); 
                out << layers;
                

                This doesn't seem to do the trick. I have been scratching my head for too long, sorry if I am being stupid.

                Also for some reason overloading QDataStream& operator<<(QDataStream& ds, const Layer *layer) doesn't work anymore, which did work earlier.

                1 Reply Last reply
                0
                • V Offline
                  V Offline
                  VRonin
                  wrote on 19 Jul 2018, 07:09 last edited by VRonin
                  #14
                  out << qint32(layers.size());
                  for(Layer* layer : qAsConst(layers))
                  out << qint32(layer->type()) << *layer;
                  

                  qint32 size;
                  qint32 type;
                  in >> size;
                  while(size-- >0 ){
                  in >> type;
                  Layer* layer = nullptr;
                  switch(type){
                  case Raster:
                  layer = new RasterLayer;
                  break;
                  default:
                  Q_UNREACHABLE();
                  }
                  in >> *layer;
                  }

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  J T 2 Replies Last reply 19 Jul 2018, 07:22
                  0
                  • V VRonin
                    19 Jul 2018, 07:09
                    out << qint32(layers.size());
                    for(Layer* layer : qAsConst(layers))
                    out << qint32(layer->type()) << *layer;
                    

                    qint32 size;
                    qint32 type;
                    in >> size;
                    while(size-- >0 ){
                    in >> type;
                    Layer* layer = nullptr;
                    switch(type){
                    case Raster:
                    layer = new RasterLayer;
                    break;
                    default:
                    Q_UNREACHABLE();
                    }
                    in >> *layer;
                    }
                    J Offline
                    J Offline
                    JonB
                    wrote on 19 Jul 2018, 07:22 last edited by JonB
                    #15

                    @VRonin
                    I don't want to muddy the waters for the OP here, but may I ask: why do you explicitly serialize the layers list yourself in a loop? I thought that the point of the link I mentioned, http://doc.qt.io/qt-5/datastreamformat.html, is that it shows:

                    The QDataStream allows you to serialize some of the Qt data types. The table below lists the data types that QDataStream can serialize

                    QList<T>	
                        The number of items (quint32)
                        The items (T)
                    

                    so why can't you just out << layers ?

                    Is this because you want to know where layer->type() is in the serialization, so that you can look at it during deserialization and do your own newing? If there were no sub-classing going on in the list elements then you wouldn't need to do that and could just do the list directly? Could it then just be deserialized with in >> layers? Or will deserializing never do any newing of elements for you?

                    V 1 Reply Last reply 19 Jul 2018, 07:29
                    0
                    • J JonB
                      19 Jul 2018, 07:22

                      @VRonin
                      I don't want to muddy the waters for the OP here, but may I ask: why do you explicitly serialize the layers list yourself in a loop? I thought that the point of the link I mentioned, http://doc.qt.io/qt-5/datastreamformat.html, is that it shows:

                      The QDataStream allows you to serialize some of the Qt data types. The table below lists the data types that QDataStream can serialize

                      QList<T>	
                          The number of items (quint32)
                          The items (T)
                      

                      so why can't you just out << layers ?

                      Is this because you want to know where layer->type() is in the serialization, so that you can look at it during deserialization and do your own newing? If there were no sub-classing going on in the list elements then you wouldn't need to do that and could just do the list directly? Could it then just be deserialized with in >> layers? Or will deserializing never do any newing of elements for you?

                      V Offline
                      V Offline
                      VRonin
                      wrote on 19 Jul 2018, 07:29 last edited by
                      #16

                      @JonB That would imply having datastream operators acting on pointers and that's dangerous:

                      • What if you pass a null pointer
                      • What if you pass a dangling pointer
                      • Who own the memory allocated by the pointer? the operator?

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      J 1 Reply Last reply 19 Jul 2018, 07:35
                      0
                      • V VRonin
                        19 Jul 2018, 07:29

                        @JonB That would imply having datastream operators acting on pointers and that's dangerous:

                        • What if you pass a null pointer
                        • What if you pass a dangling pointer
                        • Who own the memory allocated by the pointer? the operator?
                        J Offline
                        J Offline
                        JonB
                        wrote on 19 Jul 2018, 07:35 last edited by
                        #17

                        @VRonin
                        So are you saying: "Yes, you can de/serialize QList<T> directly as per that link, but while that's fine for simple types it's not suitable for pointers"?

                        I may be confusing myself. In my C# we don't have "pointers" and we just de/serialize lists directly without a care. Deserializing does whatever newing is necessary behind the scenes.

                        J 1 Reply Last reply 19 Jul 2018, 07:41
                        0
                        • J JonB
                          19 Jul 2018, 07:35

                          @VRonin
                          So are you saying: "Yes, you can de/serialize QList<T> directly as per that link, but while that's fine for simple types it's not suitable for pointers"?

                          I may be confusing myself. In my C# we don't have "pointers" and we just de/serialize lists directly without a care. Deserializing does whatever newing is necessary behind the scenes.

                          J Offline
                          J Offline
                          J.Hilk
                          Moderators
                          wrote on 19 Jul 2018, 07:41 last edited by
                          #18

                          @JonB said in How do I properly serialize and deserialize a QList class in QT using QDatastream?:

                          I may be confusing myself. In my C# we don't have "pointers" and we just de/serialize lists directly without a care.

                          I beg to differ!

                          you can use pointers in c# and manage your memory by hand, but you have to explicitly tell the compiler to allow it with -unsafe, IIRC

                          Quick google search:
                          https://www.tutorialspoint.com/csharp/csharp_unsafe_codes.htm


                          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                          Q: What's that?
                          A: It's blue light.
                          Q: What does it do?
                          A: It turns blue.

                          J 1 Reply Last reply 19 Jul 2018, 07:45
                          0
                          • V Offline
                            V Offline
                            VRonin
                            wrote on 19 Jul 2018, 07:43 last edited by
                            #19

                            take this example:

                            // QList<int*> m_ownerList;
                            m_ownerList.append(new int(5));
                            m_ownerList.append(new int(3));
                            
                            m_myInt1 = new  int(5);
                            m_myInt2 = new  int(3);
                            m_nonOwnerList = QList<int*>{{m_myInt1 ,m_myInt2} };
                            

                            What should QDataStream& operator>>(QDataStream& , const QList<int*> ) do? free the memory already allocated or not?

                            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                            ~Napoleon Bonaparte

                            On a crusade to banish setIndexWidget() from the holy land of Qt

                            1 Reply Last reply
                            0
                            • J J.Hilk
                              19 Jul 2018, 07:41

                              @JonB said in How do I properly serialize and deserialize a QList class in QT using QDatastream?:

                              I may be confusing myself. In my C# we don't have "pointers" and we just de/serialize lists directly without a care.

                              I beg to differ!

                              you can use pointers in c# and manage your memory by hand, but you have to explicitly tell the compiler to allow it with -unsafe, IIRC

                              Quick google search:
                              https://www.tutorialspoint.com/csharp/csharp_unsafe_codes.htm

                              J Offline
                              J Offline
                              JonB
                              wrote on 19 Jul 2018, 07:45 last edited by
                              #20

                              @J.Hilk
                              But I would never want to use pointers in C# or manage memory by hand, that's (more than) half the point of using C#!?

                              Maybe there's a misunderstanding. I don't use C# with Qt (I use Python). I'm just familiar with C# compared to C++. I am trying to understand @VRonin's explanation of de/serializing this QList<T>, where he is saying he does it explicitly to manage pointers, when I know I would just de/serialize a list from C# without iterating the elements myself, and trying to understand why.

                              1 Reply Last reply
                              0
                              • V VRonin
                                19 Jul 2018, 07:09
                                out << qint32(layers.size());
                                for(Layer* layer : qAsConst(layers))
                                out << qint32(layer->type()) << *layer;
                                

                                qint32 size;
                                qint32 type;
                                in >> size;
                                while(size-- >0 ){
                                in >> type;
                                Layer* layer = nullptr;
                                switch(type){
                                case Raster:
                                layer = new RasterLayer;
                                break;
                                default:
                                Q_UNREACHABLE();
                                }
                                in >> *layer;
                                }
                                T Offline
                                T Offline
                                twodee
                                wrote on 19 Jul 2018, 08:01 last edited by
                                #21

                                @VRonin Hi, thanks for the extended example. I have come up with a working solutin that looks similar to yours, could you let me know if this is a good way to deal with it?

                                QDataStream& operator >>(QDataStream& stream, QList<Layer*>& layers){
                                    layers.clear();
                                    int size;
                                    int type;
                                
                                    stream>>size;
                                
                                    QString name;
                                    QPixmap pixmap;
                                
                                    Layer* layer = nullptr;
                                    for(int i =0; i<size; ++i){
                                
                                        stream >> name >> type;
                                        switch (type) {
                                        case Layer::RASTER:
                                            stream >> pixmap;
                                            layer = new RasterLayer(name, pixmap.toImage());
                                            break;
                                        default:
                                            Q_UNREACHABLE();
                                            break;
                                        }
                                        layer->read(stream);
                                
                                        layers.push_back(layer);
                                    }
                                    return stream;
                                }
                                
                                1 Reply Last reply
                                0
                                • V Offline
                                  V Offline
                                  VRonin
                                  wrote on 19 Jul 2018, 08:05 last edited by
                                  #22

                                  The very first line of your code is a memory leak. This is exactly my point. Don't handle memory inside serialising/deserialising it's not the right place

                                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                  ~Napoleon Bonaparte

                                  On a crusade to banish setIndexWidget() from the holy land of Qt

                                  1 Reply Last reply
                                  2
                                  • T Offline
                                    T Offline
                                    twodee
                                    wrote on 19 Jul 2018, 08:09 last edited by
                                    #23

                                    I felt wrong in doing so too as I mentioned in my original question, but I still am not sure where would I be creating the new RasterLayer in my case. Could you give me some clues on how to go about it?

                                    kshegunovK 1 Reply Last reply 19 Jul 2018, 08:20
                                    0
                                    • V Offline
                                      V Offline
                                      VRonin
                                      wrote on 19 Jul 2018, 08:12 last edited by
                                      #24

                                      Memory owned by an object should be created and freed by a method of said object:

                                      You can create a wrapper class around QList<Layer*> that will take care of allocating/freeing the memory inside and create safe datastream operator for this wrapper

                                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                      ~Napoleon Bonaparte

                                      On a crusade to banish setIndexWidget() from the holy land of Qt

                                      T 1 Reply Last reply 19 Jul 2018, 08:34
                                      2
                                      • T twodee
                                        19 Jul 2018, 08:09

                                        I felt wrong in doing so too as I mentioned in my original question, but I still am not sure where would I be creating the new RasterLayer in my case. Could you give me some clues on how to go about it?

                                        kshegunovK Offline
                                        kshegunovK Offline
                                        kshegunov
                                        Moderators
                                        wrote on 19 Jul 2018, 08:20 last edited by kshegunov
                                        #25

                                        @twodee said in How do I properly serialize and deserialize a QList class in QT using QDatastream?:

                                        I felt wrong in doing so too as I mentioned in my original question, but I still am not sure where would I be creating the new RasterLayer in my case. Could you give me some clues on how to go about it?

                                        Firstly, use the factory as you're using polymorphism, that'd be the way to go here. And secondly delegate the deserialization to the object through a virtual function; the whole reading. It's a good idea, since you're dealing with polymorphic objects, to also employ some kind of stack-based memory management here, to make sure you don't leak memory.
                                        You can see how I did it here and draw some inspiration:
                                        https://github.com/VSRonin/ChatExample/blob/commonlib/QtSimpleChat/chatmessage.h
                                        https://github.com/VSRonin/ChatExample/blob/commonlib/QtSimpleChat/chatmessage.cpp

                                        I am trying to understand @VRonin's explanation of de/serializing this QList<T>, where he is saying he does it explicitly to manage pointers, when I know I would just de/serialize a list from C# without iterating the elements myself, and trying to understand why.

                                        'Cause when you have allocations and deallocations in different parts of the code, i.e. not declaring the objects' lifetimes clearly you're begging for trouble. C++ is lower level (thank god) than C#, so it's up to the proficiency of the programmer to actually choose the best and most versatile implementation. It may seem elitist, but we don't want the language to be something to be worked around whenever there's something falling outside of the standard set of problems to solve. In Java/C# you start by assuming the programmer is irresponsible and can't handle the problem of finding a good way to manage the memory. This may prevent a lot of errors, but also hits hard on the efficiency ...

                                        Read and abide by the Qt Code of Conduct

                                        1 Reply Last reply
                                        3
                                        • V VRonin
                                          19 Jul 2018, 08:12

                                          Memory owned by an object should be created and freed by a method of said object:

                                          You can create a wrapper class around QList<Layer*> that will take care of allocating/freeing the memory inside and create safe datastream operator for this wrapper

                                          T Offline
                                          T Offline
                                          twodee
                                          wrote on 19 Jul 2018, 08:34 last edited by twodee
                                          #26

                                          @VRonin So, if I create a function for the wrapper around QList<Layer*> class (let's call it LayerList) that creates the correct layers depending upon the type (essentially acting as a factory), then I can go to the >> stream and instead of doing a new RasterLayer() I will call the LayerList::createLayer() method, in which case the memory is created and freed by the object owning the layers. Is my idea correct?

                                          kshegunovK 1 Reply Last reply 19 Jul 2018, 08:56
                                          0

                                          16/29

                                          19 Jul 2018, 07:29

                                          • Login

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