C で lisp のwith系マクロごっこ

色々なパラダイムの言語を噛った後に、c をやるのは面白いかもしれない。

例によってcygwin + gccで確認してます。引数を取るマクロ(#define)は、便利なんですが、予想以上でした。プリプロセッサが処理する時は単なる文字列として扱われるという認識です。「マクロの引数に指定できるものは何だろう?」というのが元々なんですが、文字列だから何でもいいと思います。で、色々と弄っていると、複数の文でさえ、引数として渡せることが判りました!?。(cygwin+gccだけで確認しました。念のため)

lispのマクロ定義の引数でいうところの (defmacro name (&body body) …) のbodyと同じような機能を持っていることになります。

複数の文をマクロの引数に指定してみる

#include <stdio.h>
#include <stdlib.h>
#define loop(body) while(1){body}
int main(int argc, char** argv)
{
int i = 0;
loop(
if (i > 5) break;
printf("%d\n", i++);
);
return 0;
}

実行結果

$ gcc  -Wall loop.c
$ ./a.exe
01
2
3
4
5

こんなことが、できてしまいました。ちょっと説明すると、「if (i > 10) break; printf(“%d\n”, i++);」という複数の文が、loopマクロのbodyという引数に指定されて、whileに展開されました。これって、lispのマクロじゃん?色々な点でlispには及ばないけど、思っていた以上に*1強力でした。syntaxを追加できると言っても過言ではないような。。。

lispのwith系マクロごっこ

という流れで、もうちょっと変なのを定義してみました。ファイルハンドルの後始末をしてくれるwith_file_read_lineマクロを書いてみました。その実行例として、catコマンドみたいな処理をしてみました。

#include <stdio.h>
#include <stdlib.h>
#define with_file_read_line(file_name, line, length, body) \
    FILE *fp; \
    if ((fp = fopen(file_name, "r")) == NULL) { \
        fprintf(stderr, "file open error!!\n"); \
        exit(EXIT_FAILURE); \
    } \
    while (fgets(line, length, fp) != NULL) { \
        body \
    } \
    fclose(fp); \
    printf("closed.\n");
int main(int argc, char** argv)
{
char line[100];
int row_no = 1;
with_file_read_line("with.c", line, 100,
printf("%5d: ", row_no++);
printf("%s", line);
);
return 0;
}

実行結果

$ gcc  -Wall with.c
$ ./a.exe
1: #include <stdio.h>
2: #include <stdlib.h>
3:
4: #define with_file_read_line(file_name, line, length, body) \
5:  FILE *fp; \
6:  if ((fp = fopen(file_name, "r")) == NULL) { \
7:          fprintf(stderr, "file open error!!\n"); \
8:          exit(EXIT_FAILURE); \
...(ry
closed.

closed.と表示されたので、with_file_read_lineマクロを抜ける前に、ちゃんとファイルを閉じてくれたようです。

ところで、マクロの引数に、複数の文を指定できるのは、gccの拡張機能なんだろうか?

お遊びとしては、十分楽しかった。

*1:cのマクロに抱いていた偏見のせいで

コメントする

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。