一般には、新しい streambuf クラスの派生は使用しませんが、特別な動作が必要な場合には非常に有用な機能です。たとえば、ログファイルが無限に増殖するのを防ぐために、一定のサイズになると先頭から書き込みを行うログブックストリームを例に挙げます。この新しいクラスを実装するには、まず、新しいストリームバッファ型を派生させる必要があります。一番簡単な方法は、filebuf から派生させ、限定公開の仮想関数のいずれかを実装する方法です。
template <class charT, class traits> class logbuffer : public std::basic_filebuf<charT,traits> { std::streamsize max_size; public: typedef charT char_type; // 1 typedef traits::int_type int_type; typedef traits::off_type off_type; typedef traits::pos_type pos_type; typedef traits traits_type; logbuffer(std::streamsize sz) : max_size(sz) // 2 { ; } protected: int_type overflow(int_type c = traits::eof()); // 3 };
//1 | すべてのストリームに 3 種類の型を定義します。 |
//2 | このコンストラクタには、ログファイルのサイズを限定するためのサイズパラメータが必要です。 |
//3 | put 領域が一杯になって、ストリームでストリームバッファに対する書き込みを行うたびに、限定公開の仮想関数 overflow(char_type) が呼び出されます。ファイルが大きくなりすぎた場合は、この関数を再実装してファイルポインタを先頭にリセットします。 |
overflow 関数は、次のように実装します。
template <class charT, class traits> logbuffer<charT,traits>::int_type logbuffer<charT,traits>::overflow(logbuffer<charT,traits>::int_type c) { using std::ios_base; using std::basic_filebuf; std::streamsize len = epptr() - pbase(); // 1 int_type ret = basic_filebuf<charT,traits>::overflow(c); // 2 if (seekoff(0,ios_base::cur) > max_size - len) // 3 seekoff(0,ios_base::beg); // 4 return ret; // 5 }
//1 | まず、バッファの put 領域のサイズを局所変数に保存します。epptr() と pbase() は、basic_streambuf の限定公開の関数であり、それぞれ put 領域の先頭と末尾を返します。他の限定公開の関数は、 get 領域の先頭と末尾を返し、さらに現在の get と put の位置を返します。これらの関数の詳細については、『標準 C++ クラスライブラリ・リファレンス』を参照してください。 |
//2 | 次に、filebuf' の ovrflow を呼び出してバッファの put 領域をファイルに書き込みます。 |
//3 | seekoff で現在のストリーム位置を確認し、その位置と最大ファイルサイズが put バッファよりも小さいかどうかを調べます。真の場合は、put 領域の次のフラッシュが、このファイルに設定した最大サイズを超えることになります。 |
//4 | ログが大きくなりすぎた場合は、stream(file) の先頭にシークで戻ります。ストリームに対する今後の書き込みは、ログファイルの先頭から上書きされます。 |
//5 | 最後は、filebuf'の overflow 関数から取り出した値を返します。 |
新しい logbuf クラスを使用するには、新しい logstream クラスも必要です。
template <class charT, class traits> class logstream : public std::basic_iostream<charT,traits> { logbuffer<charT,traits> buf; // 1 public: typedef charT char_type; // 2 typedef traits::int_type int_type; typedef traits::off_type off_type; typedef traits::pos_type pos_type; typedef traits traits_type; logstream(std::streamsize sz, char* file); // 3 logbuffer<charT,traits> *rdbuf() const // 4 { return (logbuffer<charT,traits>*)&buf; } };
//1 | この非公開メンバーでは、logbuffer オブジェクトが得られます。 |
//2 | 再び、標準の型セットを定義します。 |
//3 | このコンストラクタでは、指定した最大サイズとファイル名のストリームが作成されます。 |
//4 | rdbuf は logbuffer までのポインタを返します。 |
最後は、次のように新しいログストリームクラスを実装します。
template <class charT, class traits> logstream<charT,traits>::logstream(std::streamsize sz, char* file) : buf(sz) { using std::ios_base; init(&buf); // 1 if ( !buf.open(file, ios_base::out) ) // 2 setstate(ios_base::failbit); buf.pubseekoff(0,ios_base::beg); // 3 }
//1 | ios_base::init() 関数は規定クラスを初期化します。初期化では一部、ios_base の logbuffer までのポインタのインストールが行われるので、基底クラスはこのバッファにアクセスします。 |
//2 | ファイルを開いて書き込みます。 |
//3 | 書き込みは、常に先頭から開始します。 |
この新しいログバッファの使用方法を次に示します。
int main () { using std::char_traits; using std::endl; logstream<char,char_traits<char> > log(4000,"test.log"); // 1 for (int i = 0; i < 1000; i++) // 2 log << i << ' '; log.rdbuf()->pubseekoff(0,std::ios_base::beg); // 3 int in = 0; log >> in; // 4 return 0; }
//1 | 最大サイズが 4000 文字の logstream オブジェクトを作成し、ファイル test.log に接続します。 |
//2 | 0 から 999 までの整数をログファイルに書き出します。この数値の表示に必要な合計文字数とその間のスペースが、設定した最大サイズよりも大きくなります。 |
//3 | ファイルの先頭までシークを行います。 |
//4 | ファイルの最初の値を調べます。通常の fstream を使用した場合は、この値は 0 になります。これは最初に書き出される値です。しかし、logstream を使用してログの容量以上に書き出す設定になっているため、ログは先頭を一巡しており、得られる値は別の値になるはずです。 |
Copyright (c) 1998, Rogue Wave Software, Inc.
このマニュアルに関する誤りのご指摘やご質問は、電子メールにてお送りください。
OEM リリース, 1998 年 6 月