Solved How do I properly serialize and deserialize a QList class in QT using QDatastream?
-
@JonB Yes, I did use my
layer
variable, I did comment out theraster
part and that didn't seem to work. My findings look a bit odd to me as well, thanks for trying. :D -
The normal way to do it:
- create 2 pure virtual protected members in
Layer
that takes aQDataStream&
argument and saves/loads the layer from it - create
QDataStream
stream operators forLayer
(notLayer*
) 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 aspaintWidget
owns the allocated memory - create 2 pure virtual protected members in
-
@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 methodinline void setName(QString &name) { _name = name; }
, what should I be doing in this case? -
Looks like you are deserialising on a const method. What is the code around that call?
-
This post is deleted! -
@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 aQList<Layer*>
to be serialized as a whole? -
@twodee
Read through threadin this forum https://forum.qt.io/topic/58701/how-to-serialize-deserialize-a-qlist-myclassAs per http://doc.qt.io/qt-5/datastreamformat.html,
QList<T>
can be de/serialized. You need to provide the de/serialization for your classT
, which is what you have been working on here. -
This post is deleted! -
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. -
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; }
-
@VRonin
I don't want to muddy the waters for the OP here, but may I ask: why do you explicitly serialize thelayers
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 ownnew
ing? 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 within >> layers
? Or will deserializing never do anynew
ing of elements for you? -
@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?
-
@VRonin
So are you saying: "Yes, you can de/serializeQList<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
new
ing is necessary behind the scenes. -
@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
, IIRCQuick google search:
https://www.tutorialspoint.com/csharp/csharp_unsafe_codes.htm -
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? -
@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. -
@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; }
-
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
-
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? -
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