Niestety, w środowisku wieloplatformowym i kompilatorach wieloplatformowych nie ma jednej niezawodnej metody robienia tego wyłącznie w czasie kompilacji.
- Zarówno _WIN32 i _WIN64 czasami może zarówno być niezdefiniowana, jeśli ustawienia projektu są wadliwe lub uszkodzone (szczególnie w Visual Studio 2008 SP1).
- Projekt oznaczony jako „Win32” mógł być ustawiony na 64-bitowy z powodu błędu konfiguracji projektu.
- W programie Visual Studio 2008 z dodatkiem SP1 czasami funkcja Intellisense nie powoduje wyszarzenia poprawnych części kodu, zgodnie z bieżącym #define. To sprawia, że trudno jest zobaczyć, który #define jest używany w czasie kompilacji.
Dlatego jedyną niezawodną metodą jest połączenie 3 prostych kontroli :
- 1) ustawienie czasu kompilacji oraz;
- 2) sprawdzenie w czasie wykonywania oraz;
- 3) Solidne sprawdzanie czasu kompilacji .
Proste sprawdzenie 1/3: ustawienie czasu kompilacji
Wybierz dowolną metodę, aby ustawić wymaganą zmienną #define. Proponuję metodę z @JaredPar:
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
Proste sprawdzenie 2/3: sprawdzenie w czasie wykonywania
W main () dwukrotnie sprawdź, czy sizeof () ma sens:
#if defined(ENV64BIT)
if (sizeof(void*) != 8)
{
wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 64-bit mode.\n");
#elif defined (ENV32BIT)
if (sizeof(void*) != 4)
{
wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 32-bit mode.\n");
#else
#error "Must define either ENV32BIT or ENV64BIT".
#endif
Proste sprawdzenie 3/3: Solidne sprawdzanie czasu kompilacji
Ogólna zasada brzmi: „każdy #define musi kończyć się #else, który generuje błąd”.
#if defined(ENV64BIT)
// 64-bit code here.
#elif defined (ENV32BIT)
// 32-bit code here.
#else
// INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE.
// - What if I made a typo and checked for ENV6BIT instead of ENV64BIT?
// - What if both ENV64BIT and ENV32BIT are not defined?
// - What if project is corrupted, and _WIN64 and _WIN32 are not defined?
// - What if I didn't include the required header file?
// - What if I checked for _WIN32 first instead of second?
// (in Windows, both are defined in 64-bit, so this will break codebase)
// - What if the code has just been ported to a different OS?
// - What if there is an unknown unknown, not mentioned in this list so far?
// I'm only human, and the mistakes above would break the *entire* codebase.
#error "Must define either ENV32BIT or ENV64BIT"
#endif
Aktualizacja 2017-01-17
Komentarz od @AI.G
:
4 lata później (nie wiem, czy wcześniej było to możliwe) możesz przekonwertować sprawdzenie w czasie wykonywania na sprawdzenie kompilacji za pomocą statycznego assert: static_assert (sizeof (void *) == 4) ;. Teraz wszystko jest gotowe w czasie kompilacji :)
załącznik A
Nawiasem mówiąc, powyższe reguły można dostosować, aby cała baza kodu była bardziej niezawodna:
- Każda instrukcja if () kończy się „else”, które generuje ostrzeżenie lub błąd.
- Każda instrukcja switch () kończy się „default:”, które generuje ostrzeżenie lub błąd.
Powodem, dla którego to działa dobrze, jest to, że zmusza cię do przemyślenia każdego pojedynczego przypadku z wyprzedzeniem i nie polega na (czasem błędnej) logice w części „else”, aby wykonać poprawny kod.
Użyłem tej techniki (między innymi), aby napisać projekt 30 000 linii, który działał bezbłędnie od dnia pierwszego wdrożenia do produkcji (czyli 12 miesięcy temu).