バイナリファイルをifstreamで操作しようとしたら不満が出たので。
- readの返り値は自身のオブジェクト(読み込んだbyte数じゃない)
- ios::binaryの時のoperator >>(...)の挙動がヤダ
なので、オレオレBinaryFileReaderを作りました。自己満足です。
ifstreamの良さが分かる日はくるのだろか。
#include <cstdio> class BinaryFileReader{ BinaryFileReader(); BinaryFileReader(const BinaryFileReader &); BinaryFileReader & operator =(const BinaryFileReader &); FILE *fp; template<typename T> BinaryFileReader & operator>>(T * o); // ポインタを渡すのを許さない(追記) public: enum SEEK_TYPE{ Beg = SEEK_SET, Cur = SEEK_CUR, End = SEEK_END }; BinaryFileReader(const char *filename) : fp(fopen(filename, "rb")){ } ~BinaryFileReader(){ fclose(fp); }; inline size_t read(void *p, size_t datasize, size_t datanum){ return fread(p, datasize, datanum, fp); } inline size_t read(void *p, size_t readlength){ return read(p, 1, readlength); } template<typename T> BinaryFileReader & operator >>(T &o){ read(&o, sizeof(T)); return *this; } inline bool eof() const { return feof(fp); } inline int seek(int length, SEEK_TYPE origin) { return fseek(fp, length, origin); } inline int seek(int length) { return seek(length, Cur); } };
こんな使い方をします。
char magic[4]; int i; short s; BinaryFileReader bfr("hoge.bin"); bfr.read(magic, 4); // 先頭から4byte取得 bfr >> i; // 4byte目から4byte(==sizeof(int))取得 bfr >> s; // 8byte目から2byte(==sizeof(short))取得 bfr.seek(0, BinaryFileReader::Beg); // fpを先頭に移動 bfr >> i; // 先頭から4byte取得
Template便利だなー。
operator >>()へポインタを渡せてしまうので、その時は32bit環境なら4byte読み込むでしょう。
なので、char a[2]; bfr >> a;とかしちゃうと、バッファオーバーフローになります。
これを制限する方法が思いつきませんでした。知識不足でダメダメですね。
追記
手持ちの本を調べたら、privateにtemplateでポインタ型を渡されたときの定義だけを書いて、実装はしない事で回避できます。
よくわからないので穴があるかもしれませんが、バッファオーバーフローが起こりうるコードを晒しておく現状よりマシです。
test.cpp: In constructor 'HLDemoParser::HLDemoParser(const char*)':
test.cpp:10:42: error: 'BinaryFileReader& BinaryFileReader::operator>>(T*) [with T = char, BinaryFileReader = BinaryFileReader]' is private
test.cpp:55:11: error: within this context
このエラー文だと、55行目でそのテンプレートを使おうとしているのでエラーになります。
該当箇所もわかって良いですね:)
また、ポインタのポインタでも同様に該当します。