I'll bite, but I have to say it's more in the spirit of macro hacking, not because I think such a macro is useful. Here goes:
#include <stdlib.h>
#include <stdio.h>
#define TO_UNSIGNED(x) ( \
(sizeof(x) == 1) ? (unsigned char) (x) : \
(sizeof(x) == sizeof(short)) ? (unsigned short) (x) : \
(sizeof(x) == sizeof(int)) ? (unsigned int) (x) : \
(sizeof(x) == sizeof(long)) ? (unsigned long) (x) : \
(unsigned long long) (x) \
)
// Now put the macro to use ...
short minus_one_s()
{
return -1;
}
long long minus_one_ll()
{
return -1LL;
}
int main()
{
signed char c = -1;
short s = -1;
int i = -1;
long int l = -1L;
long long int ll = -1LL;
printf("%llx\n", (unsigned long long) TO_UNSIGNED(c));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(s));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(i));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(l));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(ll));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_s()));
printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_ll()));
return 0;
}
The macro uses the ternary comparison operator ?:
to emulate a switch statement for all known signed integer sizes. (This should catch the appropriate unsigned integers and the typedef
'd typed from <stdint.h>
, too. It works with expressions. It also accepts floats, although not quite as I'd expect.)
The somewhat convoluted printf
s show that the negative numbers are expanded to the native size of the source integer.
Edit: The OP is looking for a macro that returns an expression of the unsigned type of the same length as the source type. The above macro doesn't do that: Because the two alternative values of the ternary comparison are promoted to a common type, the result of the macro will always be the type of the greatest size, which is unsigned long long
.
Branches of different types could probably be achieved with a pure macro solution, such that after preprocessing, the compiler only sees one type, but the preprocessor doesn't know about types, so sizeof
cannot be used here, which rules out such a macro.
But to my (weak) defense, I'll say that if the value of the unsigned long long result of the macro is assigned to the appropriate unsigned type (i.e. unsigned short for short), the value should never be truncated, so the macro might have some use.
Edit II: Now that I've stumbled upon the C11 _Generic
keyword in another question (and have installed a compiler that supports it), I can present a working solution: The following macro really returns the correct value with the correct type:
#define TO_UNSIGNED(x) _Generic((x), \
char: (unsigned char) (x), \
signed char: (unsigned char) (x), \
short: (unsigned short) (x), \
int: (unsigned int) (x), \
long: (unsigned long) (x), \
long long: (unsigned long long) (x), \
default: (unsigned int) (x) \
)
The _Generic
selection is resolved at compile time and doesn't have the overhead of producing intermediate results in an oversized int type. (A real-world macro should probably include the unsigned types themself for a null-cast. Also note that I had to include signed char
explicitly, just char
didn't work, even though my chars are signed.)
It requires a recent compiler that implements C11 or at least its _Generic
keyword, which means this solution is not very portable, though, see here.