Раскрытие макроса в строку

Вот здесь описывается как организовать автоинкремент версии файла.

В частности там есть такие строки

#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_PATCH 0
#define VERSION_BUILD 0

#define stringify(a) stringify_(a)
#define stringify_(a) #a

и потом использование

VALUE "FileVersion", stringify(VERSION_MAJOR) "." stringify(VERSION_MINOR) "." stringify(VERSION_PATCH) "." stringify(VERSION_BUILD)

Все работает, но я не пойму как. Как работает здесь макрос stringify?

Причем если объявить его так

#define stringify(a) #a

То тогда в FileVersion записывается строка VERSION_MAJOR.VERSION_MINOR.VERSION_PATCH.VERSION_BUILD

Что это за конструкция такая #a и почему ее нужно вкладывать в другой макрос?


Ответы (2 шт):

Автор решения: KoVadim

#a - это конструкция препроцессора (это важно, так как он деюре работает до компилятора), которая значит ровно одно - замени #a на "a".

Когда Вы пишите

#define stringify(a) #a
VERSION_MAJOR(VERSION)

то препроцессор именно это и делает - он просто подставляет и получается "VERSION".

Когда же используется первый, правильный способ, то в первую итерацию раскрывается вот так

stringify(VERSION_MAJOR) "."

превращается в

stringify_(1) "."

и препроцессор обнаруживает, что там ещё есть макросы и делает вторую иттерацию

"1" "."

а потом уже такие строки склеиваются в

"1."
→ Ссылка
Автор решения: Stanislav Volodarskiy

Stringification поясняет что #a превращает свой аргумент в строку. Но есть особенность:

Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification.

Аргумент не вычисляется перед превращением в строку. Это приводит к тому что stringify_(VALUE) раскрывается в "VALUE".

А если раскрывать stringify(VALUE), то перед передачей во вложенный макрос параметр раскроется и получится stringify_(1), который затем раскроется в "1".

Двойная вложенность заставляет препроцессор вычислить значение до стрингификации.

Попробуйте сами:

#define VALUE 1234
#define stringify(a) stringify_(a)
#define stringify_(a) #a

#include <stdio.h>

int main() {
    puts(stringify_(VALUE));
    puts(stringify(VALUE));
}
$ gcc stringify.c && ./a.out 
VALUE
1234

Вам, наверно, интересно почему препроцессор везде вычисляет параметры а при стрингификации нет? Чтобы работали такие штуки:

#include <stdio.h>

#define DUMP(expr) printf("%s = %d\n", #expr, (expr))
#define VALUE 42

int main() {
    int a = 2;
    int b = 3;
    DUMP(a + b);
    DUMP(VALUE);
    DUMP(a + b + VALUE);
}
$ gcc stringify.c && ./a.out 
a + b = 5
VALUE = 42
a + b + VALUE = 47
→ Ссылка