C++ の streambuf を実装し、何も出力しない ostream を作成する2020年06月04日 12時48分48秒

C や C++ で出力をただ単に捨てたいときは、/dev/null を開いた ostream に書き込むことでも、実現できる。デバッグ用の出力などを切替えたりするときに便利だ。しかし、/dev/null とてファイルを開いての呼び出し等が掛かる。そこで、ostream を改造すると、書き出すこと自体を止めて、余計なシステムコールなどを更に削減できる。

C++ のストリーム系の実装はあれこれと絡み合って色々と複雑。ostream を再実装するのはあまり現実的ではない。ostream は streambuf クラスを初期化時に取る。こちら側でバッファの制御をすれば、比較的簡単に実現できる。ostream の関数は仮想関数にはなっていないので、継承しても意味が無いのだ。

streambuf を実装し、ostream 生成時に streambuf を渡せば良い。どうせ組になって使うので、両方継承している。

#include <iostream>

class null_stream : public std::streambuf, public std::ostream
{   
    char buf_[ 128 ];
protected:
    virtual int overflow( int c )
    {   
        std::cout << "overflow( " << c << " )" << std::e
ndl;
        setp( buf_, buf_ + sizeof( buf_ ) );
        return ( c == eof() ? '\0' : c );
    }

public:
    null_stream() : std::ostream( this ) {}
};

int main()
{   
    null_stream stream;
    stream << "HIDDEN MESSAGE" << std::endl;
    std::cout << "HELLO" << std::endl;
    stream << "hidden message" << std::endl;
    std::cout << "hello" << std::endl;
}
overflow 関数がバッファを割り当てるときに呼ばれる。まず最初の出力が最初の割当になる。それ以降は、バッファが足りなくなった時になる。目的と実装によって、sync したり、バッファを広げたり出来る。今回は小さめのバッファを保持し、内容物は無視。

実行結果は以下の通り。overflow 関数が最初に呼び出されたのが分かる。しかし、出力文字列はどこにも行かない。

% c++ null_stream.cpp
% ./a.out 
overflow( 72 )
HELLO
hello