kumofs MessagePack fluentd @twitter
2010-12-20
■mplexでソースコードレベルメタプログラミング

Webアプリケーションを作るとき、HTMLを生成するテンプレートエンジンをよく使いますが、これはパラメータに応じて様々なコードを生成する自動生成ツールであると言えます。
mplexは、プログラムを生成するためのテンプレートエンジンです。
実は MessagePack-RPC for C++ の実装に使っています。似たような関数をたくさんオーバーロードするために活用しています。(そろそろ可変長templateを使いたいですねぇ)
昔はeRubyを使っていたのですが、HTML用のテンプレートエンジンはソースコードがあまりに読みにくくなるので自作しました。
mplexを使うと、普通のプログラムの中にRubyのコードを埋め込むことができます:
// クラスを4つ生成 %4.times do |i| class Test[%i%] { public: %if i % 2 == 0 int even; // iが偶数ならメンバ変数を宣言 %end %i.times do |n| int member[%n%]; // 複数のメンバ変数を生成 %end }; %end
このコードから↓このようなコードが生成されます:
さらに、if文やイテレータには後置構文を使えます:
%4.times do |i| class Test[%i%] { public: // 後置構文でシンプルに書く int even; %> if num % 2 == 0 int member[%n%]; %|n| i.times }; %end
%# マクロ定義 %def genABC(ns) namespace [%ns%] { % %w[A B C].each do |a| % yield a % end } %end %# マクロ呼び出し %genABC("my_package") do |a| void func[%a%](); %end
ソースコードにパラメータを埋め込むこともできます。
ソースコードと「context script」を渡すと、ソースコード内に記述した変数に値が埋め込まれます:
# 連想配列を返すcontext script { "Get" => { "std::string" => "key", "uint32_t" => "flags", }, "Put" => { "std::string" => "key", "std::string" => "value", }, }
// selfにcontext scriptが渡される %self.each_pair do |name, args| void [%name%]( [%args.map {|type,name| "#{type} #{name}" }.join(", ")%] ); %end
mplex は ./configure && make install もしくは rubygems でインストールできます:
$ gem install mplex
$ git clone https://github.com/frsyuki/mplex.git $ cd mplex $ ./bootstrap $ ./configure && make && sudo make install
MessagePack-RPCのIDLでも、コード生成の部分で使おうとしているところです。
Enjoy Hacking!
トラックバック - http://d.hatena.ne.jp/viver/20101220/p1
リンク元
- 1659 http://www.e-hdk.com/hina/
- 171 http://www.google.co.jp/search?q=kumofs&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&hl=ja&client=firefox-a
- 161 http://twitter.com/
- 125 http://www.google.co.jp/search?sourceid=chrome&ie=UTF-8&q=kumofs
- 97 http://www.google.co.jp/search?sourceid=chrome&ie=UTF-8&q=MessagePack
- 82 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rlz=1T4GGLL_jaJP381&q=MessagePack
- 79 http://www.google.co.jp/search?q=MessagePack&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja-JP-mac:official&hl=ja&client=firefox-a
- 76 http://reader.livedoor.com/reader/
- 60 http://www.google.co.jp/
- 56 http://www.google.com/reader/view/

ここを詳しく。
見た限りではERBのtrimモードに'%'を指定すればだいたい同じことが出来る。
print ERB.new(File.read(filename), 1, '%').result(binding())
=のタイプが面倒のも理由の一つですが、eRuby のようにメタ文字が < や > だと、vimで C++ のコードをシンタックスハイライトしたときに、メタ文字が構文エラーと解釈されて真っ赤に警告されてしまうのが大きいです。
mplexだと書けます。ただ、本当に%演算子を書きたいときに面倒になる副作用があるわけですが、今のところは困っていません。
#line 文の自動挿入とかが欲しくなる感じがしますが そういうのはどうでしょうか?
[% はC++の文法的に出現し得ないので、衝突する心配がありません。
ただ /* */ と同じアイディアで、% は #% にすると良さそうですね。こうすると本当の%演算子と衝突しなくなる上に、マクロとしてハイライトされるので嬉しい。
(と思ったら、vim では #% はハイライトされないですね…
#line文の自動挿入は欲しくなりますが、未実装です。
>なるほど。/* */ も良さそうですね。ハイライトされますし。ただ本当に /* */ を使いたいときに困りそうです。
生で使うというよりは、JavaDocの/** */みたいに、/* */を含む何かで、囲むのがいいという意味です。
例えば、/*% %*/ はくどいので、 /*= */ とか?
試してないのですが、
シンタックスハイライトもそうですが、indent(1)が使えるかが心配です。
C/C++としてValidだとこの辺もきちんと動きそうです。