I was interested to write a type validation macro, which only gives a warning if the type isn't an int/short/long or a pointer.
The trouble I have with this is that a pointer can be any kind of pointer.
#define INT_OR_POINTER_AS_UINTPTR(v) _Generic((v), \
signed long: (uintptr_t)(v), unsigned long: (uintptr_t)(v), \
signed int: (uintptr_t)(v), unsigned int: (uintptr_t)(v), \
signed short: (uintptr_t)(v), unsigned short: (uintptr_t)(v), \
default: (((void)(0 ? (*(v)) : 0), (uintptr_t)(v))))
The first block is to allow int/short/long
The default
case is to allow any pointer.
The intention of (0 ? (*(v)) : 0)
is to cause a compiler error if v
is not a pointer, but otherwise not effect the generated code (hance the 0 ? ...
).
This way, accidental inplicit casts from other types such as float
or bool
won't go un-noticed.
Ideally, this would work.
int a = 4;
struct Foo *b = NULL;
uintptr_t test_a = INT_OR_POINTER_AS_UINTPTR(a);
uintptr_t test_b = INT_OR_POINTER_AS_UINTPTR(b);
Ideally, and this would fail for both uses.
float a = 4;
struct Foo b = {0};
uintptr_t test_a = INT_OR_POINTER_AS_UINTPTR(a);
uintptr_t test_b = INT_OR_POINTER_AS_UINTPTR(b);
However, even when an int/long/short
is given as an argument, The code which checks a pointer is evaluated and errors: invalid type argument of unary '*' (have 'int')
Without having to explicitly enumerate every kind of pointer type which might be passed to this _Generic
is there a way to catch all kind of pointers, without evaluating the expression for other (non-pointer) values?