2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-10-02

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;

リストを再帰的に操作する

を参考にした。

    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では適当に書いてもわりとあっさり動きますよ。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?