Solved Deriving from QIODevice
-
Happy New Year!
I would like to sub-class
QFile
to model a file that contains a hidden header. For this I wish all public read and write operations to be offset by the length of the hidden header. This seems ok except that in a couple of placesQIODevice
does not use the virtual methodpos()
internally and as a result my override ofpos()
that allows for the header is being ignored. For example inQIODevice::readAll()
which is not virtual so I cannot properly override.Why is
QIODevice
not using thepos()
virtual method internally, it seems to usesize()
andseek()
as I would expect? -
Hi,
Unless you really need to pass your class around as a QIODevice, an alternative would be to wrap a QFile inside your class instead of inheritance. QIODevice::readAll does not use pos for the simple reason that when someone calls read all, they expect everything to be read.
-
@t3685 Thanks for your comments.
Passing the derived type as
QIODevice *
is absolutely necessary.Having
readAll()
return the hidden header is breaking the contract that the header is hidden! For example the non-hidden part is valid standing alone, the hidden header is metadata that is not necessary for a consumer or producer for the visible content. The metadata does explain the format of the data, but a client that knows or assumes the format does not need it. Regardless of that the metadata is exposed via other methods of the class.I have thought about delegating to
QFile
and having the public interface also derive fromQIODevice
but apart from having to forward many methods to the delegate I still see problems. I'll give it a try but it is a shame thatQIODevice
breaks its own contract by not usingpos()
when it should.BTW if
QIODevice
did usepos()
there is no issue with a super-class that needsreadAll()
to read all, it would simply not overridepos()
,seek()
,size()
etc.. Surely this is the point of virtual base classes? Why are these methods virtual with default implementations if not for super-classes to "map" the actual position in the underlying media? -
Hi,
I don't know the requirements you have to fulfill, but I get the impression you are misunderstanding the design of QIODevice derived classes. These classes are meant to abstract the interaction with an IO device itself and do NOT represent any file format specific knowledge. Examples of io device derived classes are QTcpSocket or QSerialPort. These classes basically allow you to read/write data, any data to the device. Only member functions that are involved in this task are made virtual. For example pos() needs to be implemented differently for a socket and for a file, but read all should do the same.
What you seem to be after is implementing a specific file type with a hidden header (note that none of the official derived classes even have notion of a header).
So I think you are asking the wrong questions as to why some functions are virtual or and why some or not. Instead you should be asking yourself why am I making a new io device when I really need a file type implementation.
-
@t3685 said:
So I think you are asking the wrong questions as to why some functions are virtual or and why some or not.
No, I am specifically asking why
QIODevice::readAll()
a non-virtual member is usingd->pos
where it could and most definitely should be usingpos()
. If it did, thereby honoring the contract it promises by allowingpos()
to be overridden, it would work just fine for me and everyone else.What I am trying to do is not really relevant, I am just asking why the implementation of
QIODevice::readAll()
is the way it is when a different, and IMHO, a correct implementation would use'pos()
. Your claim thatreadAll()
should read everything is a straw man, whatreadAll()
delivers should surely be determined by the sub-classes. -
@t3685 said:
Examples of io device derived classes are QTcpSocket or QSerialPort. These classes basically allow you to read/write data, any data to the device. Only member functions that are involved in this task are made virtual. For example pos() needs to be implemented differently for a socket and for a file, but read all should do the same.
The difference between a socket and a file is modeled by
QIODevice::isSequential()
, that itself determines howpos()
is used in otherQIODevice
methods. -
readAll() violates no contracts as it does exactly what it promises, that is reading all data from a device. How it does that is an implementation detail.
If you are 100% sure that you need to model your problem as a QIODevice and need readAll to behave differently you can always file a bug report.
However I strongly recommend you look at other QIODevice implementations before doing so. Tcp packets and serial data might also have protocol information that does not need to shown to users. If that's the case you can see how they solved that problem. -
@t3685 said:
However I strongly recommend you look at other QIODevice implementations before doing so. Tcp packets and serial data might also have protocol information that does not need to shown to users. If that's the case you can see how they solved that problem.
I have written several
QIODevice
derivations and it is not a complex process. This is the first time that deriving fromQFile
seems so appropriate but given the impossibility of making it work like that I have aggregated aQFile
object in my class that derives fromQIODevice
. It is working fine but I had to write may forwarding functions since my class needs all of theQFile
API, but it is done now.Thanks for your comments and suggestions.
Happy New Year!