A implementação de referência do CRC32 calcula uma tabela de pesquisa em tempo de execução:
/* 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;
}
Você pode calcular a tabela em tempo de compilação, eliminando a função e o sinalizador de status?
code-challenge
c++
compile-time
fredoverflow
fonte
fonte
Respostas:
Aqui está uma solução C simples:
crc32table.c
Ele se baseia na
__COUNTER__
macro fora do padrão , bem como em uma semântica de avaliação onde__COUNTER__
é avaliada antes de ser passada como argumento para uma macro.Observe que, como
STEP
avalia seu argumento duas vezes eCRC
usa oito invocações aninhadas, há uma pequena explosão combinatória no tamanho do código:Testei isso no GCC 4.6.0 e no Clang 2.8 no Linux de 32 bits e ambos produzem a tabela correta.
fonte
O loop principal
pode ser convertido em uma meta-função:
Em seguida, 256 chamadas para essa meta-função (para o inicializador de matriz) são geradas pelo pré-processador:
Se você possui o Boost instalado, gerar um inicializador de matriz é um pouco mais simples:
Por fim, o seguinte driver de teste simplesmente imprime todos os elementos da matriz no console:
fonte
Uma solução C ++ 0x
Trabalhos em GCC (4.6.1) e Clang (tronco 134121).
fonte
C >> 1
, não é a mudança de valores negativos para o comportamento não especificado correto? ;)C
aunsigned long
. A matriz constante é definida para ser inicializada pela expansão do pacoteD...
.D
é um pacote de parâmetros de modelo que não é do tipo. Uma vez que o GCC o suporta, também é possível declarar a matriz na classe comstatic unsigned long constexpr crc_table[] = { D... };
, mas o GCC ainda não analisa os inicializadores da classe. O benefício será quecompute<>::crc_table[I]
poderia ser usado em expressões constantes posteriormente no código.C ++ 0x com
constexpr
. Trabalhos em GCC4.6.1Você pode usar
crc_table.data[X]
em tempo de compilação porquecrc_table
éconstexpr
.fonte
Este é o meu primeiro metaprograma :
Eu "codifiquei" as chamadas para o modelo que faz o cálculo :)
fonte
times
modelounsigned 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 o pré-processador. Demora tanto tempo para compilar quanto o meu. Se você quiser, pode ler o último parágrafo como "Desenrolei o loop externo". Não há outra escolha em C ++ 03D
Realmente envergonha C ++, não é?
fonte
eval
.C / C ++,
306295bytesTrabalhando ao contrário, terminamos com uma matriz longa não assinada chamada crc_table. Podemos omitir o tamanho da matriz, pois as macros garantirão que haja exatamente 256 elementos na matriz. Inicializamos a matriz com 16 'linhas' de dados usando 16 invocações da macro R.
Cada chamada de R se expande em quatro fragmentos (macro F) de quatro constantes (macro K) para um total de 16 'colunas' de dados.
A macro K é o loop desenrolado indexado por k no código da pergunta original. Ele atualiza o valor c oito vezes chamando a macro C.
Essa solução baseada em pré-processador usa bastante memória durante a expansão da macro. Tentei torná-lo um pouco mais curto com um nível extra de expansão de macro e meu compilador vomitou. O código acima é compilado (lentamente) com o Visual C ++ 2012 e o g ++ 4.5.3 no Cygwin (Windows 7 de 64 bits e 8 GB de RAM).
Editar:
O fragmento acima tem 295 bytes, incluindo espaço em branco. Depois de expandir todas as macros, exceto C, ele cresce para 9.918 bytes. À medida que cada nível de macro C é expandido, o tamanho aumenta rapidamente:
Assim, quando todas as macros foram expandidas, esse pequeno arquivo de 295 bytes se expande para mais de 2,7 megabytes de código que devem ser compilados para gerar a matriz original de 1024 bytes (assumindo valores longos não assinados de 32 bits)!
Outra edição:
Modifiquei a macro C com base em uma macro de outra resposta para extrair 11 bytes extras e reduzi bastante o tamanho total da macro expandida. Embora 2,7 MB não sejam tão ruins quanto 54 MB (o tamanho final anterior de toda a expansão de macros), ainda é significativo.
fonte
Eu modificaria a resposta anterior substituindo as três últimas linhas por:
Onde crcByte é sua macro K sem a vírgula à direita. Em seguida, crie a própria tabela com:
E nunca deixe de fora o tamanho da matriz, pois o compilador verificará se você tem a quantidade correta de elementos.
fonte