I'm trying to build a simple SIGNOF
macro:
#define SIGNOF(a) ((a) < 0 ? -1 : 1)
If a
is negative it should return -1, otherwise 1. If a
is an unsigned type, it should always return 1 and the compiler can optimize away the negative code path.
However, GCC rightfully warns me that
error: comparison of unsigned expression in ‘< 0’ is always false [-Werror=type-limits]
29 | #define SIGNOF(a) ((a) < 0 ? -1 : 1)
But in this case I actually want this behavior. Is there any way to tell the compiler that this is intentional, similar to /* fall-though */
in a switch-case?
CodePudding user response:
If your compiler supports it, you can use _Generic
:
#define SIGNOF(a) _Generic(a, unsigned char: 1, \
unsigned short: 1, \
unsigned int: 1, \
unsigned long: 1, \
unsigned long long: 1, \
default: (a) < 0 ? -1 : 1)
CodePudding user response:
What works is
static inline int __signof(long long a)
{
return a < 0 ? -1 : 1;
}
#define SIGNOF(a) _Generic(a, unsigned char: 1, \
unsigned short: 1, \
unsigned int: 1, \
unsigned long: 1, \
unsigned long long: 1, \
default: __signof(a))
CodePudding user response:
This seems to fix the warning problem, at the expense of evaluating the operand twice:
#define SIGNOF(a) ((a) == 0 ? 1 : ((a) > 0) ? 1 : -1)
I observe that since the proposed DIV_ROUND()
macro evaluates both its arguments twice, it also has problems if the arguments have side effects (increments, function calls, etc).
CodePudding user response:
Time for a hideous workaround:
#define SIGNOF_(a) ((a) < 0 ? -1 : 1)
#ifdef __GNUC__
#define SIGNOF(a) ({ \
typeof(a) _a = (a); \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
int _r = SIGNOF_(_a); \
_Pragma("GCC diagnostic pop") \
_r; })
#else
#define SIGNOF(a) SIGNOF_(a)
#endif
It makes use of GNU C "statement expressions" (({ statements; })
) and the typeof
operator. The initialization _a = (a);
is to catch any -Wtype-limit
warnings in the macro parameter a
before the warning is temporarily disabled by the pragmas.