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!