L' implémentation de référence de CRC32 calcule une table de recherche au moment de l'exécution:
/* 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;
}
Pouvez-vous calculer la table au moment de la compilation, éliminant ainsi la fonction et l'indicateur d'état?
code-challenge
c++
compile-time
fredoverflow
la source
la source
Réponses:
Voici une solution simple en C:
crc32table.c
Il s'appuie sur la
__COUNTER__
macro non standard , ainsi que sur une sémantique d'évaluation où__COUNTER__
est évalué avant d'être passé comme argument à une macro.Notez que, puisque
STEP
évalue son argument deux fois et enCRC
utilise huit invocations imbriquées, il y a une petite explosion combinatoire dans la taille du code:J'ai testé cela dans GCC 4.6.0 et Clang 2.8 sur Linux 32 bits, et les deux produisent le bon tableau.
la source
La boucle centrale
peut être converti en une méta-fonction:
Ensuite, 256 appels à cette méta-fonction (pour l'initialiseur de tableau) sont générés par le préprocesseur:
Si Boost est installé, la génération de l'initialiseur de tableau est un peu plus simple:
Enfin, le pilote de test suivant imprime simplement tous les éléments du tableau sur la console:
la source
Une solution C ++ 0x
Fonctionne sur GCC (4.6.1) et Clang (trunk 134121).
la source
C >> 1
, N'est-ce pas déplacer des valeurs négatives vers le bon comportement non spécifié? ;)C
ununsigned long
. Le tableau de constantes est défini pour être initialisé par l'extension du packD...
.D
est un pack de paramètres de modèle non type. Une fois que GCC le prend en charge, on peut également déclarer le tableau en classe avecstatic unsigned long constexpr crc_table[] = { D... };
, mais GCC n'analyse pas encore les initialiseurs en classe contreventés. L'avantage sera qu'ilcompute<>::crc_table[I]
pourrait être utilisé dans des expressions constantes plus tard dans le code.C ++ 0x avec
constexpr
. Fonctionne sur GCC4.6.1Vous pouvez ensuite utiliser
crc_table.data[X]
au moment de la compilation carcrc_table
estconstexpr
.la source
Ceci est mon premier métaprogramme :
J'ai "codé en dur" les appels au modèle qui fait le calcul :)
la source
times
modèleunsigned 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 ,
utilisant le préprocesseur. Il faut environ autant de temps pour compiler que le mien. Si vous le souhaitez, vous pouvez lire ce dernier paragraphe comme "J'ai déroulé la boucle extérieure". Il n'y a pas d'autre choix en C ++ 03ré
Cela fait vraiment honte au C ++, n'est-ce pas?
la source
eval
.C / C ++,
306295 octetsEn travaillant en sens inverse, nous nous retrouvons avec un long tableau non signé nommé crc_table. Nous pouvons omettre la taille du tableau car les macros garantiront qu'il y a exactement 256 éléments dans le tableau. Nous initialisons le tableau avec 16 «lignes» de données en utilisant 16 invocations de la macro R.
Chaque invocation de R se développe en quatre fragments (macro F) de quatre constantes (macro K) pour un total de 16 «colonnes» de données.
La macro K est la boucle déroulée indexée par k dans le code de la question d'origine. Il met à jour la valeur c huit fois en appelant la macro C.
Cette solution basée sur un préprocesseur utilise beaucoup de mémoire lors de l'expansion des macros. J'ai essayé de le raccourcir un peu en ayant un niveau supplémentaire d'expansion de macro et mon compilateur a vomi. Le code ci-dessus se compile (lentement) avec Visual C ++ 2012 et g ++ 4.5.3 sous Cygwin (Windows 7 64 bits 8 Go de RAM).
Éditer:
Le fragment ci-dessus fait 295 octets, espace blanc compris. Après avoir développé toutes les macros sauf C, il passe à 9 918 octets. Au fur et à mesure que chaque niveau de macro C est développé, la taille augmente rapidement:
Donc, au moment où toutes les macros ont été développées, ce petit fichier de 295 octets se développe en plus de 2,7 mégaoctets de code qui doivent être compilés pour générer le tableau d'origine de 1024 octets (en supposant des valeurs longues non signées 32 bits)!
Un autre montage:
J'ai modifié la macro C sur la base d'une macro d'une autre réponse pour extraire 11 octets supplémentaires et j'ai considérablement réduit la taille de la macro étendue. Bien que 2,7 Mo ne soit pas aussi mauvais que 54 Mo (la taille finale précédente de toutes les extensions de macro), il est toujours important.
la source
Je modifierais la réponse précédente en remplaçant les trois dernières lignes par:
Où crcByte est sa macro K sans la virgule de fin. Créez ensuite la table elle-même avec:
Et ne laissez jamais de côté la taille du tableau car le compilateur vérifiera alors que vous avez la bonne quantité d'éléments.
la source