## 18.2 新しいストリームバッファクラスの派生

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