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 places QIODevice does not use the virtual method pos() internally and as a result my override of pos() that allows for the header is being ignored. For example in QIODevice::readAll() which is not virtual so I cannot properly override.

    Why is QIODevice not using the pos() virtual method internally, it seems to use size() and seek() 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 from QIODevice 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 that QIODevice breaks its own contract by not using pos() when it should.

    BTW if QIODevice did use pos() there is no issue with a super-class that needs readAll() to read all, it would simply not override pos(), 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 using d->pos where it could and most definitely should be using pos(). If it did, thereby honoring the contract it promises by allowing pos() 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 that readAll() should read everything is a straw man, what readAll() 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 how pos() is used in other QIODevice 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 from QFile seems so appropriate but given the impossibility of making it work like that I have aggregated a QFile object in my class that derives from QIODevice. It is working fine but I had to write may forwarding functions since my class needs all of the QFile API, but it is done now.

    Thanks for your comments and suggestions.

    Happy New Year!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.