D言語のコンパイル時引数リストとリフレクションのメモ

2016/10/12 ちょっと追記

これ。
https://dlang.org/ctarguments.html

可変長のテンプレート引数をT...で受け取ってこねくり回そうという話。
やってみる。

```d
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で試したところ２回目から表示されなかった(コンパイルされてない)。

# AliasSeqでテンプレート引数を変数化する

AliasSeqをaliasに代入する感じで後で使える。

```d
    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

を参考にした。

```d
    template LengthSeq(T...)
    {
        static if (T.length == 0)
        {
            // 再帰終わり
            alias LengthSeq = AliasSeq!();
        }
        else
        {
            alias LengthSeq =
                AliasSeq!(T.length // １個消費
                        , LengthSeq!(T[1 .. $]) // 後続を再帰呼び出し
                        );
        }
    }
    alias ls = LengthSeq!(1, "a", int, "hello");
    pragma(msg, ls);

|| tuple(4u, 3u, 2u, 1u)
```

# 0からn-1までのリストを作る
std.metaとかにありそうだったけど見つからなかったので。

```d
        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)
```

# 静的なリフレクションがある

```d
    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のマップを作る

```d
        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]
```

エラー。連想配列は静的ではないのか。
ちょっと理解が不十分。

```d
        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不要だった。

```d
        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]
```

# 参考
やりたかったことはこれ。

* https://pagefault.se/dlang/2016/02/26/declarative-opengl-dlang/

後でちゃんと読む。

# tupleofを型ではなく変数に対して使う(追記2016/10/12)
型を連想配列のように使えるようだ。

```d

// オーバーロードがあって・・・
    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では適当に書いてもわりとあっさり動きますよ。