Une des forces de D est son système d'évaluation de fonction à la compilation (CTFE). En le combinant avec de l'introspection, on peut écrire des programmes très génériques et très optimisés.
Les traits nous permettent de spécifier explicitement quel type de paramètres de schémas sont acceptés. Par exemple, separerEnMots
peut travailler avec n'importe quel type de string
:
S[] splitIntoWord(S)(S input)
if (isSomeString!S)
Cela s'applique également à d'autres types de paramètres de schémas et maSurcouche
peut s'assurer que la fonction passée en paramètre est une fonction appelable:
void myWrapper(alias f)
if (isCallable!f)
Nous allons examiner un exemple simple: commonPrefix
de std.algorithm.searching
, qui retourne le préfixe commun de deux ranges.
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)
Cela signifie que la fonction ne peut être appelée (et donc instanciée et compilée) si:
r1
est copiable (garanti par isForwardRange
)
r2
est itérable (garanti par isInputRange
)
pred
est appelable avec r1
et r2
en paramètres
r1
n'est pas une chaîne de caractères (char[]
, string
, wchar[]
ou wstring
) — pour la simplicité, puisqu'il y aurait peut être besoin de décodage
Beaucoup d'APIs veulent être les plus générales possibles, mais elles ne veulent pas que cette généralisation rende leur code plus lent à s'exécuter. Avec la puissance de l'introspection et de la CTFE, il est possible de spécialiser une méthode à la compilation pour obtenir les meilleures performances en fonction du type d'entrée.
Un problème courant consiste à différencier les tableaux dont on connait exactement la longueur, et les suites d'éléments que l'on doit parcourir pour en connaître la longueur.
Voilà une implémentation simple de la fonction walkLength
de std.range
qui s'applique à n'importe quel type itérable:
static if (hasMember!(r, "length"))
return r.length; // O(1)
else
return r.walkLength; // O(n)
commonPrefix
L'utilisation de l'introspection se retrouve partout dans Phobos. Par exemple, commonPrefix
fait la différence entre les RandomAccessRange
s et les objets sur lesquels on peut itérer linéairement, parce qu'avec une RandomAccessRange
, on peut passer d'une position à une autre, et donc accélerer l'algorithme.
std.traits englobe la majorité des traits de D, à l'exception de certains qui ne peuvent pas être englobés dans la librairie, comme compiles
par exemple, qui peut causer des erreurs de compilation:
__traits(compiles, obvious error - $%42); // false;
Pour aider au débugage, D fournit quelques mots clés spéciaux:
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);
}
Avec l'évaluation de code D en ligne de commande, on peut même jeter son horloge !
rdmd --force --eval='pragma(msg, __TIMESTAMP__);'