私はandを C++ のランダム アクセス バイナリ I/O のポリモーフィック インターフェイスとして使用std::istream
してきましたが、多くの点で次善のようです。ostream
- streampos/streamoff の制限により、64 ビット シークは移植性がなく、エラーが発生しやすくなります。現在はboost/iostreams/positioning.hppを回避策として使用していますが、注意が必要です
- ファイルの切り捨てや拡張などの操作の欠落 (ala POSIX
ftruncate
) - 具体的な実装間の矛盾; たとえば
stringstream
、独立した get/put ポジションがありますが、そうではありfilestream
ません - プラットフォームの実装間の不一致; たとえば、ファイルの最後をパスしようとする動作や、エラーでの
failbit
/の使用badbit
stream
のすべてのフォーマット機能や、場合によってはバッファリングさえも必要ありませんstreambuf
streambuf
エラー報告 (つまり、例外とエラー インジケーターを返すこと) は、実際には実装に依存していると考えられます。
Boost.Iostreams Device conceptによって提供される簡素化されたインターフェイスが気に入っていますが、これはポリモーフィック クラスではなく、関数テンプレートとして提供されています。( device
classはありますが、ポリモーフィックではなく、提供されたデバイス実装で必ずしも使用されるとは限らない単なる実装ヘルパー クラスです。) 私は主に大きなディスク ファイルを使用していますが、別の実装を簡単に置き換えることができるように、本当にポリモーフィズムが必要です (例:単体テスト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;
};