Traits

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.

Contrats explicites

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

Spécialisation

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 RandomAccessRanges 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.

Encore plus de magie CTFE

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;

Mot-clés

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__);'

Pour aller plus loin

rdmd playground.d