Stringification - как это работает?
Я знаю, что:
#define foo 4
#define str(s) #s
с str(foo)
выписывает: "foo"
, потому что stringify выполняется сначала из расширения текста, но это:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
с xstr(foo)
выписывает: "4"
.
Почему? Каковы шаги, участвующие в этом процессе?
Ответы
Ответ 1
Соответствующие шаги расширения макросов (за C 2011 [n1570] 6.10.3.1 и С++ 1998 16.3.1):
- Имена процессов, которым предшествуют
#
или ##
.
- Применить макрос к каждому аргументу.
- Замените каждый параметр соответствующим результатом вышеупомянутой замены макроса.
- Повторно сканировать дополнительные макросы.
Таким образом, при xstr(foo)
имеем:
- Текст замены
str(s)
не содержит #
или ##
, поэтому ничего не происходит.
- Аргумент
foo
заменяется на 4
, поэтому он используется как xstr(4)
.
- В тексте замены
str(s)
параметр s
заменяется на 4
, создавая str(4)
.
-
str(4)
выполняется повторно. (Полученные шаги производят "4"
.)
Обратите внимание, что проблема с str(foo)
заключается в том, что шаг 2, который заменит foo
на 4
, появится после шага 1, который изменяет аргумент на строку. На шаге 1 foo
все еще foo
; он не был заменен на 4
, поэтому результат "foo"
.
Вот почему используется вспомогательный макрос. Это позволяет нам выполнить шаг 2, а затем использовать другой макрос для выполнения шага 1.
Ответ 2
Первый случай
- Оцените
str(foo)
: замените str(foo)
на #foo
, т.е. "foo"
Второй случай
- Оцените
xstr(foo)
: замените xstr(foo)
на str(<foo-value>)
, т.е. str(4)
- Оцените
str(4)
: замените str(4)
на #4
, т.е. "4"
Вообще,
Препроцессор оценивает макро-функции, расширяющие макропеременные, пока не будет ничего, чтобы оценить:
Если вы определяете
#define xstr(s) str(s) + 1
#define str(s) s + 1
в следующем коде
#define foo 4
int main()
{
std::cout << str(foo) << '\n'
<< xstr(foo) << '\n' ;
}
он оценил бы как
Первая строка
- Замените
str(foo)
на <foo-value> + 1
, т.е. 4 + 1
- Больше ничего не заменишь. Отделка.
И результат 4 + 1
Вторая строка
- Замените
xstr(foo)
на str(<foo-value>) + 1
, т.е. str(4) + 1
- Замените
str(4)
на <4-value> + 1
, т.е. 4 + 1
- Ничего более, чтобы заменить.
И результат 4 + 1 + 1