トレイト

Dの武器の1つにコンパイル時関数実行(CTFE)というものがあります。 イントロスペクションと組み合わせることで、ジェネリックなプログラムを書くことができ、 強力な最適化を受けることができます。

明示的契約

トレイトはどのような入力が許されるか明示的に示すことを可能にします。 たとえばsplitIntoWordsは任意の文字列型を操作できます:

S[] splitIntoWord(S)(S input)
if (isSomeString!S)

これはテンプレートパラメータにも反映され、myWrapperに渡される シンボルが呼び出し可能な関数であることは保証されます:

void myWrapper(alias f)
if (isCallable!f)

シンプルな例として、2つのレンジの共通接頭辞を返すstd.algorithm.searchingcommonPrefix を分析します:

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で保証される)
  • predr1r2の要素の型で呼び出せる
  • r1はナロー文字列でない(char[]stringwchar または 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 では場所を飛び越えることができ、アルゴリズムを高速化できるためcommonPrefixRandomAccessRangeと線形反復処理可能なレンジで異なります。

さらなるCTFEマジック

std.traitscompiles のような即座にコンパイルエラーを引き起こすことがあるためラップできないものを除き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__);'

掘り下げる

rdmd playground.d