There was a discussion in the Linux kernel mailing list regarding a macro that tests whether its argument is an integer constant expression and is an integer constant expression itself.
One particularly clever approach that does not use builtins, proposed by Martin Uecker (taking inspiration from glibc's tgmath.h), is:
#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
This macro expands into an integer constant expression of value 1
if the argument is an integer constant expression, 0
otherwise. However, it relies on sizeof(void)
to be allowed (and different than sizeof(int)
), which is a GNU C extension.
Is it possible to write such a macro without builtins and without relying on language extensions? If yes, does it evaluate its argument?
For an explanation of the macro shown above, see instead: Linux Kernel's __is_constexpr Macro
Use the same idea, where the type of a ?:
expression depends on whether an argument is a null pointer constant or an ordinary void *
, but detect the type with _Generic
:
#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)
Demo on Ideone. _Generic
is a C11 addition, so if you're stuck on C99 or something earlier, you won't be able to use it.
Also, have standard links for the definition of a null pointer constant and the way null pointer constants interact with the type of a ?:
expression:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
and
If both the second and third operands are pointers or one is a null pointer constant and the other is a pointer, the result type is a pointer to a type qualified with all the type qualifiers of the types referenced by both operands. Furthermore, if both operands are pointers to compatible types or to differently qualified versions of compatible types, the result type is a pointer to an appropriately qualified version of the composite type; if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void.