Как проверить, является ли параметр целочисленным константным выражением в макросе препроцессора C? - PullRequest
6 голосов
/ 14 февраля 2012

В настоящее время я очищаю существующую C-библиотеку для бесстыдной публикации.

Макрос препроцессора NPOT используется для вычисления следующей большей степени двух для заданного интегрального константного выражения во время компиляции .Макрос обычно используется при прямой инициализации.Для всех других случаев (например, с использованием переменных параметров) есть встроенная функция с той же функцией.

Но если пользователь передает переменную, алгоритм расширяется до огромного фрагмента машинного кода.У меня вопрос: Что я могу сделать, чтобы запретить пользователю передавать в макрос что-либо, кроме целочисленного константного выражения?

#define NPOT(x)   complex_algorithm(x)

const int c=10;
int main(void) {
    int i=5;

    foo = NPOT(5);   // works, and does everything it should
    foo = NPOT(c);   // works also, but blows up the code extremely
    foo = NPOT(i);   // blows up the code also
}

Что я уже пробовал:

  1. Определить макрос для #define NPOT(x) complex_algorithm(x ## u).Это все еще работает и выдает - даже если вряд ли полезно - ошибку компилятора для переменных параметров.Если нет такой переменной, как iu ... Грязная, опасная, не хочу ее.
  2. Документация, не работает для большинства пользователей.

Ответы [ 2 ]

6 голосов
/ 14 февраля 2012

Вы можете использовать любое выражение, которое нуждается в постоянном интегральном выражении и которое затем будет оптимизировано.

#define NPOT(X)                                         \
 (1                                                     \
 ? complex_algorithm(X)                                 \
 : sizeof(struct { int needs_constant[1 ? 1 : (X)]; })  \
 )

в конце концов вы должны привести результат sizeof к соответствующему целочисленному типу, поэтомуВозвращаемое выражение относится к типу, который вы ожидаете.

Я использую здесь без тега struct, чтобы

  • имел тип, так что на самом деле временный вывод не производится
  • имеют уникальный тип, такой, что выражение может повторяться где угодно в коде, не вызывая конфликтов
  • инициирует использование VLA, что недопустимо внутри struct с C99:

Член структуры или объединения может иметь любой тип объекта, кроме изменяемого типа.

Я использую троичный ?: с 1 в качествевыбор выражения, чтобы гарантировать, что : всегда вычисляется для его типа, но никогда не оценивается как выражение.

Редактировать: Кажется, что gcc принимает VLA внутри struct как расширениеи делаетдаже не предупреждаю об этом, даже когда я прямо говорю -std=c99.Это действительно плохая идея для них.

Для такого странного компилятора :) вы можете использовать sizeof((int[X]){ 0 }) вместо этого.Это «запрещено», как и вышеприведенная версия, но к тому же даже gcc жалуется на это.

0 голосов
/ 14 февраля 2012
#define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x))

Это даст ошибку компиляции, если x не является целочисленным константным выражением.

my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3));    // OK
my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3));  // compile error

Обратите внимание, что это решение не работает для инициализации статической переменной:

static int a = INTEGRAL_CONST_EXPR(2 + 3);

вызовет ошибку компиляции, поскольку выражение с , не является константным выражением.

Как пишет @JensGustedt в комментарии, интегральное константное выражение, разрешающееся до отрицательного целого числа, не может использоваться в этом решении, поскольку ширина битового поля не может быть отрицательной.

...