Traits

Eine von D's Stärken ist CTFE (Funktionsauswertung zur Kompilierzeit), die, kombiniert mit Introspektion, das Schreiben hoch optimierter generischer Programme möglich macht.

Explizite Contracts

Traits (dt.: Merkmale / Eigenschaften) erlauben es, explizit erlaubte Eingangstypen zu spezifizieren. Z.B. kann splitIntoWords beliebige String-Typen verarbeiten:

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

Dies gilt auch für Template-Parameter. Z.B. kann myWrapper sicherstellen, dass das hineingereichte Symbol eine aufrufbare Funktion ist:

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

Als einfaches Beispiel wird die Funktion commonPrefix aus std.algorithm.searching analysiert, die das gemeinsame Präfix zweier Ranges ausgibt:

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)

Dies bedeutet, dass die Funktion nur aufrufbar ist und damit kompiliert, wenn:

  • r1 speicherbar ist (garantiert durch isForwardRange)
  • r2 iterierbar ist (garantiert durch isInputRange)
  • pred mit den Elementtypen von r1 und r2 aufrufbar ist
  • r1 kein narrow string (char[], string, wchar or wstring) ist - der Einfachheit halber, sonst müsste eine Dekodierung stattfinden

Spezialisierung

Viele APIs zielen auf Vielseitigkeit, allerdings soll die Verallgemeinerung nicht mit zusätzlicher Laufzeit erkauft werden. Mit Introspektion und CTFE ist es möglich, eine Funktion zur Kompilierzeit zu spezialisieren, um so die beste Performance für gegebene Eingangstypen zu erreichen.

Ein gängiges Problem ist, dass die Länge eines Streams oder einer Liste, im Gegensatz zu z.B. einem Array, vor dem Durchschreiten nicht bekannt ist. Das folgende Beispiel stellt eine einfache Implementation der std.range-Methode walkLength dar, die das Durchschreiten für alle iterierbaren Typen verallgemeinert:

static if (hasMember!(r, "length"))
    return r.length; // O(1)
else
    return r.walkLength; // O(n)

commonPrefix

Die Verwendung von Introspektion zur Kompilierzeit ist in Phobos allgegenwärtig. Z.B. differenziert commonPrefix zwischen RandomAccessRanges und linear iterierbaren Ranges, da es in einer RandomAccessRange möglich ist zwischen Positionen zu springen und so den Algorithmus zu beschleunigen.

Weitere CTFE-Magie

std.traits umfasst die meisten von D's traits.

Spezielle Schlüsselwörter

Zusätzlich bietet D einige spezielle Schlüsselwörter für Debuggingzwecke:

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);
}

Mit D's CLI-Auswertung wird time nicht benötigt - CTFE kann benutzt werden!

rdmd --force --eval='pragma(msg, __TIMESTAMP__);'

Weiterführende Quellen

rdmd playground.d