Die Referenzimplementierung von CRC32 berechnet zur Laufzeit eine Nachschlagetabelle:
/* 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;
}
Können Sie die Tabelle zur Kompilierungszeit berechnen und so die Funktion und das Statusflag entfernen?
code-challenge
c++
compile-time
fredoverflow
quelle
quelle
Antworten:
Hier ist eine einfache C-Lösung:
crc32table.c
Es stützt sich auf das nicht standardmäßige
__COUNTER__
Makro sowie auf eine Auswertungssemantik, die__COUNTER__
ausgewertet wird, bevor sie als Argument an ein Makro übergeben wird.Beachten Sie, dass, da
STEP
das Argument zweimal ausgewertet wird undCRC
acht verschachtelte Aufrufe verwendet werden, die Codegröße eine kleine kombinatorische Explosion aufweist:Ich habe dies in GCC 4.6.0 und Clang 2.8 unter 32-Bit-Linux getestet und beide haben die richtige Tabelle erstellt.
quelle
Die Kernschleife
kann in eine Meta-Funktion umgewandelt werden:
Dann werden 256 Aufrufe dieser Meta-Funktion (für den Array-Initialisierer) vom Präprozessor generiert:
Wenn Sie Boost installiert haben, ist das Generieren des Array-Initialisierers etwas einfacher:
Schließlich druckt der folgende Testtreiber einfach alle Array-Elemente auf die Konsole:
quelle
Eine C ++ 0x Lösung
Funktioniert mit GCC (4.6.1) und Clang (Trunk 134121).
quelle
C >> 1
wird nicht negative Werte auf der rechten Seite nicht näher bezeichnete Verhalten Verschiebung? ;)C
einunsigned long
. Das konstante Array wird so definiert, dass es durch die Pack-Erweiterung initialisiert wirdD...
.D
ist ein nicht typisiertes Vorlagenparameterpaket. Sobald GCC es unterstützt, kann man das Array auch mit deklarierenstatic unsigned long constexpr crc_table[] = { D... };
, aber GCC parst noch keine geschweiften Initialisierer in der Klasse. Der Vorteil ist, dass ercompute<>::crc_table[I]
später im Code in konstanten Ausdrücken verwendet werden kann.C ++ 0x mit
constexpr
. Funktioniert mit GCC4.6.1Sie können dann
crc_table.data[X]
zur Kompilierungszeit verwenden , weilcrc_table
istconstexpr
.quelle
Dies ist mein erstes Metaprogramm :
Ich habe die Aufrufe der Vorlage, die die Berechnung durchführt, "hardcodiert" :)
quelle
times
Vorlageunsigned 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 ,
mit dem Präprozessor. Das Kompilieren dauert ungefähr so lange wie meins. Wenn Sie möchten, können Sie den letzten Absatz als "Ich habe die äußere Schleife abgerollt" lesen. Es gibt keine andere Wahl in C ++ 03D
C ++ wird wirklich beschämt, nicht wahr?
quelle
eval
.C / C ++,
306295 BytesBei umgekehrter Arbeit erhalten wir ein vorzeichenloses langes Array mit dem Namen crc_table. Wir können die Größe des Arrays weglassen, da die Makros sicherstellen, dass das Array genau 256 Elemente enthält. Wir initialisieren das Array mit 16 'Datenzeilen', indem wir 16 Aufrufe des Makros R verwenden.
Jeder Aufruf von R expandiert in vier Fragmente (Makro F) von vier Konstanten (Makro K) für insgesamt 16 "Datenspalten".
Das Makro K ist die entrollte Schleife, die im Code der ursprünglichen Frage mit k indiziert ist. Der Wert c wird achtmal aktualisiert, indem das Makro C aufgerufen wird.
Diese auf Präprozessoren basierende Lösung beansprucht während der Makro-Erweiterung ziemlich viel Speicher. Ich habe versucht, es ein wenig kürzer zu machen, indem ich eine zusätzliche Stufe der Makro-Erweiterung hatte und mein Compiler puked. Der obige Code wird (langsam) mit Visual C ++ 2012 und g ++ 4.5.3 unter Cygwin (Windows 7 64-Bit 8 GB RAM) kompiliert.
Bearbeiten:
Das obige Fragment enthält 295 Bytes einschließlich Leerzeichen. Nachdem alle Makros mit Ausnahme von C erweitert wurden, wächst sie auf 9.918 Byte. Mit jeder Ebene des C-Makros wächst die Größe schnell:
Wenn also alle Makros erweitert wurden, wird diese kleine 295-Byte-Datei zu über 2,7 Megabyte Code erweitert, der kompiliert werden muss, um das ursprüngliche 1024-Byte-Array zu generieren (unter der Annahme von vorzeichenlosen 32-Bit-Long-Werten)!
Eine andere Bearbeitung:
Ich habe das C-Makro basierend auf einem Makro aus einer anderen Antwort modifiziert, um zusätzliche 11 Bytes herauszupressen, und die vollständig erweiterte Makrogröße stark reduziert. 2,7 MB sind zwar nicht so schlimm wie 54 MB (die vorherige endgültige Größe aller Makroerweiterungen), sie sind jedoch immer noch von Bedeutung.
quelle
Ich würde die vorherige Antwort ändern, indem ich die letzten drei Zeilen durch Folgendes ersetze:
Wobei crcByte sein K-Makro ohne das nachstehende Komma ist. Dann bauen Sie die Tabelle selbst mit:
Lassen Sie niemals die Größe des Arrays aus, da der Compiler dann überprüft, ob Sie über die richtige Anzahl von Elementen verfügen.
quelle