一般には、新しい 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 月