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 whereasfilestream
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 ofstreambuf
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;
};