Я использовал std::istream и ostream в качестве полиморфного интерфейса для двоичного ввода-вывода с произвольным доступом в C++, но во многих отношениях он кажется неоптимальным:
- 64-битные поиски не переносимы и подвержены ошибкам из-за ограничений streampos/streamoff; в настоящее время используется boost/iostreams/positioning.hpp как обходной путь, но требует бдительности
- Отсутствующие операции, такие как усечение или расширение файла (аналог POSIX
ftruncate) - Несоответствие между конкретными реализациями; например
stringstreamимеет независимые позиции получения/пута, тогда какfilestreamне имеет - Несоответствие между реализациями платформы; например поведение при поиске конца файла или использование
failbit/badbitпри ошибках - Не нужны все средства форматирования
streamили, возможно, даже буферизацияstreambuf streambufотчеты об ошибках (т. е. исключения или возврат индикатора ошибки) предположительно зависит от реализации на практике
Мне нравится упрощенный интерфейс, предоставляемый Boost.Iostreams. Концепция устройства, но она представлена в виде шаблонов функций, а не полиморфного класса. (Существует device класс, но он не полиморфен и является просто вспомогательным классом реализации, который не обязательно используется в поставляемых реализациях устройств.) Я в основном использую большие файлы на диске, но мне действительно нужен полиморфизм, поэтому я могу легко заменить альтернативные реализации (например, использовать stringstream вместо fstream для модульные тесты) без всей сложности и связи времени компиляции глубокого создания экземпляра шаблона.
Есть ли у кого-нибудь рекомендации стандартного подхода к этому? Вроде обычная ситуация, так что не хочется изобретать свои интерфейсы без надобности. Например, что-то вроде java.nio.FileChannel кажется идеальным.
Мое лучшее решение на данный момент — поместить тонкий полиморфный слой поверх устройств Boost.Iostreams. Например:
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;
};