プリプロセッサ代わりに Ruby を使う
後輩からこんな質問を受けました。
「こういうマクロの展開結果を 16 進数で出したいんですけど、簡単な方法ありませんか?」
(EVENT_DEF.h) #define ID_MODULE1 8 #define PRIORITY 2 #define EVENT_A_CHANGE_STATE ((2<<24)+( 0<<16)+(ID_MODULE1<<8)+PRIORITY) #define EVENT_A_CHANGE_RESULT ((2<<24)+( 1<<16)+(ID_MODULE1<<8)+PRIORITY) // 以下続く…
一番単純な方法はこんなプログラムを書くことです。
#include "EVENT_DEF.h" void main() { printf( "EVENT_A_CHANGE_STATE - %08x\n", EVENT_A_CHANGE_STATE ); printf( "EVENT_A_CHANGE_RESULT - %08x\n", EVENT_A_CHANGE_RESULT ); // 以下延々続く… }
しかし、 define が数百もある場合はそうもいきません。全ての定義の printf() を書かなければならないからです。ぞっとしませんね。
次に考えたのは、プリプロセッシング後のソースを見ることです。 VC++ 6.0 でやる場合、 cl.exe の引数に /P (/EP)を指定しろと MSDN に書かれています。というわけで、上の単純なプログラムの設定を書き換えてみました。[プロジェクトの設定] -> [C/C++]タブのプロジェクトオプションで
/nologo /MLd /W3 /Gm …
を
/nologo /P /EP /MLd /W3 /Gm …
に変更。これでソースコードと同じ場所に *.i が出力されます。結果、こんなファイルが出てきました。
void main() { printf( "EVENT_A_CHANGE_STATE - 0x%08x\n", ((2<<24)+( 0<<16)+(8<<8)+2) ); printf( "EVENT_A_CHANGE_RESULT - 0x%08x\n", ((2<<24)+( 1<<16)+(8<<8)+2) ); }
えー。
…というわけで、結局 Ruby でプリプロセッサライクなスクリプトを書くことに。
(pp.rb) File.open( ARGV.shift ) { |f| f.each_line {|l| if l =~ /^\#define\s(\w+)\s+(.+)$/ name, s = $1, $2 # 定義を置換 s.gsub!( /ID_MODULE1/, 8.to_s ) s.gsub!( /PRIORITY/, 2.to_s ) printf "%s\t0x%08x\n", name, eval(s).to_i end } }
これで
ruby pp.rb EVENT_DEF.h
として、ようやく次のような出力結果が得られました。やれやれ。
EVENT_A_CHANGE_STATE 0x02000802 EVENT_A_CHANGE_RESULT 0x02010802