2016/10/12 ちょっと追記
これ。
https://dlang.org/ctarguments.html
可変長のテンプレート引数をT...で受け取ってこねくり回そうという話。
やってみる。
import std.stdio;
import std.meta;
void main()
{
template Variadic(T...)
{
static assert (T.length > 1);
pragma(msg, T[0]); // <- コンパイル時のメッセージ表示
pragma(msg, T[1]);
pragma(msg, T);
}
alias Dummy = Variadic!(int, 42); // <- Variadicテンプレートの評価
//writeln(Dummy); // <- これはエラー。Dummyは実行時には存在しない
}
T...はarrayのようにlengthがあってインデックスアクセスできる。
実行時じゃなくてコンパイル時に以下の表示。
|| int
|| 42
|| tuple((int), 42)
T...はtupleらしい。
rdmdで試したところ2回目から表示されなかった(コンパイルされてない)。
AliasSeqでテンプレート引数を変数化する
AliasSeqをaliasに代入する感じで後で使える。
alias seq=AliasSeq!(int, 42);
// alias AliasSeq!(int, 42) seq; <- 別記法
alias Dummy = Variadic!seq;
リストを再帰的に操作する
- https://github.com/dlang/phobos/blob/master/std/typecons.d#L341
- D:\D\dmd2\src\phobos\std\typecons.d
を参考にした。
template LengthSeq(T...)
{
static if (T.length == 0)
{
// 再帰終わり
alias LengthSeq = AliasSeq!();
}
else
{
alias LengthSeq =
AliasSeq!(T.length // 1個消費
, LengthSeq!(T[1 .. $]) // 後続を再帰呼び出し
);
}
}
alias ls = LengthSeq!(1, "a", int, "hello");
pragma(msg, ls);
|| tuple(4u, 3u, 2u, 1u)
0からn-1までのリストを作る
std.metaとかにありそうだったけど見つからなかったので。
template Seq(uint length)
{
template SeqRec(uint n) // <- nestしたテンプレート。クロージャ的な書き方ができる
{
static if(n==0)
{
// 再帰終わり
alias SeqRec = AliasSeq!();
}
else{
alias SeqRec = AliasSeq!(length-n
, SeqRec!(n-1));
}
}
alias Seq = SeqRec!(length);
}
alias seq=Seq!4;
pragma(msg, seq);
|| tuple(0u, 1u, 2u, 3u)
静的なリフレクションがある
struct Vertex
{
float[4] Position;
float[3] Normal;
float[4] Color;
float[4] TexCoord0;
}
import std.traits;
alias fields=Fields!Vertex;
pragma(msg, fields);
(float[4], float[3], float[4], float[4])
型からメンバーのバイトoffsetのマップを作る
template OffsetMap(T)
{
//alias fields=FieldTypeTuple!T;
alias names=FieldNameTuple!T;
alias indices=Seq!(names.length);
int[string] OffsetMap={
int[string] map;
foreach(i; indices)
{
map[names[i]]=T.tupleof[i].offsetof;
}
return map;
}();
}
auto map=OffsetMap!Vertex;
writeln(map);
Error: non-constant expression ["Position":0, "Normal":16, "Color":28, "TexCoord0":44]
エラー。連想配列は静的ではないのか。
ちょっと理解が不十分。
int[string] OffsetMap(T)()
{
//alias fields=FieldTypeTuple!T;
alias names=FieldNameTuple!T;
alias indices=Seq!(names.length);
int[string] map;
foreach(i; indices)
{
map[names[i]]=T.tupleof[i].offsetof;
}
return map;
}
auto map=OffsetMap!Vertex;
writeln(map);
["Color":28, "Position":0, "TexCoord0":44, "Normal":16]
ランタイムとのコンパイルタイムのちゃんぽんになった。
index付きのforeachを活用するすればindices不要だった。
int[string] OffsetMap(T)()
{
//alias fields=FieldTypeTuple!T;
alias names=FieldNameTuple!T;
//alias indices=Seq!(names.length);
int[string] map;
foreach(i, name; names) // <- index 付きforeachを使える
{
map[name]=T.tupleof[i].offsetof;
}
return map;
}
auto map=OffsetMap!Vertex;
writeln(map);
["Color":28, "Position":0, "TexCoord0":44, "Normal":16]
参考
やりたかったことはこれ。
後でちゃんと読む。
tupleofを型ではなく変数に対して使う(追記2016/10/12)
型を連想配列のように使えるようだ。
// オーバーロードがあって・・・
void setUniform(string name, const vec3!float value)
{
glUniform3f(getUniformLocation(name), value.x, value.y, value.z);
}
void setUniform(string name, const float[4][4] value)
{
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE,
&value[0][0]);
}
// T型の各フィールドについて静的にディスパッチする
void setUniform(T)(ref T value)
{
alias names=FieldNameTuple!T;
foreach(i, name; names)
{
setUniform(name, value.tupleof[i]); // オーバーロード呼び出し
}
}
// Tは例えばこんな感じ
struct UniformVariables
{
mat4!float uModelMatrix = mat4!float.identity;
vec3!float uLightDirection = vec3!(0, -1, 0);
}
シェーダーの変数代入や、インスペクタのGUI構築がコンパイル時に解決済みにできそうな気がする。
強い。
おわり
C++のtemplateと違って、Dでは適当に書いてもわりとあっさり動きますよ。