Un ejemplo excelente de la habilidad de D para generar código en tiempo de compilación mediante mixins es la manipulación de bits.
D ofrece los siguientes operadores para la manipulación a nivel de bit:
&
: 'y' a nivel de bit.
|
: 'o' a nivel de bit.
~
: negación a nivel de bit.
<<
: desplazamiento a la izquierda a nivel de bit.
>>
: desplazamiento a la derecha a nivel de bit manteniendo el signo de bit de mayor orden.
>>>
: desplazamiento a la derecha a nivel de bit sin mantener el signo.
Un ejemplo común de la manipulación de bits es la lectura de un valor de un
bit concreto. D proporciona la función core.bitop.bt
para las tareas más
comunes, sin embargo, para acostumbrarse a la manipulación de bits, se va
a mostrar una implementación detallada de cómo se comprueba el valor de un bit:
enum posA = 1;
enum maskA = (1 << posA);
bool getFieldA() {
return _data & maskA;
}
Una generalización es comprobar bloques que sean mayores de 1. Para ello se necesita una máscara especial con la longitud del bloque para luego desplazar dicho bloque según sea necesario antes de aplicar dicha máscara:
enum posA = 1;
enum lenA = 3;
enum maskA = (1 << lenA) - 1; // ...0111
uint getFieldA() {
return (_data >> posA) & maskA;
}
La configuración de un bloque se puede definir de forma equivalente negando la máscara para luego permitir únicamente escrituras dentro del bloque especificado.
void setFieldA(bool b) {
return (_data & ~maskAWrite) | ((b << aPos) & maskAWrite);
}
std.bitmanip
al rescateEs muy divertido escribir uno mismo el código para realizar manipulación de
bits, y D proporciona las herramientas para ello. Sin embargo en muchos casos
no se quiere hacer un copia/pega de este código de manipulación de bits ya que
es muy propenso a errores además de difícil de mantener. Es por esto que D
cuenta con el módulo std.bitmanip
y la potencia de los mixins, y todo ello
sin sacrificar el rendimiento.
Un ejemplo de cómo funcionan las funciones de este módulo se puede ver en el
ejercicio de esta sección, donde se define un BitVector
que todavía usa X
bits y es prácticamente indistinguible de una estructura normal.
Los módulos std.bitmanip
y core.bitop
contienen más funciones que son de
gran ayuda para aplicaciones que requiran bajo consumo de memoria.
Como el compilador añade relleno a variables con un tamaño menor que el diseño
de la memoria del sistema operativo (size_t.sizeof
), como por ejemplo con los
tipos bool
, byte
y char
, se recomienda empezar con los campos de mayor
tamaño para facilitar el alineamiento.