Dの武器の1つにコンパイル時関数実行(CTFE)というものがあります。 イントロスペクションと組み合わせることで、ジェネリックなプログラムを書くことができ、 強力な最適化を受けることができます。
トレイトはどのような入力が許されるか明示的に示すことを可能にします。
たとえばsplitIntoWords
は任意の文字列型を操作できます:
S[] splitIntoWord(S)(S input)
if (isSomeString!S)
これはテンプレートパラメータにも反映され、myWrapper
に渡される
シンボルが呼び出し可能な関数であることは保証されます:
void myWrapper(alias f)
if (isCallable!f)
シンプルな例として、2つのレンジの共通接頭辞を返すstd.algorithm.searching
の
commonPrefix
を分析します:
auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2)
if (isForwardRange!R1
isInputRange!R2 &&
is(typeof(binaryFun!pred(r1.front, r2.front)))) &&
!isNarrowString!R1)
これはこの関数が以下の条件でのみ呼び出せることを意味します:
r1
は保存可能(isForwardRange
で保証される)
r2
は反復処理可能(isInputRange
で保証される)
pred
はr1
とr2
の要素の型で呼び出せる
r1
はナロー文字列でない(char[]
、 string
、 wchar
または wstring
) - 簡単に言うと、他のデコーディングが必要
多くのAPIは汎用を目指します、しかし一般化のために余計な実行時間を使いたくはありません。 イントロスペクションとCTFEの力で、コンパイル時に与えられた型に対して最高のパフォーマンスを得るために メソッドを特殊化することが可能になります。
よくある問題として、配列とは異なりたどってみないとストリームやリストの正確な長さがわからないことがあります。
そのため任意の反復処理可能な型に対して一般化したシンプルな実装のstd.range
のメソッドwalkLength
でこのようにします:
static if (hasMember!(r, "length"))
return r.length; // O(1)
else
return r.walkLength; // O(n)
commonPrefix
Phobosにおいてコンパイル時のイントロスペクションの使用は普遍的です。たとえばRandomAccessRange
では場所を飛び越えることができ、アルゴリズムを高速化できるためcommonPrefix
は
RandomAccessRange
と線形反復処理可能なレンジで異なります。
std.traitsはcompiles
のような即座にコンパイルエラーを引き起こすことがあるためラップできないものを除きDの
traitsをラップします:
__traits(compiles, obvious error - $%42); // false
更にデバッグ用途にDは特殊なキーワードの組を提供します:
void test(string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__,
string func = __FUNCTION__, string pretty = __PRETTY_FUNCTION__)
{
writefln("file: '%s', line: '%s', module: '%s',\nfunction: '%s', pretty function: '%s'",
file, line, mod, func, pretty);
}
DのCLI実行ではtime
は必要ありません - CTFEが使えます!
rdmd --force --eval='pragma(msg, __TIMESTAMP__);'