Quiero escribir una función (o macro) que indique si una variable está firmada o no. Es decir, cómo saber si el tipo de variable dado está firmado o no, a diferencia de su valor .
Podrías crear una macro _Generic
:
#define is_signed(X) _Generic((X), \ short : true, \ int : true, \ long : true, \ long long : true, \ unsigned short : false, \ unsigned int : false, \ unsigned long : false, \ unsigned long long : false, \ float : true, \ double : true, \ long double : true \ )
También puede delegar funciones si desea hacer algo más complicado. Hice un ejemplo simple en el que is_signed
devuelve un objeto de signs
que incluye la firma tanto del type
como del value
de la variable proporcionada. He excluido los nombres de los parámetros donde no son necesarios, lo que estará permitido en el estándar C2x. Puede agregar nombres ficticios si lo desea.
typedef struct { bool type; bool value; } signs; signs Short(short x) { signs r={true, x < 0}; return r; } signs Int(int x) { signs r={true, x < 0}; return r; } signs Long(long x) { signs r={true, x < 0}; return r; } signs Longlong(long long x) { signs r={true, x < 0}; return r; } signs UShort(unsigned short) { signs r={false, false}; return r; } signs UInt(unsigned int) { signs r={false, false}; return r; } signs ULong(unsigned long) { signs r={false, false}; return r; } signs ULonglong(unsigned long long) { signs r={false, false}; return r; } signs Float(float x) { signs r={true, x < 0.f}; return r; } signs Double(double x) { signs r={true, x < 0.}; return r; } signs LongDouble(long double x) { signs r={true, x < 0.L}; return r; } #define is_signed(X) _Generic((X), \ short : Short, \ int : Int, \ long : Long, \ long long : Longlong, \ unsigned short : UShort, \ unsigned int : UInt, \ unsigned long : ULong, \ unsigned long long : ULonglong, \ float : Float, \ double : Double, \ long double : LongDouble \ )(X)
Puede hacer esto para los tipos listados explícitamente usando _Generic
:
#include <limits.h> #include <stdio.h> int main(void) { enum Signedness { Signed, Unsigned, Unknown }; char x; enum Signedness s = _Generic(x, char: CHAR_MIN < 0 ? Signed : Unsigned, signed char: Signed, unsigned char: Unsigned, short: Signed, unsigned short: Unsigned, int: Signed, unsigned: Unsigned, long: Signed, unsigned long: Unsigned, long long: Signed, unsigned long long: Unsigned, default: Unknown ); switch (s) { case Signed: printf("The type is signed.\n"); break; case Unsigned: printf("The type is unsigned.\n"); break; case Unknown: printf("It is not known whether the type is signed or unsigned.\n"); break; } }
Aquí hay una manera imperfecta de hacerlo:
#define isSigned(x) ((x) < 0 ? 1 : (-(x) < 0 ? 1 : 0))
Si el número se compara con menos de 0, su tipo obviamente está firmado. De lo contrario, es positivo, pero si su negación es menor que 0, nuevamente, fue firmado.
Pero, ¡ups!, no es tan simple, ¡porque también es posible que el valor sea 0! (Gracias, @JohnBollinger). Si es exactamente 0, entonces si x-1
es negativo, se firmó.
#define isSigned(x) ((x) < 0 ? 1 : (x) > 0 ? (-(x) < 0 ? 1 : 0) : ((x) - 1 < 0 ? 1 : 0))
(Desafortunadamente, la cláusula adicional para manejar el caso x == 0
lo lleva al borde de "apenas legible" a "casi completamente ofuscado").
En cualquier caso, esto supone que la variable tiene un valor (es decir, se ha inicializado).
Desafortunadamente, no funciona en absoluto para tipos más pequeños que int
, porque en ese caso, -x
es negativo incluso si x
no tiene signo.
Demostración:
int main() { char c = 1; unsigned char uc = 1; signed char sc = 1; int i = -1; unsigned u = 1; float f = 1; double d = -1; printf("char: %ssigned\n", isSigned(c) ? "" : "un"); printf("signed char: %ssigned\n", isSigned(sc) ? "" : "un"); printf("unsigned char: %ssigned\n", isSigned(uc) ? "" : "un"); printf("int: %ssigned\n", isSigned(i) ? "" : "un"); printf("unsigned: %ssigned\n", isSigned(u) ? "" : "un"); printf("float: %ssigned\n", isSigned(f) ? "" : "un"); printf("double: %ssigned\n", isSigned(d) ? "" : "un"); }