PRINT USINGその2

なんかの拍子で5分間だけDやります。
目的のシンタクス。

print_using!("### + ### = ###")(1, 2, 3);
void function (int, int) f = &print_using!("##/##");
f(12, 8);

めんどくさいんで対応は#とintだけでいいや。連続している#の数の幅でintを右詰め出力する関数を生成するtemplate。template引数の文字列に応じて関数の引数の数も変化。
さて……。

import
  std.typetuple,
  std.traits;

template print_using(char[] format, int width)
{
  static if(format.length == 0 && width == 0){
    void func()
    {
      printf("\n");
    }
  }else static if(format.length == 0 && width > 0){
    void func(int value)
    {
      printf("%.*d\n", width, value);
    }
  }else static if(format[0] == '#'){
    alias print_using!(format[1 .. $], width + 1).func func;
  }else static if(width > 0){
    alias print_using!(format, 0).func rest;
    alias ParameterTypeTuple!(rest) r_params;
    void func(int value, r_params args)
    {
      printf("%.*d", width, value);
      rest(args);
    }
  }else{
    alias print_using!(format[1 .. $], 0).func rest;
    alias ParameterTypeTuple!(rest) r_params;
    void func(r_params args)
    {
      printf("%c", format[0]);
      rest(args);
    }
  }
}

template print_using(char[] format)
{
  alias print_using!(format, 0).func print_using;
}

void main()
{
  printf(`print_using!("")();`\n);
  print_using!("")();
  printf(`print_using!("xyz")();`\n);
  print_using!("xyz")();
  printf(`print_using!("#")(123);`\n);
  print_using!("#")(123);
  
  print_using!("### + ### = ###")(1, 2, 3);
  void function (int, int) f = &print_using!("##/##");
  f(12, 8);
}

すごいや、ぼくにもできたよてんぷれーと!
30分ぐらいかかっているのは内緒です。
TypeTupleをそのまま引数のシグネチャにするのは確かにアイデアと思うのですが、inout付けたいときはどうするんだろう。あとvoid func(tuple1 args1, tuple2 args2)だと内部エラー。
どうでもいいですが、templateのインスタンス化結果を中のものそのものにしてしまう定義は

  alias print_using!(format, 0).func print_using;

こう書けた方がDらしいと思います。

  alias print_using!(format, 0).func this;

って何を今更……。
にしてもいやー、"!"の位置間違えまくりました。instanceの方が好みだなーぼくは。
それはそれとして、最早static ifで直感的に構文木を構築してしまえるんですね。こりゃー簡単かつ強力です。私なんかはどのパスを通っても同じシグネチャで同じものを揃えないといけない、なんかの制限があったほうがいいような気もしてしまうのですが、コンパイルタイム言語で型チェックしなくても展開後の実行時言語をコンパイルする時点でどうせコンパイルエラーになるので、コンパイルタイム言語は柔軟性最優先、と。実に正しいな。
そして、こーゆー遊びをやるときは、ギャグにしか思えなかった-runが、とてつもなく便利なことに気付かされました。
templateによるコード生成は強力なんですが、言語仕様でかい組は「それで最終的にやりたいこと」そのものを言語に抱えてる&後付けするより組み込みの方が強力に決まってるので、言語に含める気にはならないけれどややこしい機能をシンプルに記述できることが要求される「書式化入出力」は、template組の数少ない独壇場ですよね。ボソボソ。つーかdout/din廃止されないかな。