what uint8_t uint16_t uint16 c++ gcc cstdint

uint8_t - C++: int largo int frente int largo frente int64_t



uint8_t use (3)

Experimenté un comportamiento extraño al usar rasgos de tipo C ++ y reduje mi problema a este peculiar problema por el cual daré un montón de explicaciones ya que no quiero dejar nada abierto para una interpretación errónea.

Digamos que tienes un programa como ese:

#include <iostream> #include <cstdint> template <typename T> bool is_int64() { return false; } template <> bool is_int64<int64_t>() { return true; } int main() { std::cout << "int:/t" << is_int64<int>() << std::endl; std::cout << "int64_t:/t" << is_int64<int64_t>() << std::endl; std::cout << "long int:/t" << is_int64<long int>() << std::endl; std::cout << "long long int:/t" << is_int64<long long int>() << std::endl; return 0; }

Tanto en compilación de 32 bits con GCC (y con MSVC de 32 y 64 bits), la salida del programa será:

int: 0 int64_t: 1 long int: 0 long long int: 1

Sin embargo, el programa resultante de una compilación GCC de 64 bits generará:

int: 0 int64_t: 1 long int: 1 long long int: 0

Esto es curioso, ya que long long int es un entero de 64 bits con signo y es, para todos los efectos, idéntico a los tipos long int e int64_t , por lo que lógicamente, int64_t , long int long long int y long int long long int serían tipos equivalentes: el el conjunto generado al usar estos tipos es idéntico. Una mirada a stdint.h me dice por qué:

# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif

En una compilación de 64 bits, int64_t es long int , no long long int (obviamente).

La solución para esta situación es bastante fácil:

#if defined(__GNUC__) && (__WORDSIZE == 64) template <> bool is_int64<long long int>() { return true; } #endif

Pero esto es terriblemente hackish y no escala bien (funciones reales de la sustancia, uint64_t , etc.). Entonces mi pregunta es: ¿hay alguna manera de decirle al compilador que una long long int es también una int64_t , al igual que long int is?

Mi idea inicial es que esto no es posible, debido a la forma en que funcionan las definiciones de tipo C / C ++. No hay una manera de especificar la equivalencia de tipo de los tipos de datos básicos para el compilador, ya que ese es el trabajo del compilador (y permitir que eso pueda romper muchas cosas) y typedef solo va en una dirección.

Tampoco estoy demasiado preocupado por obtener una respuesta aquí, ya que este es un caso extremo que no sospecho que a nadie le importará cuando los ejemplos no sean horriblemente artificiales (¿eso significa que debería ser una wiki comunitaria?) .

Adjuntar : La razón por la que estoy usando la especialización de plantilla parcial en lugar de un ejemplo más fácil como:

void go(int64_t) { } int main() { long long int x = 2; go(x); return 0; }

es que dicho ejemplo todavía se compilará, ya que long long int es implícitamente convertible a int64_t .

Agregar : la única respuesta hasta ahora supone que quiero saber si un tipo es de 64 bits. No quería engañar a la gente para que pensara que eso me importa y probablemente debería haber proporcionado más ejemplos de dónde se manifiesta este problema.

template <typename T> struct some_type_trait : boost::false_type { }; template <> struct some_type_trait<int64_t> : boost::true_type { };

En este ejemplo, some_type_trait<long int> será un boost::true_type , pero some_type_trait<long long int> no lo será. Si bien esto tiene sentido en la idea de tipos de C ++, no es deseable.

Otro ejemplo es usar un calificador como same_type (que es bastante común para usar en C ++ 0x Concepts):

template <typename T> void same_type(T, T) { } void foo() { long int x; long long int y; same_type(x, y); }

Ese ejemplo no compila, ya que C ++ (correctamente) ve que los tipos son diferentes. g ++ no compilará con un error como: no hay función coincidente llamada same_type(long int&, long long int&) .

Me gustaría hacer hincapié en que entiendo por qué sucede esto, pero estoy buscando una solución alternativa que no me obligue a repetir el código por todos lados.


Entonces mi pregunta es: ¿hay alguna manera de decirle al compilador que una int larga larga es también una int64_t, al igual que long int is?

Esta es una buena pregunta o problema, pero sospecho que la respuesta es NO.

Además, una long int puede no ser una long long int .

# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif

Creo que esto es libc. Sospecho que quieres profundizar.

Tanto en compilación de 32 bits con GCC (y con MSVC de 32 y 64 bits), la salida del programa será:

int: 0 int64_t: 1 long int: 0 long long int: 1

Linux de 32 bits utiliza el modelo de datos ILP32. Los enteros, longs y punteros son de 32 bits. El tipo de 64 bits es de long long .

Microsoft documenta los rangos en los rangos de tipos de datos . El decir long long es equivalente a __int64 .

Sin embargo, el programa resultante de una compilación GCC de 64 bits generará:

int: 0 int64_t: 1 long int: 1 long long int: 0

Linux de 64 bits usa el modelo de datos LP64 . Longs son de 64 bits y long long son de 64 bits. Al igual que con 32 bits, Microsoft documenta los rangos en los rangos de tipos de datos y long long sigue siendo __int64 .

Hay un modelo de datos ILP64 donde todo es de 64 bits. Tienes que hacer un trabajo extra para obtener una definición para tu tipo de word32 . También vea documentos como Modelos de programación de 64 bits: ¿Por qué LP64?

Pero esto es terriblemente hackish y no escala bien (funciones reales de la sustancia, uint64_t, etc.) ...

Sí, se pone aún mejor. GCC mezcla y combina declaraciones que se supone que toman tipos de 64 bits, por lo que es fácil meterse en problemas aunque sigas un modelo de datos en particular. Por ejemplo, lo siguiente causa un error de compilación y le dice que use -fpermissive :

#if __LP64__ typedef unsigned long word64; #else typedef unsigned long long word64; #endif // intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864) // extern int _rdrand64_step(unsigned __int64 *random_val); // Try it: word64 val; int res = rdrand64_step(&val);

En resultado de:

error: invalid conversion from `word64* {aka long unsigned int*}'' to `long long unsigned int*''

Por lo tanto, ignore LP64 y cámbielo a:

typedef unsigned long long word64;

Luego, pase a un dispositivo ARM IoT de 64 bits que define LP64 y usa NEON:

error: invalid conversion from `word64* {aka long long unsigned int*}'' to `uint64_t*''


¿Desea saber si un tipo es del mismo tipo que int64_t o desea saber si algo tiene 64 bits? En función de su solución propuesta, creo que está preguntando sobre esto último. En ese caso, haría algo así como

template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64


No necesita ir a 64 bits para ver algo como esto. Considere int32_t en int32_t comunes de 32 bits. Puede ser typedef como int o como un long , pero obviamente solo uno de los dos a la vez. int y long son por supuesto distintos tipos.

No es difícil ver que no hay una solución que haga int == int32_t == long en sistemas de 32 bits. Por la misma razón, no hay forma de hacer long == int64_t == long long en sistemas de 64 bits.

Si pudieras, las posibles consecuencias serían bastante dolorosas para el código que sobrecargó foo(int) , foo(long) y foo(long long) - ¡de repente tendrían dos definiciones para la misma sobrecarga?

La solución correcta es que su código de plantilla generalmente no debe depender de un tipo preciso, sino de las propiedades de ese tipo. Toda la lógica de same_type aún podría estar bien para casos específicos:

long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);

Es decir, la sobrecarga foo(int64_t) no está definida cuando es exactamente igual a foo(long) .

[editar] Con C ++ 11, ahora tenemos una forma estándar de escribir esto:

long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);