Una de las características más potentes de D es su sistema de evaluación de funciones en tiempo de compilación (CTFE). Gracias a este sistema y a la introspección, se pueden escribir tanto programas genéricos como conseguir grandes optimizaciones.
Los rasgos en D (traits en inglés) permiten especificar de forma explícita
qué tipos de parámetros se aceptan dentro de las funciones. Por ejemplo,
la función splitIntoWords
funciona con cualquier tipo de dato que sea una
cadena de caracteres (string
):
S[] splitIntoWord(S)(S input)
if (isSomeString!S)
Esto también se aplica a los parámetros de las plantillas, con lo que la
función myWrapper
se puede asegurar de que el símbolo que se le pasa como
argumento es una función que se puede llamar:
void myWrapper(alias f)
if (isCallable!f)
Se puede analizar, como ejemplo sencillo,
commonPrefix
,
del módulo std.algorithm.searching
, que devuelve el prefijo común de dos rangos:
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)
Esto significa que esta función sólo se puede llamar y sólo compila si:
r1
se puede guardar (se garantiza mediante isForwardRange
).
r2
es iterable (se garantiza mediante isInputRange
).
pred
se puede llamar con elementos de tipo r1
y r2
.
r1
no es una cadena estrecha (narrow string en inglés) (char[]
,string
, wchar
o wstring
). Esto se hace por simplicidad, ya que si no fuese así se necesitaría decodificación adicional.
Muchas API pretenden ser de propósito general. Sin embargo, estas no quieren que haya sobrecarga extra en tiempo de ejecución por culpa de esta generalización. Gracias a la potencia de la introspección y al CTFE, es posible especializar un método en tiempo de compilación para conseguir el mejor rendimiento dados sus parámetros de entrada.
Un problema común es que, al contrario que con los arrays, podría no conocerse
la longitud exacta de una ristra de datos o una lista antes de recorrerla.
Aquí tenemos una simple implementación del método walkLength
del módulo
std.range
generalizado para cualquier tipo de dato que sea iterable:
static if (hasMember!(r, "length"))
return r.length; // O(1)
else
return r.walkLength; // O(n)
commonPrefix
El uso de la introspección en tiempo de compilación es ubicua en Phobos. Por
ejemplo, la función commonPrefix
diferencia entre rangos de tipo
RandomAccessRange
y rangos donde la iteración es lineal. Eso es así porque
en un RandomAccessRange
se puede saltar entre posiciones con lo que se mejora
la velocidad del algoritmo.
El módulo std.traits engloba la
mayoría de la funcionalidad de los traits
de D, excepto para algunas construcciones de tipo compiles
, que llevarían a
un error de compilación inmediato:
__traits(compiles, obvious error - $%42); // false
Adicionalmente, con el propósito de depurar código, D proporciona un par de palabras reservadas especiales:
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);
}
Gracias a la funcionalidad de D de poder evaluar programas desde la línea de
comandos, no se necesita usar la función time
, ya que se puede usar CTFE.
rdmd --force --eval='pragma(msg, __TIMESTAMP__);'