Задержка строкового преобразования без расширения макроса

Можно ли изменить следующий фрагмент кода, чтобы предотвратить печать #pragma GCC warning от изменения, если какой-либо из токенов идентификатора в deprecation_message определяются как объектно-подобные макросы в точке, где dmacro расширяется, сохраняя при этом возможность замены symbol в сообщение? Уродство - не объект, расширения GCC - честная игра, если только clang их реализует, но содержание диагностики не может быть изменено.

#define deprecation_message(symbol) \
   #symbol will be removed from <header.h> in the next release\n\
   of $LIBRARY. To use #symbol, include <moved/header.h> instead.

#define make_pw(...) make_pw_(__VA_ARGS__)
#define make_pw_(...) make_pw__(GCC warning #__VA_ARGS__)
#define make_pw__(...) _Pragma(#__VA_ARGS_)

#define dmacro(a,b,c) make_pw(deprecation_message(dmacro)) xmacro(a,b,c)

// Uncommenting any of the following #define lines should *not*
// change the text of the diagnostic in any way.
//#define header
//#define n f
//#define will won't'
dmacro(x,y,z)

(Вы можете испытать желание достичь конкатенации строкового литерала, но это не сработает; оба _Pragma сам и #pragma GCC warning принимать только один строковый литерал. _Pragma("this" "that") это синтаксическая ошибка.)

1 ответ

Решение

Может быть, я неправильно понял вопрос, но следующий код работает и компилирует оба clang а также gccи протестирован с -std=c99 а также -std=c11:

#include <stdio.h>
#include <stdlib.h>

#define deprecation_message(symbol) \
   #symbol " will be removed from <header.h> in the next release\n" \
   "of $LIBRARY. To use " #symbol ", include <moved/header.h> instead."

#define make_pw__(...) _Pragma(#__VA_ARGS__)
#define make_pw_(...)  make_pw__(GCC warning #__VA_ARGS__)
#define make_pw(...)   make_pw_(__VA_ARGS__)

#define xmacro(a, b, c) puts("I'm working: " #a #b #c "!")
#define dmacro(a, b, c) make_pw(deprecation_message(dmacro)) xmacro(a, b, c)

int
main(void)
{
    dmacro(x, y, z);
    return EXIT_SUCCESS;
}

Теперь приведенный выше код расширит это с clang:

int
main(void)
{
    #pragma GCC warning "\042dmacro\042 \042 will be removed from <header.h> in the next release\134n\042 \042of $LIBRARY. To use \042 \042dmacro\042 \042, include <moved/header.h> instead.\042"
    puts("I'm working: " "x" "y" "z" "!");
    return 0 /* Successful exit status.  */;
}

и выдаст следующие предупреждения clang:

src/main.c:18:5: warning: "dmacro" " will be removed from <header.h> in the next release\n" "of $LIBRARY. To use " "dmacro" ", include <moved/header.h> instead." [-W#pragma-messages]
    dmacro(x, y, z);
    ^

и это на gcc:

src/main.c:18:13: warning: "dmacro" " will be removed from <header.h> in the next release\n" "of $LIBRARY. To use " "dmacro" ", include <moved/header.h> instead."
     dmacro(x, y, z);
             ^~~~~~~~

По сути, все, что я сделал, это поместил сообщение об устаревании в кавычки...


ОБНОВЛЕНИЕ1:

Теперь самое смешное, если вы удалите make_pw_, то есть:

#define make_pw_(...)  make_pw__(GCC warning __VA_ARGS__)

затем clang даст вам это:

src/main.c:18:5: warning: dmacro will be removed from <header.h> in the next releaseof $LIBRARY. To use dmacro, include <moved/header.h> instead. [-W#pragma-messages]
    dmacro(x, y, z);
    ^

Что приятно, так как не содержит нежелательных цитат, однако GCC только даст вам это:

src/main.c:18:13: warning: dmacro
     dmacro(x, y, z);
             ^~~~~~~~

Что еще более странно, это то, что если вы измените deprecation_message к этому:

#define deprecation_message(symbol) "" #symbol "..."

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

Если честно, я не знаю, является ли это ошибкой GCCили это четко определенное поведение и clang делает некоторую дополнительную работу, чтобы разобраться в этом, тем не менее, это происходит с последними версиями двух компиляторов. (Если бы мне пришлось делать ставку, я бы сказал, это ошибка:P)


ОБНОВЛЕНИЕ2:

Вот немного измененная версия, которая работает на обоих компиляторах, как вы описали в своем комментарии. Хитрость в том, что макрос DEPRECATED имеет два этапа вместо одного.

#include <stdio.h>
#include <stdlib.h>

#define DEPRECATED_(...) #__VA_ARGS__
#define DEPRECATED(symbol)                                                     \
    DEPRECATED_(#symbol will be removed from <header.h> in the next release\n  \
                of $LIBRARY. To use #symbol, include <moved/header.h> instead.)

#define PRAGMA_WARN_(message) _Pragma(#message)
#define PRAGMA_WARN(message)  PRAGMA_WARN_(GCC warning message)

#define dmacro(a, b, c)                                                        \
    PRAGMA_WARN(DEPRECATED(dmacro))                                            \
    puts("I'm working: " #a #b #c "!")

int
main(void)
{
    dmacro(x, y, z);
    return EXIT_SUCCESS;
}

Так что это даст следующий результат в clang:

src/main.c:19:5: warning: "dmacro" will be removed from <header.h> in the next release of $LIBRARY. To use "dmacro", include <moved/header.h> instead. [-W#pragma-messages]
    dmacro(x, y, z);
    ^

И следующий в GCC:

src/main.c:19:13: warning: "dmacro" will be removed from <header.h> in the next release
 of $LIBRARY. To use "dmacro", include <moved/header.h> instead.
     dmacro(x, y, z);
             ^~~~~~~~

ПРИМЕЧАНИЕ: я настоятельно рекомендую вам удалить\nперсонаж из вашего сообщения!

Другие вопросы по тегам