streambufのカスタマイズ

ネットワーク関連のライブラリでsockstreamだけ良く分からないまま放置していたので,真面目に調べてみました.ソケットを介したデータ通信をC++のiostream(std::cinとかstd::cout)のインターフェースで行おうとするとbasic_streambuf<Ch, Tr>をカスタマイズすることになります.カスタマイズするときは,basic_streambuf<Ch, Tr>を継承してvirtual宣言されてあるメソッド群をオーバーライドします.詳細は,iostreamの拡張あたりで.

さて,ここで問題になったのが,出力用バッファを用意するかどうかということでした.ソケットを介したデータ通信をiostreamのように見せかけるためには,underflow()が呼ばれたときにrecv()システムコールで受信し,overflow()が呼ばれたときにsend()システムコールで送信するように実装するのですが,出力用バッファがない状態だと1文字ずつsend()してしまうため明らかに効率が悪いです.

ただ,streambufの規格を調べたり実際に実験してみた結果,出力バッファはなくてもいけるんじゃないかなという感触が得られました.streambufの出力の動きは,以下のようになります.

  1. <<演算子で文字列(など)がストリームへ流されると,sputn(s, n)が呼び出される(sは文字列,nは文字列数).
  2. sputn(s, n)は,単にxsputn(s, n)を呼び出すだけという実装になっている.
  3. xsputn(s, n)はデフォルトの実装では,sputc(c)がn回呼び出される.
  4. sputc(c)は出力バッファがあればそこへ格納する.ないか出力用バッファがいっぱいのときはoverflow()メソッドを呼び出す.
  5. xsputn(s, n)は派生クラスで別の実装を行っても良いことになっている.

と言う訳で,xsputn(s, n)は,(1回の<<演算子で渡された)文字列を全部持っているため,xsputn(s, n)の中でsend()システムコールで送ってしまえば出力用バッファは要らないことになります.ただし,その実装にした場合,n文字全部送れなかったときにはbadbitが立ってしまうので注意する(n文字送れるまでsend()し続けるとか)必要があります.

出力用バッファを用意しないときの動きの特徴は,<<演算子で文字列(など)を渡すと,その文字列だけですぐに送信してしまうということでしょうか.出力バッファがある状態だと,バッファがいっぱいになるか,std::flushを流さない限りは通信は(多分:p)発生しません.出力バッファがあった方がいいような気もしますが,中間バッファを介さない分速くなるかなぁとかいろいろ考えるとどっちがいいのか分からないところです.現在の実装は,出力バッファなんていらないよ派.

今まで書いたライブラリコードは大学内のスペースにアップロードしていたのですが,新たにスペースを取得してライブラリのWebページと実装コード部だけは切り離すことにしました.新しいWebページはCLX C++ Libraries.同時にSourceForge.JPにもアカウントを作って,コードはSourceForge.JP内のSubversionにぶち込んでおくことに.