2015-08-25 7 views
7

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

#define f() foo 
void f()_bar(void); 
void f()bar(void); 
void f()-bar(void); 

Когда я исполняю gcc -E -P test.c (работает только препроцессор), я получаю следующий результат:

void foo _bar(void); 
void foo bar(void); 
void foo-bar(void); 

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

Является ли это обязательным для любого стандарта (я не мог найти документацию по теме)?

Я хочу сделать _bar часть того же токена. Есть какой-либо способ сделать это? Я мог бы использовать оператор конкатенации токенов ##, но для этого потребуется несколько уровней макросов (поскольку в реальном коде f() более сложный). Мне было интересно, есть ли простое (и, вероятно, более читаемое) решение.

+1

Is google down? Вы даже не пытались найти C [стандарт] (http://port70.net/~nsz/c/c11/n1570.html#6.10.3), который ** явно ** является авторитетным источником для консультаций. – Olaf

+1

@Olaf - я не нашел явного утверждения, отвечающего на мой вопрос ни в C99, ни в ссылке, на которую вы указали меня. – martinkunev

+4

Справедливости ради следует отметить, что поведение, описанное в стандарте, не совсем очевидно. Я предполагаю, что ключевое слово заключается в том, что препроцессор заменяет макрос * токенами *, что означает, что для этого на самом деле будут вставлены пробелы. – nemetroid

ответ

1

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

Да.

Соответствует ли это стандарту (я не мог найти документацию по этой теме)?

Да, хотя для реализации токенов допускается вставка еще более одного пробела.

f()_bar

здесь у вас есть 4 маркеров после лексического анализа (на самом деле они препроцессора лексемы на данном этапе, но давайте называть их токенов): f, (, ) и _bar.

Функционально как макрос замены семантический (как определено в C11, 6.10.3) должен заменить 3 маркера f, (, ) в новую foo. Не разрешается работать с другими токенами и менять последний токен _bar. Для этого реализация должна вставить хотя бы одно пробел, чтобы сохранить токен _bar. В противном случае результатом будет foo_bar, который является единственным токеном.

gcc препроцессор несколько documents это здесь:

После того, как входной файл разбивается на лексемы, границы лексем никогда не изменится, кроме случаев, когда оператор «##» предварительной обработки используются для вставки маркеров вместе. См. Конкатенация. Например,

#define foo() bar 
foo()baz 
    ==> bar baz 
not 
    ==> barbaz 

В другом случае, как f()-bar, есть 5 жетонов: f, (, ), - и bar. (- - токен акцента в C, тогда как _ в _bar - это просто символ токена идентификатора). Реализации не нужно вставлять разделитель токенов (как пробел) здесь, поскольку после замены макроса -bar по-прежнему считаются двумя отдельными токенами из синтаксиса Си.

gcc препроцессор (cpp) не вставляет здесь пробелы просто потому, что это не обязательно. В cppdocumentation, на лексемы расстояния написано (по другому вопросу):

Однако, мы хотели бы, чтобы вставить пространство до минимума, как по эстетическим причинам и потому, что создает проблемы для людей, которые все еще пытаются злоупотреблять препроцессором для таких вещей, как источник Fortran и Makefiles.

Я не рассматривал решение вашей проблемы в этом ответе, но я думаю, вы должны использовать оператор явно заданный для конкатенации лексем: лексемы оператора оклейки ##.

1

Единственный способ, которым я могу думать (если вы не можете использовать маркер оператора конкатенации ##) использует традиционный (предварительный стандарт) C предварительной обработки:

gcc -E -P -traditional-cpp test.c 

Выход:

void foo_bar(void); 
void foobar(void); 
void foo-bar(void); 

More info

Смежные вопросы