Is it possible to use a QFile like a std::iostream? I'm quite sure there must be a wrapper out there. The question is where?
I have another libs, which requires a std::istream as input parameter, but in my program i only have a QFile at this point.
Here's a good guide for subclassing std::streambuf to provide a non-seekable read-only std::istream: https://stackoverflow.com/a/14086442/316578
Here is a simple class based on that approach which adapts a QFile into an std::streambuf which can then be wrapped in an std::istream.
#include <iostream>
#include <QFile>
constexpr qint64 ERROR = -1;
constexpr qint64 BUFFER_SIZE = 1024;
class QFileInputStreamBuffer final : public std::streambuf {
private:
    QFile &m_file;
    QByteArray m_buffer;
public:
    explicit QFileInputStreamBuffer(QFile &file)
        : m_file(file),
          m_buffer(BUFFER_SIZE, Qt::Uninitialized) {
    }
    virtual int underflow() override {
        if (atEndOfBuffer()) {
            // try to get more data
            const qint64 bytesReadIntoBuffer = m_file.read(m_buffer.data(), BUFFER_SIZE);
            if (bytesReadIntoBuffer != ERROR) {
                setg(m_buffer.data(), m_buffer.data(), m_buffer.data() + bytesReadIntoBuffer);
            }
        }
        if (atEndOfBuffer()) {
            // no more data available
            return std::char_traits<char>::eof();
        }
        else {
            return std::char_traits<char>::to_int_type(*gptr());
        }
    }
private:
    bool atEndOfBuffer() const {
        return gptr() == egptr();
    }
};
If you want to be able to more things like seek, write, etc., then you'd need one of the other more complex solutions here which override more streambuf functions.
I came up with my own solution using the following code:
#include <ios>
#include <QIODevice>
class QStdStreamBuf : public std::streambuf
{
public:
    QStdStreamBuf(QIODevice *dev) : std::streambuf(), m_dev(dev)
    {
        // Initialize get pointer.  This should be zero so that underflow is called upon first read.
        this->setg(0, 0, 0);
    }
protected:
virtual std::streamsize xsgetn(std::streambuf::char_type *str, std::streamsize n)
{
    return m_dev->read(str, n);
}
virtual std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
    return m_dev->write(str, n);
}
virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*__mode*/)
{
    switch(dir)
    {
        case std::ios_base::beg:
            break;
        case std::ios_base::end:
            off = m_dev->size() - off;
            break;
        case std::ios_base::cur:
            off = m_dev->pos() + off;
            break;
    }
    if(m_dev->seek(off))
        return m_dev->pos();
    else
        return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type off, std::ios_base::openmode /*__mode*/)
{
    if(m_dev->seek(off))
        return m_dev->pos();
    else
        return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::int_type underflow()
{ 
    // Read enough bytes to fill the buffer.
    std::streamsize len = sgetn(m_inbuf, sizeof(m_inbuf)/sizeof(m_inbuf[0]));
    // Since the input buffer content is now valid (or is new)
    // the get pointer should be initialized (or reset).
    setg(m_inbuf, m_inbuf, m_inbuf + len);
    // If nothing was read, then the end is here.
    if(len == 0)
        return traits_type::eof();
    // Return the first character.
    return traits_type::not_eof(m_inbuf[0]);
}
private:
    static const std::streamsize BUFFER_SIZE = 1024;
    std::streambuf::char_type m_inbuf[BUFFER_SIZE];
    QIODevice *m_dev;
};
class QStdIStream : public std::istream
{
public:
    QStdIStream(QIODevice *dev) : std::istream(m_buf = new QStdStreamBuf(dev)) {}
    virtual ~QStdIStream()
    {
        rdbuf(0);
        delete m_buf;
    }
private:
    QStdStreamBuf * m_buf;
};
I works fine for reading local files. I haven't tested it for writing files. This code is surely not perfect but it works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With