GCCのプリプロセッサの動きをみて遊ぶ
ruby-libvirtのソースコードを読んでいたら、何やら怪しげなマクロが。
67 #define generic_get(kind, v) \ 68 do { \ 69 vir##kind##Ptr ptr; \ 70 Data_Get_Struct(v, vir##kind, ptr); \ 71 if (!ptr) \ 72 rb_raise(rb_eArgError, #kind " has been freed"); \ 73 return ptr; \ 74 } while (0); 75
シャープだと!しかも2つだと!見たことないぞ!
というわけで引数マクロについてちょっと実験してみました。
基本的な引数付きマクロ
まずは基本的な引数マクロ。
1 #include <stdio.h> 2 3 #define test_puts(hoge) \ 4 do { \ 5 hoge("inside hoge"); \ 6 } while (0); 7 8 main() 9 { 10 test_puts(puts); 11 }
$ gcc -E a.c main() { do { puts("inside hoge"); } while (0);; }
引数hogeがputsに展開されている様子がわかります。
引数付きマクロの引数をマクロ中テンプレートとくっつける
##を使うと、「引数とマクロ内のソースコードとくっつける」することができます。
1 #include <stdio.h> 2 3 #define test_puts(hoge) \ 4 do { \ 5 p##hoge##f("inside hoge"); \ 6 } while (0); 7 8 main() 9 { 10 test_puts(rint); 11 }
プリプロセッシング結果。
$ gcc -E a.c main() { do { printf("inside hoge"); } while (0);; }
うおおお!!これはすごい。
渡した引数を"(ダブルクオート)でくくってCの文字列に変換
個人的にはこれが一番変態的だと思う。
マクロ内では、#で与えられた変数は"(ダブルクオート)でくくられてCの文字列になります。
1 #include <stdio.h> 2 3 #define test_puts(hoge) \ 4 do { \ 5 puts(#hoge); \ 6 } while (0); 7 8 main() 9 { 10 test_puts(aiya-); 11 }
$ gcc -E a.c main() { do { puts("aiya-"); } while (0);; }
うひょー!これはすごい。まだまだ知らないことが多いなぁ。
ruby-libvirtでは、このテクニックを使ってコード量を削減しています。nice redhat.
*1:実際は#include