プログラミング言語 C における文字列というのはメモリの塊そのものです。 見えているままのデータがメモリ上のどこかに配置されています。 余計なことをしない単純さは機械の性能を引き出しやすい一方で、ちょっとしたことでもプログラマが手間をかけなければいけません。
もちろん、文字列を扱うライブラリを導入したり作ったりすればいくらでも複雑なことは出来るのですが、凝ったデータ構造は標準ライブラリとの組合せがやり難くなります。 そのあたりを考慮して使い勝手がよく汎用性が高い仕組みが作れないものかと私は考えていました。
そして先日、 Qiita に投稿されたある記事の文字列生成関数に対して「出力先として FILE*
を指定できるバージョンもあればよいのではないか」という旨のコメントがついているのを見て、文字列 (メモリブロック) にストリームとしてのインターフェイスがあればよいのだと思いつきました。
ファイルと文字列が共通のインターフェイスで扱える仕組みを私は知っています。 Scheme の文字列ポートです。 C のファイルの読書きをするときにはストリームという抽象化された層が用意されていますが、 Scheme にも同様にポートという概念が用意されており、ポートは文字列に対しても使えます。 考え方を真似て C でもやれば良いのです。
結果として出来たライブラリに memstream と名付けて Github に置きました。
例えば数をストリームに出力する関数があったとして、関数 open_output_memstream
がオープンしたストリームを出力先にすればそのまま文字列を構築することが出来ます。 以下のように使えます。
#include <stdio.h>
#include <stdlib.h>
#include "memstream.h"
int count_three(FILE* fp) {
int total=0;
for(int i=3; i>0; i--)
total += fprintf(fp, "%d\n", i);
return total;
}
char* count_three_string(void) {
FILE* fp=open_output_memstream();
count_three(fp);
fputc('\0', fp);
size_t len;
char* str = mclose(fp, &len);
return str;
}
int main(void) {
count_three(stdout);
char* str=count_three_string();
printf("%s\n", str);
free(str);
return 0;
}
ライブラリ memstream は Windows のパイプの機能を利用しています。 パイプの読書きのハンドルに対しては、シークを除いてはファイルに対しての読書きとほぼ同じ操作が可能なので、パイプのもう一旦で受取った情報を文字列として構築するという考え方です。 私自身のプログラミング環境の都合で Windows のみを想定していますが、 Unix 等でも実現可能な方法だと思うので、興味があれば移植してみても面白いかもしれません。