PDA

View Full Version : Serialization custom type



ado130
3rd February 2017, 17:08
Hello,
I'm trying write to binary file my custom type, so I have:


typedef enum t_timeloopsStatus
{
NOTHING = 0,
UPDATED,
} t_timeloopsStatus;

typedef struct t_timeloops
{
QString key;
t_timeloopsStatus status;
} t_timeloops;

QMap<QString, t_timeloops> _timeLoops;
I know, that I have to overload << and >> operators

QDataStream &operator<<(QDataStream &out, const QMap<QString, t_timeloops> &timeloops);
QDataStream &operator>>(QDataStream &in, QMap<QString, t_timeloops> &timeloops);
but I have no idea how will looks function for that.

I found an original function for QMap and tried to rewrite it, but without success.

QDataStream &operator<<(QDataStream &out, const QMap<QString, t_timeloops> &timeloops)
{
out << quint32(timeloops.size());
typename QMap<QString, t_timeloops>::ConstIterator it = timeloops.end();
typename QMap<QString, t_timeloops>::ConstIterator begin = timeloops.begin();
while (it != begin) {
--it;
out << it.key() << it.value();
}
return out;
}

QDataStream &operator>>(QDataStream &in, QMap<QString, t_timeloops> &timeloops)
{
QDataStream::Status oldStatus = in.status();
in.resetStatus();
timeloops.clear();

quint32 n;
in >> n;

timeloops.detach();
for (quint32 i = 0; i < n; ++i) {
if (in.status() != QDataStream::Ok)
break;

QString key;
t_timeloops value;
in >> key >> value.key >> value.pad >> value.cylinder >> value.status;
timeloops.insertMulti(key, value);
}
if (in.status() != QDataStream::Ok)
timeloops.clear();
if (oldStatus != QDataStream::Ok)
in.setStatus(oldStatus);
return in;
}

Thanks for any help!

d_stranz
3rd February 2017, 23:01
The reason this doesn't work is because you are not writing the same thing you are trying to read in. For each map entry, you write out two things, namely the QMap's key and value, but then on input you try to read 5 things for each entry and only the first one (key) corresponds to something you have written out. I have no idea where you think value.pad and value.cylinder are going to come from - they aren't even members of your struct.

You are probably going to have to write operator >> and << for your t_timeloops struct unless Qt is able to create one on its own. You probably don't need to write these operators for QMap<>; these already exist in Qt and do the right thing. However, the QMap operators need to know how to write out the values in the map, and for that you need to supply the operators for t_timeloops.

ado130
4th February 2017, 10:47
Thanks for reply. Yeah, sorry the structure is not whole, just a part of it, there's missing a few QString and one enum.
So as you said, I need operators just for t_timeloops? Can you show me any pseudocode?

d_stranz
4th February 2017, 20:17
It is no different in principle from what you wrote for QMap; you need to replace the calling arguments with "& t_timeloops", and then replace the guts of the routine with code to write or read each element in your struct. Operators already exist for QString, so you don't need to reimplement those, and you can static_cast your enums to int or vice versa on write or read, respectively.

Once you have that, then you can simply serialize your QMap using the built-in QMap operators for QDataStream:


Related Non-Members

QDataStream &operator<<(QDataStream &out, const QMap<Key, T> &map)

Writes the map map to stream out.
This function requires the key and value types to implement operator<<().
See also Format of the QDataStream operators.

QDataStream &operator>>(QDataStream &in, QMap<Key, T> &map)

Reads a map from stream in into map.
This function requires the key and value types to implement operator>>().
See also Format of the QDataStream operators.

ado130
5th February 2017, 16:41
I had to slightly change the structure:

typedef enum t_timeloopsStatus
{
NOTHING = 0,
UPDATED,
UPLOADED,
CHANGED
} t_timeloopsStatus;

typedef enum t_timeloopsTime
{
KEY = 0,
PAD
} t_timeloopsTime;

typedef struct t_keyParam
{
QString keyTime;
QString padTime;
QString cylinderTime;
t_timeloopsStatus status;
t_timeloopsTime select;
} t_keyParam;

typedef struct t_timeloops
{
QMap<QString, t_keyParam> key;
} t_timeloops;

QMap<QString, t_timeloops> _timeLoops;


Or maybe, do you have some idea how to save data? My situation is:


Pad#1 - Key#1- key (qstring)
- pad (qstring)
- selected (enum)
...
- Key#2- key (qstring)
- pad (qstring)
- selected (enum)
...
...
Pad#2 - Key#2- key (qstring)
- pad (qstring)
- selected (enum)
...
- Key#3- key (qstring)
- pad (qstring)
- selected (enum)
...
...
...


And back to the main probolem. Probably it's not good solution at all, but:


QDataStream &operator<<(QDataStream &out, const t_keyParam &keyParam);
QDataStream &operator<<(QDataStream &out, const t_timeloops &timeLoops);
QDataStream &operator>>(QDataStream &in, const t_keyParam &keyParam);
QDataStream &operator>>(QDataStream &in, const t_timeloops &timeLoops);

QDataStream &operator<<(QDataStream &out, const t_keyParam &keyParam)
{
out << static_cast<quint8>(keyParam.select) << static_cast<quint8>(keyParam.status);
return out;
}

QDataStream &operator<<(QDataStream &out, const t_timeloops &timeLoops)
{
out << timeLoops.key;
return out;
}


QDataStream &operator>>(QDataStream &in, const t_keyParam &keyParam)
{
in >> static_cast<quint8>(keyParam.select) >> static_cast<quint8>(keyParam.status);
return in;
}


QDataStream &operator>>(QDataStream &in, const t_timeloops &timeLoops)
{
in >> timeLoops.key;
return in;
}


So I'm trying write a function just for my typedef and enum.

d_stranz
5th February 2017, 18:09
Or maybe, do you have some idea how to save data? My situation is:

I have no idea what you are trying to accomplish. Maybe if you described (in words) the user problem you are trying to solve with "pads", "keys" and so forth, it will make more sense.

If you have a custom class or struct that you wish to serialize using the QDataStream operators, your operator methods have to serialize -all- of the members of that class or struct if you want to be able read in and restore exactly what you have written. You don't just serialize one member unless you can somehow create the values of the other members from that one member's value.

I don't understand why you are defining a t_timeloops struct that contains another QMap as a member. If that's all it is, typedef it:



typedef QMap< QString, t_keyParam > t_timeloops;


You still end up with a QMap< QString, QMap< QString, t_keyParam > > as your top-level data container without the extra class as a nuisance, if that is indeed what you want.

You do not need to write any other QDataStream operators -except- the pair for t_keyParam, and you need to read / write -all- the members of that struct. The QMap operators will take care of the jigher levels for you.

ado130
6th February 2017, 15:05
Ok thanks, so this could be a final struct


typedef enum t_timeloopsStatus
{
NOTHING = 0,
UPDATED,
UPLOADED,
CHANGED
} t_timeloopsStatus;

typedef enum t_timeloopsTime
{
KEY = 0,
PAD
} t_timeloopsTime;

typedef struct t_timeloops
{
QString keyTime;
QString padTime;
QString cylinderTime;
t_timeloopsStatus status;
t_timeloopsTime select;
} t_timeloops;

QMap<QString, QMap<QString, t_timeloops>> _timeLoops;


QDataStream &operator<<(QDataStream &out, const t_timeloops &timeLoops)
{
out << static_cast<QString>(timeLoops.keyTime) << static_cast<QString>(timeLoops.padTime) << static_cast<QString>(timeLoops.cylinderTime) << static_cast<quint16>(timeLoops.select) << static_cast<quint16>(timeLoops.status);
return out;
}


QDataStream &operator>>(QDataStream &in, const t_timeloops &timeLoops)
{
in >> static_cast<QString>(timeLoops.keyTime) >> static_cast<QString>(timeLoops.padTime) >> static_cast<QString>(timeLoops.cylinderTime) >> (quint16&)timeLoops.select >> (quint16&)timeLoops.status;
return in;
}

It seems that the writing works, but still problem with reading and enums.

Thanks again!, it's really helpful.

d_stranz
6th February 2017, 21:27
The static_cast< QString > is completely unnecessary. QDataStream knows how to serialize QString types, just write out << timeloops.keyTime, etc.

Enums are no different from int or unsigned int as far as serialization goes. Your methods should look like this (note the removal of "const" from operator>> - you can't write to a const variable):



QDataStream & operator<<(QDataStream & out, const t_timeloops & timeLoops)
{
out << timeLoops.keyTime << timeLoops.padTime << timeLoops.cylinderTime << static_cast<quint16>(timeLoops.select) << static_cast<quint16>(timeLoops.status);
return out;
}

QDataStream & operator>>(QDataStream & in, t_timeloops & timeLoops)
{
in >> timeLoops.keyTime >> timeLoops.padTime >> timeLoops.cylinderTime;
quint16 inVal;
in >> inVal;
timeLoops.select = static_cast< t_timeloopsTime >( inVal );
in >> inVal;
timeLoops.status = static_cast< t_timeloopsStatus >( inVal );
return in;
}

ado130
7th February 2017, 17:44
Thanks a lot d_tranz, you're great, it was really helpful.