読者です 読者をやめる 読者になる 読者になる

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!