Стандартное поведение функционально-подобного макроса с пустым аргументом рядом с оператором ##? - PullRequest
0 голосов
/ 20 ноября 2018

Возьмем следующий пример:

#define FOO(x) bar x ## baz
FOO( )

Каков ожидаемый вывод приведенного выше кода в соответствии со стандартами ANSI C и C99 после фазы предварительной обработки?

Я запустилвыше через gcc -E и clang -E, и оба производят выход, эквивалентный следующему:

bar baz

Кроме того, если вышеупомянутый предварительно обработанный вывод считается соответствующим стандартам, то что по этому поводу?

#define FOO(x) x ## baz
FOO( )

С вышеуказанной модификацией GCC и clang по-прежнему производят вывод, эквивалентный следующему, без вывода каких-либо предупреждений или ошибок.(даже с флагами -Wall -Werror):

baz

Я подозреваю, что вышеприведенный вывод не соответствует стандартам, потому что стандарт ANSI C 9899: 1990 гласит:

6.8.3.3 Оператор ##

A ## маркер предварительной обработки не должен появляться в начале или в конце списка замены ни для одной из форм определения макроса.

Если в списке замены естьЗа параметром непосредственно предшествует или следует ## токен предварительной обработки, параметр заменяется последовательностью токена предварительной обработки соответствующего аргумента.

Хорошо, так что технически оператор ## НЕ находится в начале списка замены в обоих приведенных выше двух примерах, но x расширяется до ... одного пробела?ничего такого?... так что оператор ## (по крайней мере, насколько я понимаю) объединяет пробел pp-токен (или ничего) в baz.

Кроме того, стандарт гласит:

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

Итак, учитывая первый приведенный выше пример, почему не следуетскажем, это правильный вывод?

barbaz

ОБНОВЛЕНИЕ

В качестве идентификатора мне удалось заставить GCC выдать ошибку для первого примера, который я привел выше при предварительной обработке кода следующим образом:

gcc -ansi -pedantic -Wall -Werror -E ex1.c -o -

Вывод (с ошибкой):

foo.c:2:6: error: invoking macro FOO argument 1: empty macro arguments are undefined in ISO C90 [-Werror=pedantic]
 FOO( )
      ^
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "foo.c"

bar baz
cc1: all warnings being treated as errors

Ошибка, однако, не связана с поведением оператора ## , о чем и был этот вопрос.Тем не менее, это по-прежнему интересно.

Также интересно отметить, что clang не выдает ошибку при предварительной обработке файла с использованием тех же флагов.

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018
Хорошо, так что технически оператор ## НЕ находится в начале списка замены в обоих приведенных выше двух примерах,

Слово "технически" здесь избыточно.Список замены обоих ваших макросов имеет x перед ##, так что это правило не применяется.

, но x расширяется до ... одного пробела?ничего?

6.4 перечисляет то, что считается токенами предварительной обработки: заголовок-имя, идентификатор, номер pp, символьная константа, строковый литерал, пунктуатор и «каждый непробельный символ, который не может быть одним из перечисленных выше»,Обратите внимание, что явно отсутствует в этом списке ... сами по себе пробелы.

Таким образом, ваш пробел в вашем аргументе FOO не имеет значения;Ваш аргумент представляет собой последовательность из 0 токенов предварительной обработки (то есть 0 из вещей в этом списке выше).Это означает, что это применимо:

6.10.3.3 p2:

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

Следующий абзац 3 относится к полученному здесь результату:

конкатенация метки с токеном предварительной обработки без меток приводит к токену предварительной обработки без меток.

Итак, метка наклеенная на baz производит baz.

0 голосов
/ 20 ноября 2018

Если у вас есть это:

#define FOO(x) bar x ## baz                
FOO();                                  

Вы получаете bar baz.Таким образом, пробел в вызове FOO не имеет значения.

Если у вас есть это:

#define FOO(x) bar ## x ## baz                 
FOO();                                  

вы получите barbaz, потому что вы просите соединить все эти данные вместе.

Но вы пишете:

#define FOO(x) bar x ## baz                 
FOO();                                  

Вы получаете bar baz, потому что вы просите bar и вставку x и baz.Что похоже на #define FOO(x) bar,x ## baz, что дает вам bar,baz.

...