views:

95

answers:

2

I've been using std::istream and ostream as a polymorphic interface for random-access binary I/O in C++, but it seems suboptimal in numerous ways:

  • 64-bit seeks are non-portable and error-prone due to streampos/streamoff limitations; currently using boost/iostreams/positioning.hpp as a workaround, but it requires vigilance
  • Missing operations such as truncating or extending a file (ala POSIX ftruncate)
  • Inconsistency between concrete implementations; e.g. stringstream has independent get/put positions whereas filestream does not
  • Inconsistency between platform implementations; e.g. behavior of seeking pass the end of a file or usage of failbit/badbit on errors
  • Don't need all the formatting facilities of stream or possibly even the buffering of streambuf
  • streambuf error reporting (i.e. exceptions vs. returning an error indicator) is supposedly implementation-dependent in practice

I like the simplified interface provided by the Boost.Iostreams Device concept, but it's provided as function templates rather than a polymorphic class. (There is a device class, but it's not polymorphic and is just an implementation helper class not necessarily used by the supplied device implementations.) I'm primarily using large disk files, but I really want polymorphism so I can easily substitute alternate implementations (e.g. use stringstream instead of fstream for unit tests) without all the complexity and compile-time coupling of deep template instantiation.

Does anyone have any recommendations of a standard approach to this? It seems like a common situation, so I don't want to invent my own interfaces unnecessarily. As an example, something like java.nio.FileChannel seems ideal.

My best solution so far is to put a thin polymorphic layer on top of Boost.Iostreams devices. For example:

class my_istream
{
public:
    virtual std::streampos seek(stream_offset off, std::ios_base::seekdir way) = 0;
    virtual std::streamsize read(char* s, std::streamsize n) = 0;
    virtual void close() = 0;
};

template <class T>
class boost_istream : public my_istream
{
public:
    boost_istream(const T& device) : m_device(device)
    {
    }

    virtual std::streampos seek(stream_offset off, std::ios_base::seekdir way)
    {
        return boost::iostreams::seek(m_device, off, way);
    }

    virtual std::streamsize read(char* s, std::streamsize n)
    {
        return boost::iostreams::read(m_device, s, n);
    }

    virtual void close()
    {
        boost::iostreams::close(m_device);
    }

private:
    T m_device;
};
+1  A: 

Did you have a look an Qt's QIODevice class and subclasses? I'm not quite sure if it fits your needs, but maybe its worth a try: QIODevice.

Elrohir
Thanks for the pointer, I had forgotten about Qt. I don't want to take a dependency on it, but it does provide some perspective.
Trevor Robinson
A: 

I just ended up going with a set of abstract interfaces similar to what I outlined in the question. There don't seem to be any lightweight, polymorphic standards for this...

Trevor Robinson