streambuf と iostream

std::streambuf と std::iostream / std::istream / std::ostream のどれかを継承して自分好みの stream を作ろうとかそういうアレ。多分 3 回目くらいの try 。今まで適当にいじってわかんねーわかんねー言ってたので今回はとりあえず仕様からあたることにした。というわけでまず http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2010/n3090.pdf のおれ訳 -> http://github.com/januswel/Cxx0xISja の 27 ではじまる file 。なんだけどけっこう大雑把というか何がどう呼ばれてどう動くのかがこれだけではわかりにくいので ( C++ の仕様書はすばらしく情報が散らばっているので多分全部がここに書かれているわけではない、と思う ) 自分でまとめてみた。とはいっても仕様書の焼き直しをしても意味がないので「何をどうすればいいのか」という点を頑張る方向で。

とりあえず仕様が派生 class 定義者に要求するのは std::streambuf を派生させて以下の仮想関数を適宜定義すること。というのも std::iostream を操作すると stream まわりの仕組みが最終的にこいつらを呼び出すからであってまぁ継承を使った customize という意味では orthodox だね。あー全部 protected 。

  • locale まわり
    • void imbue(const std::locale&);
  • buffer まわり
    • basic_streambuf* setbuf(char_type* s, std::streamsize n);
  • positioning まわり
    • pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);
    • pos_type seekpos(pos_type sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);
  • input まわり
    • std::streamsize showmanyc();
    • std::streamsize xsgetn(char_type* s, std::streamsize n);
    • int_type underflow();
    • int_type uflow();
  • put back まわり
    • int_type pbackfail(int_type c = traits_type::eof());
  • output まわり
    • std::streamsize xsputn(const char_type* s, std::streamsize n);
    • int_type overflow(int_type c = traits_type::eof());
    • int sync();

と signature を並べてみたんだけどこれだけではさっぱりわからないので手っ取り早く実装から動作を把握してみる。というわけで以下の code ( std::streambuf を派生させて上記の仮想関数をすべて自分の名前を出力 & 仕様で定義されている default 動作をするように定義 ) を compile & execute 。下 2 つがうちで使ってる処理系での結果。余談だけど抽出子 ( operator>>() ) まわりを見ると g++ の方が効率的に動作するぽい ( 派生 class で効率的に動作する実装が与えられている可能性のある xsputn() を呼び出している ) 。

というわけで上記の結果を見ると大体以下のような感じなことがわかる。

  • ほとんどの入力系は uflow() 経由で最終的に underflow() 、 read() が xsgetn() というあたり
  • 出力系は overflow() と xsputn() で
  • 位置まわりは入力出力を問わず seekoff() と seekpos() で処理されている
  • 文字戻し系は pbackfail() 一択
  • 同様に locale 操作は imbue() のみ

これで目的 ( std::(i|o|io)stream class の member function ) と手段 ( std::streambuf の仮想関数 ) の関連が見えたわけだ。となると次はそれぞれの仮想関数で何をすればいいのかを明らかにする scene なわけなんだけど一回切る。