[Qt-interest] How to deal with pointers for a QDataStream?
Oliver Heins
heins at sopos.org
Wed Apr 21 11:55:50 CEST 2010
Hi André,
thank you for your kind help again.
Andre Somers <andre at familiesomers.nl> writes:
> On 20-4-2010 22:24, Oliver Heins wrote:
>
> Remember, there is nothing magical about QDataStream. It is reading
> and writing some binairy data, but it does not know anything about
> it's format that you did not tell it explicitly. Any byte in the
> stream is taking to mean exactly what you tell QDataStream it
> means. Also, the reading code knows nothing about the writing
> code. That means that you need to read it back exactly the way you
> wrote it out. So, you need to be very careful to keep your reading and
> your writing code symetrical. Also, you need to know what you expect
> in the datafile, and how much of it. That means for instance, that for
> every list you write out, you need to write out how many elements are
> in that list, and then be sure that you actually read that many
> elements. That is exactly what Qt is doing. So, it requires
> precision. It awards you with fast write-out and readback, and small
> files.
>
>>> Is token.unique actually a quint32? You stream it out as one, but I
>>> can not see if is streamed in properly based on this code.
>>>
>> No, it is an ordinary int, and it seems to cause problems. (I thought
>> there would be an implicit cast from quint32 to int, but I seem to be
>> wrong again.)
>>
> As I said above: you need to read back exactly what you wrote out. If
> you write out a quint32, you need to read back the same data type. The
> cast you as for does happen, but int is not casted to quint32, but (I
> guess) to qint32. Note the missing 'u' there. Having said that: why
> isn't token.unique a quint32 in the first place? Are you using
> negative identifiers?
I made it one, so no casting is needed for it anymore. However, the
problem still remains.
,----
| class Token
| {
| public:
| enum Type { Space, Paragraph, Kern, Char, Format, Note, End };
| enum SubType { Null = 0,
| Bold, Italic,
| Header1, Header2, Header3, Header4, Header5, Header6,
| Itemize, Enumerate, Description, Item,
| Note1, Note2, Note3, Note4, Note5 };
|
| Token() { this->unique = ++lastUnique;
| this->chr = (QChar)0;
| this->subType = Null;
| this->partner = 0;
| this->parent = 0; }
| // [...] (other constructors and destructor)
|
| Type type;
| SubType subType;
| QChar chr;
| quint32 unique;
| Token *parent;
| QLinkedList<Token *> children;
| Token *partner;
| static quint32 lastUnique;
| };
`----
> Also, I find that it is a good idea to use Qt's explicit data types
> when you work with binairy files. The size of an int is not constant
> between systems, for instance. What do you expect to happen when you
> read in an int on a 32 bits machine from a file that was created on a
> 64 bits machine? If you are lucky, they agree on the size of int. If
> you are unlucky, then you are reading back only half of what was
> written out, and the next thing you read ends up being trash.
That's why I decided to cast to quint32. But you're right, I should
make it an quint32 in the class itself.
>> Unfortunately, I can't get it work, even if I do an
>> explicit cast. Here's what class Token now looks like and how the
>> operators are defined:
>>
> On first inspection, I don't see any obvious mistakes. Best is to do a
> bit of trying out. Write out your file, and open it with a binairy
> editor so you can inspect the data inside. There is a very nice on in
> KDE that can handle Qt data types, if you need it. You can then
> inspect if the data actually looks like what you expect it to
> be. Perhaps start with writing out and reading back a single Token
> object.
You're talking about Okteta? I installed it, but I can't figure out how
it can handle Qt data types. However, from what I can tell, everything
looks fine to me. This is the binary written out by
,----
| QDataStream out(&file);
| out.setVersion(QDataStream::Qt_4_6);
|
| out << quint32(MagicNumber);
| QLinkedListIterator<Token *> i(parser->tokenlist);
| quint32 max = 0;
| quint32 count = 0;
| while (i.hasNext()) {
| max = qMax(max, i.next()->unique);
| count++;
| }
| out << max << count;
| i.toFront();
| while (i.hasNext())
| out << *(i.next());
`----
with
,----
| QDataStream &operator>>(QDataStream &in, Token &token)
| {
| quint32 partner, parent;
| qint16 type, subType;
|
| token.parent = 0;
| token.partner = 0;
| in >> type >> subType >> token.unique >> parent >> partner >> token.chr;
| token.type = static_cast<Token::Type>(type);
| token.subType = static_cast<Token::SubType>(subType);
|
| return in;
| }
`----
Magic
Number max nr count
8C2B0CA6 00000022 00000022
Type/
Subtype uniq id parent partner chr
00030000 00000001 00000000 00000000 0048 Char: H
00030000 00000002 00000000 00000000 0061 Char: a
00030000 00000003 00000000 00000000 006C Char: l
00030000 00000004 00000000 00000000 006C Char: l
00030000 00000005 00000000 00000000 006F Char: o
00030000 00000006 00000000 00000000 002C Char: ,
00000000 00000007 00000000 00000000 0000 Space
00040001 00000008 00000000 00000016 0000 Format:Bold, Partner: 0x16
00030000 00000009 00000000 00000000 0057 Char: W
00030000 0000000A 00000000 00000000 0065 Char: e
00030000 0000000B 00000000 00000000 006C Char: l
00030000 0000000C 00000000 00000000 0074 Char: t
00030000 0000000D 00000000 00000000 0021 Char: !
00000000 0000000E 00000000 00000000 0000 Space
00040002 0000000F 00000000 0000001C 0000 Format:Italic, Partner: 0x1c
00030000 00000010 00000000 00000000 0049 Char: I
00030000 00000011 00000000 00000000 0074 Char: t
00030000 00000012 00000000 00000000 0061 Char: a
00030000 00000013 00000000 00000000 006C Char: l
00030000 00000014 00000000 00000000 0069 Char: i
00030000 00000015 00000000 00000000 0063 Char: c
00060000 00000016 00000000 00000008 0000 End, Partner: 0x08
00000000 00000017 00000000 00000000 0000 Space
00030000 00000018 00000000 00000000 0067 Char: g
00030000 00000019 00000000 00000000 0065 Char: e
00030000 0000001A 00000000 00000000 0068 Char: h
00030000 0000001B 00000000 00000000 0074 Char: t
00060000 0000001C 00000000 0000000F 0000 End, Partner: 0x0f
00000000 0000001D 00000000 00000000 0000 Space
00030000 0000001E 00000000 00000000 0061 Char: a
00030000 0000001F 00000000 00000000 0075 Char: u
00030000 00000020 00000000 00000000 0063 Char: c
00030000 00000021 00000000 00000000 0068 Char: h
00030000 00000022 00000000 00000000 0021 Char: !
The code I use to read is:
,----
| QDataStream in(&file);
| in.setVersion(QDataStream::Qt_4_6);
|
| quint32 magic, max, count;
| in >> magic >> max >> count;
| if (magic != MagicNumber) {
| QApplication::restoreOverrideCursor();
| QMessageBox::warning(this, tr("Critical Text Editor"),
| tr("File %1 is not recognized.")
| .arg(file.fileName()));
| return false;
| }
|
| for (quint32 i = 0; i < count; ++i) {
| Token token;
| in >> token;
| tokenVec.append(token);
| }
`----
with
,----
| QDataStream &operator>>(QDataStream &in, Token &token)
| {
| quint32 partner, parent;
| qint16 type, subType;
|
| token.parent = 0;
| token.partner = 0;
| in >> type >> subType >> token.unique >> parent >> partner >> token.chr;
| token.type = static_cast<Token::Type>(type);
| token.subType = static_cast<Token::SubType>(subType);
|
| return in;
| }
`----
What happens, is that the first 12 elements are read properly to token,
but from the 13th on, I get trash. Debugging tells me, that then
token.unique is set to 0 and token.chr gets a value of (ushort)8448.
>From that on, only garbage is read, and everything, including
token.type. So it looks like the data comes out of sync.
Best regards,
olli
More information about the Qt-interest-old
mailing list