Бях заинтересован да напиша макрос за валидиране на тип, който дава предупреждение само ако типът не е int/short/long или указател.
Проблемът, който имам с това, е, че указателят може да бъде всякакъв вид указател.
#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))))
Първият блок е да разрешите int/short/long
Случаят default
позволява всеки указател.
Намерението на (0 ? (*(v)) : 0)
е да причини грешка на компилатора, ако v
не е указател, но в противен случай да не повлияе на генерирания код (оттук 0 ? ...
).
По този начин случайни инплицитни прехвърляния от други типове като float
или bool
няма да останат незабелязани.
В идеалния случай това би свършило работа.
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);
В идеалния случай това би било неуспешно и за двете употреби.
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);
Въпреки това, дори когато int/long/short
е дадено като аргумент, кодът, който проверява указател, се оценява и грешки: invalid type argument of unary '*' (have 'int')
Без да се налага изрично изброяване на всеки тип указател, който може да бъде предаден на този _Generic
, има ли начин да се уловят всички видове указатели, без да се оценява изразът за други (не-указателни) стойности?