La implementación de referencia de CRC32 calcula una tabla de búsqueda en tiempo de ejecución:
/* Table of CRCs of all 8-bit messages. */
unsigned long crc_table[256];
/* Flag: has the table been computed? Initially false. */
int crc_table_computed = 0;
/* Make the table for a fast CRC. */
void make_crc_table(void)
{
unsigned long c;
int n, k;
for (n = 0; n < 256; n++) {
c = (unsigned long) n;
for (k = 0; k < 8; k++) {
if (c & 1) {
c = 0xedb88320L ^ (c >> 1);
} else {
c = c >> 1;
}
}
crc_table[n] = c;
}
crc_table_computed = 1;
}
¿Se puede calcular la tabla en tiempo de compilación, eliminando así la función y el indicador de estado?
code-challenge
c++
compile-time
flujo libre
fuente
fuente
Respuestas:
Aquí hay una solución C simple:
crc32table.c
Se basa en la
__COUNTER__
macro no estándar , así como en una semántica de evaluación donde__COUNTER__
se evalúa antes de pasarla como argumento a una macro.Tenga en cuenta que, dado que
STEP
evalúa su argumento dos veces, yCRC
usa ocho invocaciones anidadas de él, hay una pequeña explosión combinatoria en el tamaño del código:Probé esto en GCC 4.6.0 y Clang 2.8 en Linux de 32 bits, y ambos producen la tabla correcta.
fuente
El bucle central
se puede convertir en una metafunción:
Luego, el preprocesador genera 256 llamadas a esta metafunción (para el inicializador de matriz):
Si tiene instalado Boost, generar el inicializador de matriz es un poco más simple:
Finalmente, el siguiente controlador de prueba simplemente imprime todos los elementos de la matriz en la consola:
fuente
Una solución C ++ 0x
Funciona en GCC (4.6.1) y Clang (trunk 134121).
fuente
C >> 1
, ¿no está cambiando los valores negativos al comportamiento correcto no especificado? ;)C
aunsigned long
. La matriz constante está definida para ser inicializada por la expansión del paqueteD...
.D
es un paquete de parámetros de plantilla que no es de tipo. Una vez que GCC lo admite, también se puede declarar la matriz en clase constatic unsigned long constexpr crc_table[] = { D... };
, pero GCC aún no analiza los inicializadores en clase. El beneficio será quecompute<>::crc_table[I]
podría usarse dentro de expresiones constantes más adelante en el código.C ++ 0x con
constexpr
. Funciona en GCC4.6.1Luego puede usar
crc_table.data[X]
en tiempo de compilación porquecrc_table
esconstexpr
.fuente
Este es mi primer metaprograma :
"Codifiqué" las llamadas a la plantilla que hace el cálculo :)
fuente
times
plantillaunsigned crc_table[] = { f<0>::value , f<0 + 1>::value , f<0 + 2>::value , f<0 + 2 + 1>::value , f<0 + 4>::value , f<0 + 4 + 1>::value , f<0 + 4 + 2>::value , f<0 + 4 + 2 + 1>::value , f<0 + 8>::value ,
usando el preprocesador. Toma casi tanto tiempo compilar como el mío. Si lo desea, puede leer el último párrafo como "Desenrollé el bucle externo". No hay otra opción en C ++ 03re
Realmente avergüenza a C ++, ¿no?
fuente
eval
.C / C ++,
306295 bytesTrabajando en reversa, terminamos con una matriz larga sin signo llamada crc_table. Podemos omitir el tamaño de la matriz ya que las macros se asegurarán de que haya exactamente 256 elementos en la matriz. Inicializamos la matriz con 16 'filas' de datos utilizando 16 invocaciones de la macro R.
Cada invocación de R se expande en cuatro fragmentos (macro F) de cuatro constantes (macro K) para un total de 16 'columnas' de datos.
La macro K es el bucle desenrollado indexado por k en el código de la pregunta original. Actualiza el valor c ocho veces invocando la macro C.
Esta solución basada en un preprocesador utiliza bastante memoria durante la expansión de macro. Traté de hacerlo un poco más corto al tener un nivel adicional de expansión de macro y mi compilador vomitó. El código anterior se compila (lentamente) con Visual C ++ 2012 y g ++ 4.5.3 en Cygwin (Windows 7 64 bit 8GB RAM).
Editar:
El fragmento anterior es de 295 bytes, incluido el espacio en blanco. Después de expandir todas las macros, excepto C, crece a 9.918 bytes. A medida que se expande cada nivel de macro C, el tamaño crece rápidamente:
Entonces, para cuando se hayan expandido todas las macros, ¡ese pequeño archivo de 295 bytes se expande en más de 2.7 megabytes de código que se debe compilar para generar la matriz original de 1024 bytes (suponiendo valores largos sin signo de 32 bits)!
Otra edición:
Modifiqué la macro C basada en una macro de otra respuesta para exprimir 11 bytes adicionales y reduje en gran medida el tamaño de la macro expandida completa. Si bien 2.7 MB no es tan malo como 54 MB (el tamaño final anterior de toda expansión macro), sigue siendo significativo.
fuente
Modificaría la respuesta anterior reemplazando las últimas tres líneas con:
Donde crcByte es su macro K sin la coma final. Luego construya la tabla en sí con:
Y nunca omita el tamaño de la matriz, ya que el compilador verificará que tiene la cantidad correcta de elementos.
fuente