Dear Atul,
I see you are thinking of BinaryReader/BinaryWriter as kind of stream classes. When I created them my intention was NOT that they become another QDataStream. Let me summarize my previous experience with QDataStream and Packets and why BinaryReader/BinaryWriter crystallized as a good solution for me. I don’t insist that is the best solution and I’m quite open for improving it or changing the concept altogether.
Btw what I find extremely important but traditionally not captured in any why is the evolution path of the software. It has happened to me that I come across a problem, solve it, then after some time come back to the same place, see how it can be “improved”, switch back to the old solution just to figure out there was a problem, reminding me why I used the other solution… but enough figurative speech…
Main problem I had with QDataStream is that it uses its own binary format. It’s perfectly fine for the primitive data types as ints but not at all fine for e.g. QHostAddress, QByteArray (C strings and raw bytes). I found myself writing code that works around the limitations of QDataStream for writing the binary data of Gnutella packets. I expected to have similar problems with other protocols. I needed really low-level tools which I can use to easily read/write binary packets. I didn’t want to serialize objects right away. I wanted to have a tool the objects (packets and other communication data particularly) can use to easily serialize themselves. So I didn’t plan for e.g. writer.write (packet); because I thought that would better be done on a higher abstraction level (by e.g. a stream class).
BinaryReader/BinaryWriter is all we need for BitTorrent packets as they are so simple. For Gnutella packets we would need more complicated reader/writer. But it would be quite easy to derive a class form BinaryReader and add a function VendorCode readVendorCode(); or QHostAddress readIPv4Address(). Actually it is now that I realize why I have added suffixes to the read and write functions back then! You are not allowed to overload just by return type ;-). The writer class was pretty much copy-paste-rename-rework of the reader class, thus the suffixes were preserved. In my opinion it is better to keep the interfaces of BinaryReader and BinaryWriter alike. Normally if you read something you will also want to write it. You can more easily inspect the code if you see readStuff();, aha, writeStuff();.
The explicit keyword in C++ is related to implicit conversions involving constructors and that, in my opinion, is a quite specific example of the “explicit is better than implicit” principle. In my opinion this principle has a much broader meaning and it does apply to our case with low level communication protocols where a single bit matters.
There are also cases where you want to serialize the packet type as a single byte but you store it in an enum member. Doing writer.write (packetType); will write you 4 bytes although you’ll be thinking you are writing one byte. Doing writer.writeByte (packetType); will get you a compile error at first and you’ll need an explicit cast to uchar.
I already wrote quite much, so finally, templates do reduce the size of the code but I think we can live with a few more trivial functions ;-) If we need a stream like class that serializes objects than we’d better build upon BinaryReader and BinrayWriter is my opinion.
Let me know what you think.
Best regards,
Peter
