呼び出し元(親)のスタックをいじって変数を置けたらさぞかしC言語も書きやすくなるだろうなぁ、と思った。
s/orig/new/g;
を考えた場合、C言語では例えば以下のようになる。
{ int r; char *text="test text da orig"; char new_text[BUFSIZE]; r=my_regexp_replace("orig", "new", text, new_text, BUFSIZE); if(r){ printf("ERROR: my_regexp_replace(...)=%d\n", r); return -1; } }
おまえはアホかと言いたい。エラー処理のためのtry-catch
がないのはしょうがないとしても、せめて以下のように書かせてほしい。
{ int r; char *text="test text da orig"; char *new_text=my_regexp_replace("orig", "new", text); if(new_text==NULL){ printf("ERROR: my_regexp_replace(...)=NULL\n"); return -1; } }
ここでミソなのは、new_textの実体がスタックに置かれるということ。親のスタックをかじれるCをここで「親スタC」と省略して呼ぶことにすると、親スタCではmy_regexp_replaceが親スタック内に可変長でデータを確保できるので、戻りテキストデータを親スタック内に確保してそこに置けるのである。親関数は戻り値を見て処理するが、あえて開放する必要はない。期限(スコープ)が切れれば勝手に開放される。無論GCのほうが良いことは間違いないけど、親スタCってGCよりは実装が簡単じゃない?
いつも思うのは、malloc等で確保された領域を返すとメモリリークが起きるし開放の作法が問題になる(いわゆるstrdup問題※)。そこでポインタを含む引数をたくさん並べることになるのだが、これがプログラムを悪くしているように思う。数あるメモリの中で(そんなたくさんの種類はないけど(笑))、スタックはCに深々と組み込まれた共通基盤である。活用しない手はない。
…でもスタックの構造を考えるといろいろ問題があるよな。そんなこと言ったら身もフタもないが(笑)。
(※)strdup問題というのは、strdupの戻り値でポインタが返ってくるバイト列がmallocというstring系でない関数で確保された領域であるということだ。だからプログラムは「strdupで確保したメモリをfreeで開放する」のようなチグハグな作りになってしまう。ここで「strdupで確保したものをstrfreeで開放する」のように厳密にやってstring系の関数が全部こうなれば良いのだが、実際はさらに面倒なことになりそうだからか思想上の問題か知らないけどCはそこまでやっていない。