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

cprb -- Ruby風Cプリプロセッサ

C++や、それ風の言語で書いていると徒労感に襲われる時ってありませんか。

マクロはほとんど使用禁止みたいなもんだし、そもそも文字列を繋ぐ程度の能力しかありません。テンプレートはコンテナとか作ってるぶんにはいいのですが、少々凝ったことをしだすと関数オブジェクトだのtypenameだのが乱舞しだしてタイプ量が減ったのか増えたのか分からないことになり、チューリング完全とか言い出すと闇が待ち受けています。マクロを使って不安に苛まれるか、テンプレートを使って何とかするか、諦めて手で繰り返し書くかの不愉快な三択。

こいつはそもそもC++メタプログラミング能力が貧弱なのがいかんのです。


というわけで、Ruby風にCをプリプロセスできるというものを作ってみました。設計方針は

  • 実装が簡単、見た目がシンプル
  • emacsc++-modeで使うことが前提

です。

サンプルその1

いきなりですが、サンプルです。 hello-world.cpprb にこう書いておいて

#include <iostream>
using namespace std;

int main () {
  cout << "Hello World, I was born at #{Time.now} !\n";
}

cprb hello-world.cpprb --keep とやると、以下のような中間スクリプト hello-world.rb を生成し、

puts <<END0
#include <iostream>
using namespace std;

int main () {
  cout << "Hello World, I was born at #{Time.now} !\\n";
}
END0

これを実行することで、以下の hello-world.cppを作ってくれます。

#include <iostream>
using namespace std;

int main () {
  cout << "Hello World, I was born at Wed Dec 08 00:00:41 +0900 2010 !\n";
}

サンプルその2

このtest-1.cpprbが

#include <iostream>
using namespace std;

const int N = 10; // the size of mesh

/*"
  def xyzi()
  ctr = 0
  ['X', 'Y', 'Z'].each{|dir| yield(dir,ctr); ctr+=1}
  end  

  xyzi{|dir,| puts "*/ 
struct #{dir}{}; 
/*" }

  xyzi{|dir,i| puts "*/
int operator+(int x, #{dir} d) {
  return x + 1#{ ' *N'*i };
}
/*" }

  puts "*/
int main () {
  /* calculate address at (2,4,0) */
  cout << 0#{' +X()'*2}#{' +Y()'*4} << endl;
  return 0;
} 

こんな感じに変換されます。

#include <iostream>
using namespace std;

const int N = 10; // the size of mesh

struct X{}; 
struct Y{}; 
struct Z{}; 
int operator+(int x, X d) {
  return x + 1;
}
int operator+(int x, Y d) {
  return x + 1 *N;
}
int operator+(int x, Z d) {
  return x + 1 *N *N;
}
int main () {
  /* calculate address at (2,4,0) */
  cout << 0 +X() +X() +Y() +Y() +Y() +Y() << endl;
  return 0;
} 

まとめ

githubで公開しています。 https://github.com/nushio3/cprb
ちょっとサーベイして無かったので作ったのですが、いかにもありそうな発想なので、既出だよ、もっとマシなのがあるよという場合教えてください。そっちを使いに行くんで。感想などもお待ちしてます。