Sfortunatamente, in un ambiente cross-platform e cross-compilatore, non esiste un unico metodo affidabile per farlo puramente in fase di compilazione.
- A volte sia _WIN32 che _WIN64 possono essere entrambi indefiniti, se le impostazioni del progetto sono errate o danneggiate (in particolare su Visual Studio 2008 SP1).
- Un progetto con l'etichetta "Win32" potrebbe essere impostato su 64 bit, a causa di un errore di configurazione del progetto.
- Su Visual Studio 2008 SP1, a volte l'intellisense non oscura le parti corrette del codice, secondo l'attuale #define. Questo rende difficile vedere esattamente quale #define viene usato al momento della compilazione.
Pertanto, l' unico metodo affidabile è combinare 3 semplici controlli :
- 1) Impostazione del tempo di compilazione e;
- 2) controllo di runtime e;
- 3) Controllo del tempo di compilazione robusto .
Controllo semplice 1/3: impostazione del tempo di compilazione
Scegli un metodo per impostare la variabile #define richiesta. Suggerisco il metodo da @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
Controllo semplice 2/3: controllo runtime
In main (), controlla due volte se sizeof () ha senso:
#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
Controllo semplice 3/3: controllo del tempo di compilazione robusto
La regola generale è "ogni #define deve finire con un #else che genera un errore".
#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
Aggiornamento 2017-01-17
Commento di @AI.G
:
4 anni dopo (non so se fosse possibile prima) è possibile convertire il controllo di run-time in tempo di compilazione usando assert statico: static_assert (sizeof (void *) == 4) ;. Ora è tutto fatto in fase di compilazione :)
Appendice A
Per inciso, le regole sopra possono essere adattate per rendere più affidabile l'intera base di codice:
- Ogni istruzione if () termina con un "else" che genera un avviso o un errore.
- Ogni istruzione switch () termina con un "valore predefinito:" che genera un avviso o un errore.
Il motivo per cui funziona bene è che ti costringe a pensare in anticipo a ogni singolo caso e non fare affidamento sulla logica (a volte imperfetta) nella parte "else" per eseguire il codice corretto.
Ho usato questa tecnica (tra le altre cose) per scrivere un progetto di 30.000 linee che ha funzionato perfettamente dal giorno in cui è stato implementato per la prima volta in produzione (12 mesi fa).