Implementacja referencyjna CRC32 oblicza tabeli odnośników w czasie wykonywania:
/* 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;
}
Czy możesz obliczyć tabelę w czasie kompilacji, pozbywając się w ten sposób funkcji i flagi statusu?
code-challenge
c++
compile-time
fredoverflow
źródło
źródło
Odpowiedzi:
Oto proste rozwiązanie C:
crc32table.c
Opiera się na niestandardowym
__COUNTER__
makrze, a także semantyce oceny, w której__COUNTER__
jest oceniany przed przekazaniem go jako argument do makra.Zauważ, że skoro
STEP
dwukrotnie ocenia swój argument iCRC
używa ośmiu jego zagnieżdżonych wywołań, rozmiar kodu jest niewielki w kombinacji:Przetestowałem to w GCC 4.6.0 i Clang 2.8 na 32-bitowym Linuksie i oba tworzą poprawną tabelę.
źródło
Pętla rdzenia
można przekształcić w meta-funkcję:
Następnie preprocesor generuje 256 wywołań tej meta-funkcji (dla inicjatora macierzy):
Jeśli masz zainstalowany Boost, generowanie inicjalizatora tablicy jest nieco prostsze:
Wreszcie następujący sterownik testowy po prostu drukuje wszystkie elementy tablicy w konsoli:
źródło
Rozwiązanie C ++ 0x
Działa na GCC (4.6.1) i Clang (trunk 134121).
źródło
C >> 1
: Czy przesunięcie wartości ujemnych na właściwe, nieokreślone zachowanie? ;)C
unsigned long
D...
D
static unsigned long constexpr crc_table[] = { D... };
compute<>::crc_table[I]
C ++ 0x z
constexpr
. Działa na GCC4.6.1Możesz wtedy użyć
crc_table.data[X]
w czasie kompilacji, ponieważcrc_table
jestconstexpr
.źródło
To jest mój pierwszy metaprogram :
„Zakodowałem” wywołania szablonu, który wykonuje obliczenia :)
źródło
times
szablonunsigned 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 ,
za pomocą preprocesora. Kompilacja zajmuje tyle samo czasu co moja. Jeśli chcesz, możesz przeczytać ten ostatni akapit jako „rozwinąłem zewnętrzną pętlę”. W C ++ 03 nie ma innego wyborure
Naprawdę zawstydza C ++, prawda?
źródło
eval
.C / C ++,
306295 bajtówPracując w odwrotnej kolejności, otrzymujemy niepodpisaną długą tablicę o nazwie crc_table. Możemy pominąć rozmiar tablicy, ponieważ makra zapewnią, że w tablicy jest dokładnie 256 elementów. Inicjujemy tablicę za pomocą 16 „wierszy” danych, używając 16 wywołań makra R.
Każde wywołanie R rozwija się do czterech fragmentów (makro F) czterech stałych (makro K), co daje 16 „kolumn” danych.
Makro K jest rozwiniętą pętlą indeksowaną przez k w kodzie z pierwotnego pytania. Aktualizuje wartość c osiem razy, wywołując makro C.
To rozwiązanie oparte na preprocesorze zużywa sporo pamięci podczas rozwijania makr. Próbowałem to zrobić nieco krócej, mając dodatkowy poziom ekspansji makr, a mój kompilator zwymiotował. Powyższy kod kompiluje się (powoli) zarówno z Visual C ++ 2012, jak i g ++ 4.5.3 pod Cygwin (Windows 7 64 bit 8 GB RAM).
Edytować:
Powyższy fragment ma 295 bajtów, w tym białe znaki. Po rozwinięciu wszystkich makr oprócz C rośnie do 9 918 bajtów. W miarę rozszerzania każdego poziomu makra C rozmiar szybko rośnie:
Do czasu rozwinięcia wszystkich makr ten mały plik 295 bajtów rozwija się do ponad 2,7 megabajtów kodu, który należy skompilować, aby wygenerować oryginalną tablicę 1024 bajtów (przy założeniu 32 bitowych długich wartości bez znaku)!
Kolejna edycja:
Zmodyfikowałem makro C na podstawie makra z innej odpowiedzi, aby wycisnąć dodatkowe 11 bajtów, i znacznie zmniejszyłem pełny rozmiar makra. Chociaż 2,7 MB nie jest tak złe jak 54 MB (poprzedni końcowy rozmiar wszystkich rozszerzeń makr), nadal jest znaczące.
źródło
Zmodyfikowałbym poprzednią odpowiedź, zastępując ostatnie trzy wiersze:
Gdzie crcByte jest jego makro K bez końcowego przecinka. Następnie zbuduj sam stół za pomocą:
I nigdy nie pomijaj rozmiaru tablicy, ponieważ kompilator sprawdzi, czy masz odpowiednią liczbę elementów.
źródło