preprocesador - directivas en c++
Determinación de 32 vs 64 bit en C++ (13)
Estoy buscando una forma confiable de determinar si el código C ++ se está compilando en 32 vs 64 bit. Hemos encontrado lo que pensamos que es una solución razonable al usar macros, pero teníamos curiosidad por saber si la gente podía pensar en casos en los que esto podría fallar o si hay una mejor manera de hacerlo. Tenga en cuenta que estamos tratando de hacer esto en un entorno multiplataforma y compilador múltiple.
#if ((ULONG_MAX) == (UINT_MAX))
# define IS32BIT
#else
# define IS64BIT
#endif
#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif
Gracias.
Debería poder usar las macros definidas en stdint.h
. En particular, INTPTR_MAX
es exactamente el valor que necesita.
#include <cstdint>
#if INTPTR_MAX == INT32_MAX
#define THIS_IS_32_BIT_ENVIRONMENT
#elif INTPTR_MAX == INT64_MAX
#define THIS_IS_64_BIT_ENVIRONMENT
#else
#error "Environment not 32 or 64-bit."
#endif
Algunas (¿todas?) Versiones del compilador de Microsoft no vienen con stdint.h
. No estoy seguro de por qué, ya que es un archivo estándar. Aquí hay una versión que puede usar:
Desafortunadamente, en un entorno multiplataforma de compilador cruzado, no existe un único método confiable para hacerlo solo en tiempo de compilación.
- Ambos _WIN32 y _WIN64 a veces pueden estar indefinidos, si la configuración del proyecto es defectuosa o está dañada (especialmente en Visual Studio 2008 SP1).
- Un proyecto etiquetado como "Win32" podría configurarse en 64 bits, debido a un error de configuración del proyecto.
- En Visual Studio 2008 SP1, a veces el intellisense no atenúa las partes correctas del código, de acuerdo con el #define actual. Esto hace que sea difícil ver exactamente qué #define se está utilizando en tiempo de compilación.
Por lo tanto, el único método confiable es combinar 3 controles simples :
- 1) Configuración del tiempo de compilación , y;
- 2) comprobación de tiempo de ejecución , y;
- 3) Comprobación robusta del tiempo de compilación .
Verificación simple 1/3: configuración del tiempo de compilación
Elija cualquier método para establecer la variable #define requerida. Sugiero el método de @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
Comprobación simple 2/3: comprobación del tiempo de ejecución
En main (), comprueba dos veces si sizeof () tiene sentido:
#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
Comprobación simple 3/3: Comprobación robusta del tiempo de compilación
La regla general es "cada #define debe terminar en #else que genera un error".
#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
Actualización 2017-01-17
Comentario de @AI.G
:
4 años después (no sé si fue posible antes) puede convertir el control en tiempo de ejecución al tiempo de compilación utilizando static assert: static_assert (sizeof (void *) == 4) ;. Ahora todo está hecho en tiempo de compilación :)
Apéndice A
Incidentalmente, las reglas anteriores se pueden adaptar para hacer que toda su base de código sea más confiable:
- Cada instrucción if () termina en un "else" que genera una advertencia o error.
- Cada instrucción switch () finaliza en un "valor predeterminado:" que genera una advertencia o error.
La razón por la que esto funciona bien es porque te obliga a pensar en cada caso por adelantado y no confiar en la lógica (a veces defectuosa) en la parte "else" para ejecutar el código correcto.
Utilicé esta técnica (entre muchas otras) para escribir un proyecto de 30,000 líneas que funcionó impecablemente desde el día en que se implementó por primera vez en la producción (eso fue hace 12 meses).
El siguiente código funciona bien para la mayoría de los entornos actuales:
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define IS64BIT 1
#else
#define IS32BIT 1
#endif
Eso no funcionará en Windows para empezar. Longs y ints son ambos 32 bits ya sea que esté compilando para ventanas de 32 bits o de 64 bits. Creo que comprobar si el tamaño de un puntero es de 8 bytes es probablemente una ruta más confiable.
La gente ya sugirió métodos que intentarán determinar si el programa se está compilando en 32-bit
o 64-bit
.
Y quiero agregar que puede usar la característica c ++ 11 static_assert
para asegurarse de que la arquitectura es lo que cree que es ("relajar").
Entonces, en el lugar donde defines las macros:
#if ...
# define IS32BIT
static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is")
#elif ...
# define IS64BIT
static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is")
#else
# error "Cannot determine the Arch"
#endif
Lamentablemente, no existe una macro multiplataforma que defina 32/64 bits en los principales compiladores. He encontrado que la manera más efectiva de hacer esto es lo siguiente.
Primero elijo mi propia representación. Prefiero ENVIRONMENT64 / ENVIRONMENT32. Luego descubro qué hacen todos los compiladores principales para determinar si se trata de un entorno de 64 bits o no y lo uso para establecer mis variables.
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif
Otra ruta más fácil es simplemente establecer estas variables desde la línea de comandos del compilador.
Podrías hacer esto:
#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
Pondría las fuentes de 32 bits y de 64 bits en diferentes archivos y luego seleccioné los archivos fuente apropiados usando el sistema de compilación.
Si puede usar configuraciones de proyectos en todos sus entornos, eso facilitaría la definición de un símbolo de 64 y 32 bits. Entonces tendrías configuraciones de proyecto como esta:
Depuración de 32 bits
Versión de 32 bits
Depuración de 64 bits
Versión de 64 bits
EDITAR: Estas son configuraciones genéricas, no configuraciones dirigidas. Llámalos como quieras.
Si no puedes hacer eso, me gusta la idea de Jared.
Su enfoque no estaba muy lejos, pero solo está verificando si long
e int
son del mismo tamaño. Teóricamente, ambos podrían ser de 64 bits, en cuyo caso su comprobación fallaría, suponiendo que ambos son 32 bits. Aquí hay un cheque que realmente verifica el tamaño de los tipos en sí, no su tamaño relativo:
#if ((UINT_MAX) == 0xffffffffu)
#define INT_IS32BIT
#else
#define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
#define LONG_IS32BIT
#else
#define LONG_IS64BIT
#endif
En principio, puede hacer esto para cualquier tipo para el que tenga una macro definida por el sistema con el valor máximo.
Tenga en cuenta que el estándar requiere una long long
de al menos 64 bits, incluso en sistemas de 32 bits.
"Compilado en 64 bits" no está bien definido en C ++.
C ++ establece solo límites inferiores para tamaños como int, long y void *
. No hay garantía de que int sea de 64 bits, incluso cuando se compila para una plataforma de 64 bits. El modelo permite, por ejemplo, 23 bit int
s y sizeof(int *) != sizeof(char *)
Existen diferentes unix.org/version2/whatsnew/lp64_wp.html para plataformas de 64 bits.
Su mejor apuesta es una prueba específica de la plataforma. Su segunda mejor decisión portátil debe ser más específica en lo que es 64 bits.
Try this:
#ifdef _WIN64
// 64 bit code
#elif _WIN32
// 32 bit code
#else
if(sizeof(void*)==4)
// 32 bit code
else
// 64 bit code
#endif
template<int> void DoMyOperationHelper();
template<> void DoMyOperationHelper<4>()
{
// do 32-bits operations
}
template<> void DoMyOperationHelper<8>()
{
// do 64-bits operations
}
// helper function just to hide clumsy syntax
inline void DoMyOperation() { DoMyOperationHelper<sizeof(size_t)>(); }
int main()
{
// appropriate function will be selected at compile time
DoMyOperation();
return 0;
}