typelist.d

良い子のみんなーめたぷろぐらみんぐの時間だよー。とりあえず Typelist を書きました。参考書はもちろん Modern C++ Design 。

class NullType {}

class Typelist(H, T) {
  static H head;
  static T tail;
}

template Length(TL) {
  enum { value = 1 + Length!(typeof(TL.tail)).value }
}
template Length(TL : NullType) {
  enum { value = 0 }
}

template Pushfront(TL, T) {
  alias Typelist!(T, TL) value;
}

template TypeAt(TL, int i) {
  alias TypeAt!(typeof(TL.tail), i-1).value value;
}
template TypeAt(TL, int i : 0) {
  alias typeof(TL.head) value;
}

template Typelist1(T1) {
  alias Typelist!(T1, NullType) value;
}
template Typelist2(T1, T2) {
  alias Typelist!(T1, Typelist1!(T2).value) value;
}
template Typelist3(T1, T2, T3) {
  alias Typelist!(T1, Typelist2!(T2, T3).value) value;
}
template Typelist4(T1, T2, T3, T4) {
  alias Typelist!(T1, Typelist3!(T2, T3, T4).value) value;
}

unittest {
  alias Typelist1!(byte).value TL1;
  // it fails in Length. why?
//  alias Typelist2!(byte, short).value TL2;
  alias Typelist2!(short, byte).value TL2;
  // it fails in Length. why?
//  alias Typelist3!(byte, short, int).value TL3;
  alias Typelist3!(int, short, byte).value TL3;
  // it fails in Length. why?
//  alias Typelist4!(byte, short, int, long).value TL4;
  alias Typelist4!(long, int, short, byte).value TL4;

  assert(0 == Length!(NullType).value);
  assert(1 == Length!(TL1).value);
  assert(2 == Length!(TL2).value);
  assert(3 == Length!(TL3).value);
  assert(4 == Length!(TL4).value);

  alias Pushfront!(TL4, char[]).value TL5;
  assert(5 == Length!(TL5).value);

  TypeAt!(TL5, 0).value stringVal = "string";
  TypeAt!(TL5, 4).value byteVal = 'a';

  assert(1 == (TypeAt!(TL5, 4).value).sizeof);
  assert(2 == (TypeAt!(TL5, 3).value).sizeof);
  assert(4 == (TypeAt!(TL5, 2).value).sizeof);
  assert(8 == (TypeAt!(TL5, 1).value).sizeof);
}

メタカックロ電卓でもまた書くかなーと思ったけど、コンパイラのバグが多すぎてうまく行きませんでした。続きを書く気もなくなりました。暇な時にでも再チャレンジといきましょう。

色々コンパイラを落とすバグを見つけたけど、現状で報告しても嫌がらせにすぎないよねえ…

TypelistN (N は任意の整数) を書いてるとメタメタな何かが欲しくなりますね。そのへんは K.INABA さんがためになることを書いておられたなあ… MetaOCaml と Template Haskell か。学習キューに積んでおこう。

追記: Loki の Int2Type はそのまま作れるので整数のコンパイルタイムリストは実現できるわけですけど、 D言語の場合グローバル変数とかモジュールなんかも alias template parameter でリストにつっこめるわけです。それ使ってなんか面白いことできないもんかなと考えたけどどうも今一つ。

メタ記述

といえばやねうらおさんも考察されていたわけですけど (http://www.sun-inet.or.jp/~yaneurao/newlang/chap0000.html)、 ぶっちゃけ気合い入ったプリプロセッサってスクリプト言語になるわけで、…つまり eruby で書いちまえ。

Typelist1 から 4 を

template Typelist1(T1) {
	alias Typelist!(T1, NullType) value;
}

<%=
(2..30).collect do |i|
  "template Typelist#{i}(" +
  (1..i).collect do |j|
    "T#{j}"
  end.join(', ') +
  ") {\n" +
  "    alias Typelist!(T1, Typelist#{i-1}!(" +
  (2..i).collect do |j|
    "T#{j}"
  end.join(', ') +
  ").value) value;\n" +
  "}\n"
end.join('')
%>

で置き替えて、typelist.rbd とかいうファイルに保存、 eruby typelist.rbd > typelist.d でできあがり。実に美しくない解決だと思うのですよ。 boost-dev でもこういう話があったけど無視されてましたなあ…

あと何か Ruby に恨みでもありそうな書き方してみた。

Java 屋さんは Struts とか Velocity で書くと良いかもね。

追記: こりゃメタではないか。言語について何も知りませんから。でもプリプロセッサメタプログラミングっていうしなあ。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h