Мне было интересно написать макрос проверки типа, который выдает предупреждение только в том случае, если тип не является 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
, есть ли способ перехватить все типы указателей без оценки выражения для других (не указателей) значений?