Metaprogramación mediante plantillas

Si nunca se ha tenido contacto con la metaprogramación mediante plantillas (template meta programming en inglés) en C++, se puede sentir alivio gracias a las herramientas que ofrece D para hacer la vida más fácil. La metaprogramación mediante plantillas es una técnica que permite la toma de decisiones dependiendo del tipo de plantilla, permitiendo así hacer que los tipos de datos genéricos sean aún más flexibles basándose en el tipo de dato con el que van a ser instanciados.

Expresiones static if e is

Igual que un if normal, un static if compila un bloque de código de forma condicional según una condición que pueda ser evaluada en tiempo de compilación:

static if(is(T == int))
    writeln("T es un int");
static if (is(typeof(x) :  int))
    writeln("La variable x se convierte de forma implícita a int");

La expresión is es una expresión genérica de ayuda que evalúa condiciones en tiempo de compilación.

static if(is(T == int)) { // T es un parámetro de plantilla
    int x = 10;
}

Las llaves ({}) se omiten si la condición es verdadera (true), es decir, no se crea un ámbito nuevo. Si se quiere crear un ámbito nuevo, hay que hacerlo de forma explítica declarando un bloque mediante { { y } }.

La expresión static if se puede usar en cualquier parte del código: en funciones, en el ámbito global o dentro de definiciones de tipos.

Expresión mixin template

En cualquier sitio donde se vea código repetitivo (boiler plate en inglés), mixin template es tu amigo:

mixin template Foo(T) {
    T foo;
}
...
mixin Foo!int; // `int foo` disponible a partir de aquí

La expresión mixin template puede contener cualquier número de expresiones complejas que son insertadas en el punto donde se instancia la plantilla. Con esto se puede decir adiós a las directivas del preprocesador de C.

Restricciones de plantillas

Una plantilla se puede definir con cualquier número de restricciones. Estas restricciones fuerzan qué propiedades tiene que tener un tipo de dato:

void foo(T)(T value)
  if (is(T : int)) { // foo!T es sólo válida si T
                     // se puede convertir a `int`.
}

Las restricciones se pueden combinar mediante expresiones booleanas e, incluso, pueden contener llamadas a otras funciones que puedan ser evaluadas en tiempo de compilación. Por ejemplo, la función std.range.primitives.isRandomAccessRange comprueba si un tipo de dato es un rango que soporta el operador [].

En profundidad

Referencias básicas

Referencias avanzadas

rdmd playground.d