Прежде всего, вот оно, полностью стандартное решение C11:
#include <stdint.h>
#include <assert.h>
#define is_integral_constant_p(c) (_Generic(0 ? (void*)(uintptr_t)((c) - (c)) : (int*)(0), \
int*: 1, \
default: 0))
#define foo(x, y) (is_integral_constant_p(x) ? real_foo : error_foo_x_is_not_a_constant_expression)(x, y)
extern int error_foo_x_is_not_a_constant_expression(int, int);
int real_foo(int x, int y)
{
assert(x < 10);
return x+y;
}
int main(void)
{
static_assert(is_integral_constant_p(5), "5 is not a constant expression?");
foo(5, 1);
// int x = 0;
// foo(x, 1); // This will not link
}
Волшебство заключается в том, как работает условное выражение.Если один из операндов является константой нулевого указателя, тип результата является типом другого аргумента указателя.В противном случае, если один аргумент равен void*
, результат равен void*
.Поскольку (void*)(0)
является константой нулевого указателя, второй операнд является константой нулевого указателя, только когда (c) - (c)
является выражением целочисленной константы, равным 0
.Это происходит тогда, когда c
является целочисленным константным выражением.
_Generic
просто выбирает константу в зависимости от типа результата, который мы ожидаем равным либо int*
в истинном случае, либо void*
иначе.Более того, сам макрос оценивается как целочисленное константное выражение (и поэтому может даже использоваться в assert
).
Выше приведена адаптация метода, используемого в ядре Linux (как обсуждалось ).здесь ), только без специального расширения GNU до C. Используются только функции C11.
Теперь мы реализуем foo
в качестве макроса, который проверяет x
, и пересылаем в"real_foo", который выполняет вычисления, или функции, которая объявлена, но не определена.Таким образом, программа не может установить связь, если x
не является константой.
Смотрите вживую здесь .
Что касается вашего конкретного случая, вы можете удалитьполностью подтвердите утверждение и перенесите проверку в макрос foo
.Это выглядело бы примерно так:
#define foo(x, y) (is_integral_constat_p(x) && (x < 10) ? real_foo : \
error_foo_x_is_not_a_constant_expression_or_more_than_10)(x, y)
Макрос в сочетании с оценкой короткого замыкания дает нам поведение, которое вы хотите.Как вы можете видеть здесь .
В этот момент я чувствую, что должен вас предостеречь.Этот код может быть не так прост, чтобы защитить в обзоре кода.Поэтому тщательно продумайте, стоит ли какое-либо повышение производительности получить объяснения.