Imaginary Template Metaprogramming in D
D言語を使って脳内でメタプログラミングして遊んでみる(コンパイルできる環境が手元にないので想像)。本当にただの妄想なので、そこのところは了承してちょんまげ。
template Tuple(xs...) {
alias xs Tuple;
}いつの間にかPhobosから削除されていたけど、これがないと不便。
template Foldr(alias f, alias init) {
alias init Foldr;
}
template Foldr(alias f, alias init, alias x, xs...) {
static if (xs.length != 0)
alias f!(x, Foldr!(f, init, xs)) Foldr;
else
alias f!(x, init) Foldr;
}
template Foldl(alias f, alias init) {
alias init Foldl;
}
template Foldl(alias f, alias init, alias x, xs...) {
static if (xs.length != 0)
alias Foldl!(f!(init, x), xs) Foldl;
else
alias f!(init, x) Foldl;
}コンパイルタイムfold。
どうせならFoldl!((T, U) { ... }, x, xs)みたいにtemplateリテラル書きたいなぁ、となる。なんかデリゲートテンプレートリテラルはあるらしいけど。でも無名なtemplateがあったりしたら、そのメンバの名前を省略できなくて悲しい、なんかないのか…いや、そこはC++式にtypeメンバを用意するとかでいいのかも知れないけど。
struct Pack(xs...) {};別のタプルを二つ以上引数に取りたいときどうすればいいかなぁ…と思って考えてみた。型にすればいけるんじゃね?というわけで、
template Foo(T : Pack!(xs).Pack, U : Pack!(ys).Pack, xs..., ys...) {}なんかこんな感じに書けて、Foo!(Pack!(int, char), Pack!(double, float))と書けばxs, ysが勝手に求まってほしいんだけど、まぁ知らん。私が調べた限りでは、一度に複数のテンプレートパラメータを決定することはできるっぽいし、TemplateTupleParameterを二つ以上書くことは禁止されていない。
せめてtemplate Foo(T : Pack!(xs).Pack, xs...)とかはできてほしいというか、これができないとか何なの?ってなる。
あと一応 .Pack まで書いたけど、いらんのかどうかは知らん。
template Zip(T : Pack!(), U : Pack!()) {
alias Tuple!() Zip;
}
template Zip(T : Pack!(x, xs), U : Pack!(y, ys), x, xs..., y, ys...) {
alias Tuple!(Pack!(x, y), Zip!(Pack!(xs), Pack!(ys))) Zip;
}TとUを与えてx, xs, y, ysが自動推論されるなら、こんな感じにZipを書ける。
template AddReference(T) {
alias typeof(ref typeof(T.init) function() { return T.init; } ()) AddReference;
}refが型の一部で、typeof(f())が、f()の戻り値の型そのままを示すのであれば、AddReferenceメタ関数は書ける。ref Tというのは関数のパラメータの型か戻り値の型にしか書けないらしいので、こんなややこしい書き方になった。
template Foo(alias x, xs...) {
alias x Foo;
}
alias Tuple!(int, int, int) tup;
alias Foo!(int, int, int).x test1;
alias Foo!(Tuple!(int, int, int)).x test2;
alias Foo!(tup).x test3;って書いたとき、私としてはtest1〜3は全て同じ結果になってほしいのだけど、どうなんだろう…
test1はまぁintになると思うけど、test2の場合、alias xにTuple!(int, int, int)が丸ごとセットされるのか、test1みたいにタプルが解体されて、xがintでxsが残り全部、みたいになるのか。TemplateAliasParameterの説明にはTemplateInstanceを引数に取れるとは書いてないけど、global nameにTemplateInstanceが含まれるかもしれないから(C++ルールでは)、test2はintなの?それともTuple!(int, int, int)なの?test3の場合、引数は明らかにtemplate alias nameなので、結果はTuple!(int, int, int)にならないとおかしい。でもaliasかそうでないかで挙動が変わるのは不審すぎるし、このあたりの挙動が分からんなぁ…Exceptional DとかEffective Dとか出たら間違いなく槍玉にあげられるレベルでやばい。
まぁ細かいことは実装=規格ということでいいんだろうけど、それだといつまでたっても規格決まらないじゃない!遊びにくいですわよ!
で、また思ったんだけど、別に毎回毎回特殊化でパターン書いて判別しなくても、
template Unpack(T : Pack!(xs), xs...) {
alias xs Unpack;
}というのさえ私の意図した通りになってくれればUnpack!(tup)だけで済むので、それはそれでいいんじゃなかろうかと思う。
あとtemplate Foo() { template Foo() { alias int x; } }みたいに、あるテンプレートのすぐ内側に同じ名前のテンプレートが定義できるのだろうかというのも分からん。できたからと言って面白いことができるとか、そういうのは知らん。