cmp_int
cmp_int copied to clipboard
does not work for extended or bit-precise integer types
The case listing in the macros is quite restrictive. I think better would be something like
#define is_signed(X) \
_Generic((X), \
bool: 0, \
char: (char)-1 < 0, \
signed char: 1, \
signed short 1, \
unsigned char: 0, \
unsigned short: 0, \
default: (0 ? (X)%1 : -1) < 0 \
)
That ensures to work also for __int128
or other such wild animals if they exist, and to give the expected answer for char
if the user has a concept of "could hold negative values" for being a signed type.
The %1
trick also ensures that this targets exactly integer types, for all others this expression is a constraint violation. So there would be no need of the static_assert
part.
I tried the following:
#define is_signed(x) (_Generic((x), \
_Bool: 0, \
char: (char)-1 < 0, \
signed char: 1, \
signed short: 1, \
unsigned char: 0, \
unsigned short: 0, \
default: (0 ? (x) % 1 : -1) < 0))
Unfortunately this breaks down for the following test cases:
puts(cmp_equal(-5, (unsigned)5) ? "true" : "false");
Casting the value to unsigned generates all sorts of compiler warnings:
<source>: In function 'main':
<source>:52:28: warning: operand of '?:' changes signedness from 'int' to 'unsigned int' due to unsignedness of other operand [-Wsign-compare]
52 | default: (0 ? (x) % 1 : -1) < 0))
| ^~
<source>:92:24: note: in expansion of macro 'is_signed'
92 | (is_signed(lhs) == is_signed(rhs) ? (lhs) == (rhs) : \
| ^~~~~~~~~
<source>:125:10: note: in expansion of macro 'cmp_equal'
125 | puts(cmp_equal(-5, (unsigned)5) ? "true" : "false");
| ^~~~~~~~~
<source>:52:32: warning: comparison of unsigned expression in '< 0' is always false [-Wtype-limits]
52 | default: (0 ? (x) % 1 : -1) < 0))
| ^
<source>:92:24: note: in expansion of macro 'is_signed'
92 | (is_signed(lhs) == is_signed(rhs) ? (lhs) == (rhs) : \
| ^~~~~~~~~
<source>:125:10: note: in expansion of macro 'cmp_equal'
125 | puts(cmp_equal(-5, (unsigned)5) ? "true" : "false");
| ^~~~~~~~~
Compiler returned: 0
or any unsigned type:
unsigned int ui = 5;
puts(cmp_equal(-5, ui) ? "true" : "false");
ah, what a crap ;-) I usually avoid to switch this kind of warning on, because I use unsigned types as they are supposed to be. You may get away with this by introducing an extra macro for readability
#define __convertto(X, C) (0 ? (X) : (C))
that you then only use with non-negative constants
the default
expression then becomes
((-__convertto(X, 1)) < __convertto(X, 0))
The warning for "always false" is even crappier. That kind of warning is really not constructive if you are doing macro programming.