Для этого вам нужно проверить, имеет ли параметр тип integer, и вам нужно проверить, является ли он типом или выражением.
Проверка, является ли параметр макроса, который может быть типом или выражением, целочисленного типа:
Это можно сделать с помощью _Generic
. Выражение _Generic
не может содержать два идентичных типа, поэтому его будет достаточно, если вы сравните только со всеми типами stdint.h. Так как они будут псевдонимами с целочисленными типами по умолчанию, но не будут конфликтовать друг с другом (как, например, int
и long
могли бы).
Теперь _Generic
не принимает тип в качестве операнда, поэтому вы должны настроить ввод, чтобы он всегда становился выражением.
Уловка, которую я только что изобрел, заключается в том, чтобы использовать неоднозначность между оператором скобок и оператором приведения и одновременно использовать неоднозначность между унарными + и бинарными + операторами.
Дано (x)+0
.
- Если
x
является типом, ()
становится оператором приведения, а +0
является унарным оператором сложения, применяемым к целочисленной константе.
- Если
x
является выражением, оно будет заключено в скобки, а затем +
является оператором двоичного сложения.
Так что вы можете сделать:
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
Это будет работать для всех целочисленных, символьных и плавающих типов, а также для указателей. Он не будет работать с типами struct / union (ошибка компилятора). Он не будет работать с void*
и, вероятно, не с NULL
(ошибка компилятора, невозможно выполнить арифметику указателей).
Проверка, является ли макрос-параметр, который может быть типом или выражением, выражением:
Это также можно сделать, используя тот же трюк, что и выше, используйте неоднозначность между различными операторами. Например:
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
- Если
x
является ненулевым целочисленным константным выражением, мы получаем 1 + 0 + 1 = 2
.
- Если
x
является константным выражением с нулевым целым числом, мы получаем 0 + 1 + 1 = 2
.
- Если
x
является типом, мы получаем !!(int)+!(int)+1
, что равно 0
. Оба + одинарные.
Это не делает разницы между числами с плавающей точкой и целыми числами, поэтому нам нужно объединить этот трюк с макросом IS_INT
.
Решение:
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
Завершите пример тестовыми случаями, напечатав 1, если целочисленное константное выражение, в противном случае 0:
#include <stdint.h>
#include <stdio.h>
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
#define test(arg) printf("%d %s\n", IS_INTCONSTEXPR(arg),(#arg))
int main (void)
{
test(42);
test(sizeof(int));
test(1+1);
test(int);
test(unsigned int);
test(42.0);
test(double);
test(uint32_t);
test(uint32_t*);
test(_Bool);
_Static_assert( !IS_INTCONSTEXPR(int), "" ); // OK, passed
_Static_assert( IS_INTCONSTEXPR(42), "" ); // OK, passed
return 0;
}
Выход:
1 42
1 sizeof(int)
1 1+1
0 int
0 unsigned int
0 42.0
0 double
0 uint32_t
0 uint32_t*
0 _Bool