cmp_int icon indicating copy to clipboard operation
cmp_int copied to clipboard

does not work for extended or bit-precise integer types

Open gustedt opened this issue 3 years ago • 3 comments

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.

gustedt avatar Nov 09 '21 14:11 gustedt

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.

gustedt avatar Nov 09 '21 19:11 gustedt

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");

rcseacord avatar Nov 10 '21 04:11 rcseacord

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.

gustedt avatar Nov 10 '21 09:11 gustedt