特性
特性 (trait) は、コンパイル時に、 プログラムからコンパイラの内部情報を取得できるようにする言語拡張です。 コンパイル時リフレクション と呼ばれることもあります。 構文は(pragmaに似た)簡単に拡張可能な形式で、 新しい機能が必要になったら すぐに追加できるようになっています。
TraitsExpression: __traits ( TraitsKeyword , TraitsArguments ) TraitsKeyword: isAbstractClass isArithmetic isAssociativeArray isFinalClass isFloating isIntegral isScalar isStaticArray isUnsigned isVirtualFunction isAbstractFunction isFinalFunction hasMember getMember getVirtualFunctions classInstanceSize allMembers derivedMembers isSame compiles TraitsArguments: TraitsArgument TraitsArgument , TraitsArguments TraitsArgument: AssignExpression Type
isArithmetic
引数が全て、算術型であるか算術型の式である場合、 true を返します。 それ以外の場合は false を返します。 引数が無い場合は false を返します。
import std.stdio; void main() { int i; writefln(__traits(isArithmetic, int)); writefln(__traits(isArithmetic, i, i+1, int)); writefln(__traits(isArithmetic)); writefln(__traits(isArithmetic, int*)); }
出力:
true true false false
isFloating
isArithmetic と同じように、浮動小数点数型 (虚数型と複素数型を含む) かどうかを判定します。
isIntegral
isArithmetic と同じように、整数型 (文字型を含む) かどうかを判定します。
isScalar
isArithmetic と同じように、スカラ型 かどうかを判定します。
isUnsigned
isArithmetic と同じように、符号無し型 かどうかを判定します。
isStaticArray
isArithmetic と同じように、静的配列型 かどうかを判定します。
isAssociativeArray
isArithmetic と同じように、連想配列型 かどうかを判定します。
isAbstractClass
引数が全て、abstractクラス型であるかabstractクラス型の式である場合、 true を返します。 それ以外の場合は false を返します。 引数が無い場合は false を返します。
import std.stdio; abstract class C { int foo(); } void main() { C c; writefln(__traits(isAbstractClass, C)); writefln(__traits(isAbstractClass, c, C)); writefln(__traits(isAbstractClass)); writefln(__traits(isAbstractClass, int*)); }
出力:
true true false false
isFinalClass
isAbstractClass と同じように、finalクラス かどうかを判定します。
isVirtualFunction
引数を1つ取ります。その引数が仮想関数なら true、そうでなければ false を返します。
import std.stdio; struct S { void bar() { } } class C { void bar() { } } void main() { writefln(__traits(isVirtualFunction, C.bar)); // true writefln(__traits(isVirtualFunction, S.bar)); // false }
isAbstractFunction
引数を1つ取ります。その引数がabstract関数なら true、そうでなければ false を返します。
import std.stdio; struct S { void bar() { } } class C { void bar() { } } class AC { abstract void foo(); } void main() { writefln(__traits(isAbstractFunction, C.bar)); // false writefln(__traits(isAbstractFunction, S.bar)); // false writefln(__traits(isAbstractFunction, AC.foo)); // true }
isFinalFunction
引数を1つ取ります。その引数がfinal関数なら true、そうでなければ false を返します。
import std.stdio; struct S { void bar() { } } class C { void bar() { } final void foo(); } final class FC { void foo(); } void main() { writefln(__traits(isFinalFunction, C.bar)); // false writefln(__traits(isFinalFunction, S.bar)); // false writefln(__traits(isFinalFunction, C.foo)); // true writefln(__traits(isFinalFunction, FC.foo)); // true }
hasMember
第一引数には、メンバがあるか調べたい型か、 メンバがあるか調べたい型を持つ式を指定します。 第二引数は文字列です。 その文字列が指定された型の有効なプロパティだった場合 true、そうでなければ false を返します。
import std.stdio; struct S { int m; } void main() { S s; writefln(__traits(hasMember, S, "m")); // true writefln(__traits(hasMember, s, "m")); // true writefln(__traits(hasMember, S, "y")); // false writefln(__traits(hasMember, int, "sizeof")); // true }
getMember
二つ引数を取り、 第二引数は文字列です。 "第一引数 . 第二引数を識別子化したもの" という形の式を返します。
import std.stdio; struct S { int mx; static int my; } void main() { S s; __traits(getMember, s, "mx") = 1; // same as s.mx=1; writefln(__traits(getMember, s, "m" ~ "x")); // 1 __traits(getMember, S, "mx") = 1; // error, no this for S.mx __traits(getMember, S, "my") = 2; // ok }
getVirtualFunctions
第一引数にはクラス型か、 クラス型を持つ式を指定します。 第二引数には、 そのクラスの関数名にマッチする文字列を指定します。 結果は、その名前で定義されている仮想関数全ての配列です。
import std.stdio; class D { this() { } ~this() { } void foo() { } int foo(int) { return 2; } } void main() { D d = new D(); foreach (t; __traits(getVirtualFunctions, D, "foo")) writefln(typeid(typeof(t))); alias typeof(__traits(getVirtualFunctions, D, "foo")) b; foreach (t; b) writefln(typeid(t)); auto i = __traits(getVirtualFunctions, d, "foo")[1](1); writefln(i); }
出力:
void() int() void() int() 2
classInstanceSize
クラス型かクラス型を持つ式一つを 引数に取ります。 返値は size_t 型で、 値はそのクラスのインスタンスが消費するバイト数になります。 この値は、クラスの静的型に基づいたサイズです。 多態は考慮されません。
allMembers
型か、 型を持つ式一つを引数にとります。 返値は文字列リテラルの配列で、 指定された型及びその基底クラスの全てのメンバの 名前が格納されています。 名前が重複することはありません。 また、組み込みのプロパティは含まれません。
import std.stdio; class D { this() { } ~this() { } void foo() { } int foo(int) { return 0; } } void main() { auto a = __traits(allMembers, D); writefln(a); // [_ctor,_dtor,foo,print,toString,toHash,opCmp,opEquals] }
結果の文字列配列がどのような順番になっているかは 特に定義されていません。
derivedMembers
型か、 型を持つ式一つを引数にとります。 返値は文字列リテラルの配列で、 指定された型の全てのメンバの名前が格納されています。 名前が重複することはありません。 基底クラスのメンバは含まれません。 また、組み込みのプロパティは含まれません。
import std.stdio; class D { this() { } ~this() { } void foo() { } int foo(int) { return 0; } } void main() { auto a = __traits(derivedMembers, D); writefln(a); // [_ctor,_dtor,foo] }
結果の文字列配列がどのような順番になっているかは 特に定義されていません。
isSame
二つの引数を受け取り、同じシンボルならば true、 そうでなければ false を返します。
import std.stdio; struct S { } int foo(); int bar(); void main() { writefln(__traits(isSame, foo, foo)); // true writefln(__traits(isSame, foo, bar)); // false writefln(__traits(isSame, foo, S)); // false writefln(__traits(isSame, S, S)); // true writefln(__traits(isSame, std, S)); // false writefln(__traits(isSame, std, std)); // true }
二つの引数がリテラルとenumで構成された式であって、 同じ値に評価されれば、true が返ります。
compiles
全ての引数がコンパイルが通る(意味的に正しい)ならば、 true を返します。 引数としては、 文法的に正しいシンボル,型,式のいずれかを指定できます。 文や宣言を引数として指定することはできません。
ゼロ引数で呼び出した場合 false となります。
import std.stdio; struct S { static int s1; int s2; } int foo(); int bar(); void main() { writefln(__traits(compiles)); // false writefln(__traits(compiles, foo)); // true writefln(__traits(compiles, foo + 1)); // true writefln(__traits(compiles, &foo + 1)); // false writefln(__traits(compiles, typeof(1))); // true writefln(__traits(compiles, S.s1)); // true writefln(__traits(compiles, S.s3)); // false writefln(__traits(compiles, 1,2,3,int,long,std)); // true writefln(__traits(compiles, 3[1])); // false writefln(__traits(compiles, 1,2,3,int,long,3[1])); // false }
使い方としては:
- 複雑なテンプレート定義の内部で、 読みやすいエラーメッセージを提供する
- テンプレートの部分特殊化よりも 粒度の細かい特殊化を行う
