古橋貞之の日記

kumofs MessagePack fluentd @twitter

2010-12-20

mplexでソースコードレベルメタプログラミング mplexでソースコードレベルメタプログラミングを含むブックマーク 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!

qwertyqwerty 2010/12/21 05:06 > HTML用のテンプレートエンジンはソースコードがあまりに読みにくくなる
ここを詳しく。
見た限りではERBのtrimモードに'%'を指定すればだいたい同じことが出来る。
print ERB.new(File.read(filename), 1, '%').result(binding())

viverviver 2010/12/21 05:19 eRubyだとインラインへのソースコードの埋め込みを <%= %> <% %> と書きますが、mplexでは [% %] [%: %] です。
=のタイプが面倒のも理由の一つですが、eRuby のようにメタ文字が < や > だと、vimで C++ のコードをシンタックスハイライトしたときに、メタ文字が構文エラーと解釈されて真っ赤に警告されてしまうのが大きいです。

viverviver 2010/12/21 05:27 あとtrimモードに'%'を指定しても、%は「行頭」にしか書けなくて、行頭に空白を入れて字下げした状態では書けないのです。
mplexだと書けます。ただ、本当に%演算子を書きたいときに面倒になる副作用があるわけですが、今のところは困っていません。

IKeJIIKeJI 2010/12/21 15:40 むしろ、/* と */ で囲んでみるとか?

#line 文の自動挿入とかが欲しくなる感じがしますが そういうのはどうでしょうか?

viverviver 2010/12/27 10:24 なるほど。/* */ も良さそうですね。ハイライトされますし。ただ本当に /* */ を使いたいときに困りそうです。
[% はC++の文法的に出現し得ないので、衝突する心配がありません。

ただ /* */ と同じアイディアで、% は #% にすると良さそうですね。こうすると本当の%演算子と衝突しなくなる上に、マクロとしてハイライトされるので嬉しい。
(と思ったら、vim では #% はハイライトされないですね…

#line文の自動挿入は欲しくなりますが、未実装です。

IKeJIIKeJI 2011/01/24 08:06 亀ですが。

>なるほど。/* */ も良さそうですね。ハイライトされますし。ただ本当に /* */ を使いたいときに困りそうです。
生で使うというよりは、JavaDocの/** */みたいに、/* */を含む何かで、囲むのがいいという意味です。
例えば、/*% %*/ はくどいので、 /*= */ とか?

試してないのですが、
シンタックスハイライトもそうですが、indent(1)が使えるかが心配です。
C/C++としてValidだとこの辺もきちんと動きそうです。