Алгоритм раскрытия define'ов
От: glap  
Дата: 26.06.11 17:32
Оценка:
Есть всем известный приём, как преобразовать вложенный define в строку или применить оператор ##, чтобы препроцессор его раскрыл, а не впихнул его имя. Например:

#define ARG blabla

#define TO_STR(a) #a
#define USELESS(a) TO_STR(a)

TO_STR(ARG) // == "ARG"
USELESS(ARG) // == "blabla"


В какой момент препроцессор решает, что ARG стоит раскрыть?

По началу я себе представлял поведения препроцеессора при развороте дефайнов примерно так:
1. Подствляет вместо упоминаемых параметров аргументы (т.е. тупо копирование строки).
2. Рекурсивно раскрывает вложенные дефайны слева-на-право.

Но такой алгоритм никак не учитывает "прослоечные" дефайны и вконце концов перед оператором # будет стоять ARG, который он по правилам из стандарта не будет раскрывать, а преобразует в строку "ARG".

Спасибо.
Re: Алгоритм раскрытия define'ов
От: Vain Россия google.ru
Дата: 26.06.11 23:31
Оценка:
Здравствуйте, glap, Вы писали:

G>Есть всем известный приём, как преобразовать вложенный define в строку или применить оператор ##, чтобы препроцессор его раскрыл, а не впихнул его имя.

Здесь "всем известный приём" стоит заменить на "приём для всем известного компилятора" — MSVC.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Алгоритм раскрытия define'ов
От: const_volatile  
Дата: 27.06.11 03:24
Оценка:
Здравствуйте, glap, Вы писали:

G>
G>#define ARG blabla

G>#define TO_STR(a) #a
G>#define USELESS(a) TO_STR(a)

G>TO_STR(ARG) // == "ARG"
G>USELESS(ARG) // == "blabla"
G>


G>В какой момент препроцессор решает, что ARG стоит раскрыть?


в случае оператора # после подстановки аргумента (a->ARG) макроподстановка прекращается. в остальных случаях макроподстановка продолжается, пока не будут исчерпаны токены. в этом легко убедиться, добавив
#define blabla XXX
USELESS(ARG) // -> "XXX"

то есть все макросы (a->ARG->blabla->XXX) раскрываются в момент подстановки аргументов USELESS, но до раскрытия TO_STR. в качестве аргумента TO_STR подаётся уже XXX.
Re[2]: Алгоритм раскрытия define'ов
От: const_volatile  
Дата: 27.06.11 03:24
Оценка:
Здравствуйте, Vain, Вы писали:

G>>Есть всем известный приём, как преобразовать вложенный define в строку или применить оператор ##, чтобы препроцессор его раскрыл, а не впихнул его имя.

V>Здесь "всем известный приём" стоит заменить на "приём для всем известного компилятора" — MSVC.

оператор # в стандарте C с 89 года. ваш К.О.
Re[2]: Алгоритм раскрытия define'ов
От: glap  
Дата: 27.06.11 12:50
Оценка:
Здравствуйте, const_volatile, Вы писали:

_>Здравствуйте, glap, Вы писали:


G>>
G>>#define ARG blabla

G>>#define TO_STR(a) #a
G>>#define USELESS(a) TO_STR(a)

G>>TO_STR(ARG) // == "ARG"
G>>USELESS(ARG) // == "blabla"
G>>


G>>В какой момент препроцессор решает, что ARG стоит раскрыть?


_>в случае оператора # после подстановки аргумента (a->ARG) макроподстановка прекращается. в остальных случаях макроподстановка продолжается, пока не будут исчерпаны токены. в этом легко убедиться, добавив

_>
_>#define blabla XXX
_>USELESS(ARG) // -> "XXX"
_>

_>то есть все макросы (a->ARG->blabla->XXX) раскрываются в момент подстановки аргументов USELESS, но до раскрытия TO_STR. в качестве аргумента TO_STR подаётся уже XXX.

Ну вот тут возникает противоречение. Если напрямую вызывать TO_STR(ARG), то он сначала раскроет TO_STR, а потом уже увидит там оператор решётки и на том закончит, а если через USELESS(ARG), то он принимает решение раскрыть ARG раньше, чем TO_STR. Я этого момента не могу уловить.
Re[2]: Алгоритм раскрытия define'ов
От: glap  
Дата: 27.06.11 12:56
Оценка:
Здравствуйте, const_volatile, Вы писали:

_>Здравствуйте, glap, Вы писали:


G>>
G>>#define ARG blabla

G>>#define TO_STR(a) #a
G>>#define USELESS(a) TO_STR(a)

G>>TO_STR(ARG) // == "ARG"
G>>USELESS(ARG) // == "blabla"
G>>


G>>В какой момент препроцессор решает, что ARG стоит раскрыть?


_>в случае оператора # после подстановки аргумента (a->ARG) макроподстановка прекращается. в остальных случаях макроподстановка продолжается, пока не будут исчерпаны токены. в этом легко убедиться, добавив

_>
_>#define blabla XXX
_>USELESS(ARG) // -> "XXX"
_>

_>то есть все макросы (a->ARG->blabla->XXX) раскрываются в момент подстановки аргументов USELESS, но до раскрытия TO_STR. в качестве аргумента TO_STR подаётся уже XXX.

Или выходит, что когда каждый аргумент подставляется, внутренность этого каждого в отдельности эаргумента сканируется на наличие вложенных дефайнов?
Re: Алгоритм раскрытия define'ов
От: Centaur Россия  
Дата: 27.06.11 13:24
Оценка:
Здравствуйте, glap, Вы писали:

G>Есть всем известный приём, как преобразовать вложенный define в строку или применить оператор ##, чтобы препроцессор его раскрыл, а не впихнул его имя. Например:


G>#define ARG blabla

G>#define TO_STR(a) #a
G>#define USELESS(a) TO_STR(a)

G>TO_STR(ARG) // == "ARG"
G>USELESS(ARG) // == "blabla"
G>


G>В какой момент препроцессор решает, что ARG стоит раскрыть?


Стандартопроект C++ 2011, [cpp.subst]/1:

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.


[cpp.stringize]:

1: Each # preprocessing token in the replacement list for a function-like macro shall be followed by a parameter as the next preprocessing token in the replacement list.

2: A character string literal is a string-literal with no prefix. If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character string literal preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument. Each occurrence of white space between the argument’s preprocessing tokens becomes a single space character in the character string literal. White space before the first preprocessing token and after the last preprocessing token comprising the argument is deleted. Otherwise, the original spelling of each preprocessing token in the argument is retained in the character string literal, except for special handling for producing the spelling of string literals and character literals: a \ character is inserted before each " and \ character of a character literal or string literal (including the delimiting " characters). If the replacement that results is not a valid character string literal, the behavior is undefined. The character string literal corresponding to an empty argument is "". The order of evaluation of # and ## operators is unspecified.


Разворачиваем TO_STR(ARG) согласно [cpp.subst]/1: arguments = [ARG], replacement_list = [#, a], срабатывает условие «unless preceded by a #», параметр не разворачивается. Разворачиваем стрингизацию согласно [cpp.stringize]/2: «If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character string literal preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument.» Получаем replacement_list' = ["ARG"]. На этом разворот макросов завершается, так как у нас остался только строковый литерал.

Разворачиваем USELESS(ARG): arguments = [ARG], replacement_list = [TO_STR, (, a, )], условие не срабатывает, поэтому заменяем параметр: a «is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.» Значение аргумента — ARG — макрос, который перед подстановкой разворачивается в blabla: replacement_list' = [TO_STR, (, blabla, )].

Далее, [cpp.rescan]/1:

After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. Then the resulting preprocessing token sequence is rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.


Соответственно, [TO_STR, (, blabla, )] подвергается дальнейшей развёртке. Макрос = TO_STR, arguments = [ARG], replacement_list = [#, a], опять срабатывает условие про #, стрингизация отрабатывает и даёт "blabla", replacement_list' = ["blabla"].
Re[3]: Алгоритм раскрытия define'ов
От: const_volatile  
Дата: 27.06.11 13:29
Оценка:
Здравствуйте, glap, Вы писали:

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


по-моему там не просто аргумент, а вся строка заново сканируется после каждого цикла подстановок. например,
#define TEST FUNC
#define FUNC(x) x*x
#define SQR(y) TEST y
SQR(2)   // выведет FUNC 2
SQR((2)) // выведет 2*2
Re[2]: Алгоритм раскрытия define'ов
От: glap  
Дата: 27.06.11 14:23
Оценка:

Значение аргумента — ARG — макрос, который перед подстановкой разворачивается в blabla


Вот это ключевая фраза. Выходит, что вложенные макросы разворачиваются в 2-х ситуациях.
Сначала если они внутри аргумента, а потом ещё раз после подстановки всех аргументов просматривается вся новая строка (т.е. новый список этот).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.