Является ли это допустимым способом проверки, если список аргументов макропеременного макроса пуст? - PullRequest
10 голосов
/ 29 марта 2019

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

Мне кажется, я нашел простое, компактное и стандартное решение:

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

В: Существуют ли обстоятельства, когда мое решение не сработает или вызовет плохо определенное поведение?

На основе C17 6.10.3.2/2 (оператор #): "Литерал строки символов, соответствующий пустому аргументу, равен """ , я считаю, что #__VA_ARGS__ всегда четко определен ,

Объяснение макроса:

  • Это создает массив массивов литералов и инициализирует его, используя строковый литерал.
  • Независимо от того, что передано макросу, все аргументы будут преобразованы в один длинный строковый литерал.
  • Если список макросов пуст, строковый литерал станет "", который состоит только из нулевого терминатора и поэтому имеет размер 1.
  • Во всех остальных случаях его размер будет больше 1.

Ответы [ 2 ]

2 голосов
/ 29 марта 2019

Примечание: эта версия этого ответа является результатом серьезной перезаписи. Некоторые претензии были удалены, а другие значительно изменены, чтобы сосредоточиться и лучше обосновать наиболее важные моменты.

Макросы Variadic и их переменные аргументы

[Спорная, много спорная позиция удалена. Это больше отвлекало, чем помогало.]


Предлагаемый макрос

Мне кажется, я нашел простое, компактное и стандартное решение:

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

Мы можем обойти любой вопрос неопределенности, рассмотрев эту вариацию:

#define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

. Те же соображения применимы к интерпретации пустых против непустых переменных переменных здесь, как и в вашей исходной версии. В частности,

На основе C17 6.10.3.2/2 (оператор #): " Строка символов литералом, соответствующим пустому аргументу, является """, я считаю, что #__VA_ARGS__ всегда четко определено.

Я согласен. Здесь также важен раздел 6.10.3.1/2: «Идентификатор __VA_ARGS__, встречающийся в списке замены, должен обрабатываться так, как если бы он был параметром [...].»

Объяснение макроса:

  • Это создает составной массив символов литерала и инициализирует его, используя строковый литерал.

Да.

  • Независимо от того, что передано макросу, все аргументы будут преобразованы в один длинный строковый литерал.

Да. __VA_ARGS__ рассматривается как a (один) параметр. Если имеется несколько переменных-переменных, это может повлиять на повторное сканирование, но оператор строкового преобразования действует в точке раскрытия макроса до повторного сканирования.

  • Если список макросов пуст, строковый литерал станет "", который состоит только из нулевого терминатора и поэтому имеет размер 1.

Да.

  • Во всех остальных случаях его размер будет больше 1.

Да. Это справедливо даже в случае двух аргументов с нулевым токеном в списке аргументов переменной, is_empty(dummy,,), где #__VA_ARGS__ будет расширяться до ",". Это также верно в случае аргумента, состоящего из пустого строкового литерала, is_empty(dummy, ""), где #__VA_ARGS__ будет расширяться до "\"\"".

ОДНАКО , что все еще может не соответствовать вашим целям. В частности, вы не можете использовать его в директиве условной компиляции. Хотя sizeof выражения обычно разрешены в выражениях целочисленных констант, таких как формы управляющих выражений таких директив,

  • лексически, как токен предварительной обработки, sizeof классифицируется как идентификатор (нет различия между ключевыми словами и идентификаторами для токенов предварительной обработки) и
  • в соответствии с пунктом 6.10.1 / 4 стандарта при обработке управляющего выражения директивы условной компиляции,

    После того, как все замены, вызванные расширением макроса и определенным унарным оператором, были выполнены, все оставшиеся идентификаторы (включая те, которые лексически идентичны ключевым словам) заменены на номер pp 0

    (выделение добавлено).

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

1 голос
/ 29 марта 2019

Лично мне не нравится смешивать оценку на макро / препроцессорном уровне и тест на уровне компиляции.

Кажется, что нет стандартного способа сделать это на макроуровне, но здесь есть хаки: препроцессор C ++ __VA_ARGS__ количество аргументов

...