Моя немедленная реакция на ваш вопрос была "ты слишком усложняешь его!" Например, что не так с:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
и последующим вызовом:
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1)
Один частичный ответ на вопрос «в чем проблема?» заключается в том, что «это неочевидно в этом примере, но когда префикс выглядит примерно так: PROJECT_LONG_SUBMODULE_ERROR
, вам не следует вводить его постоянно». Это нормально, но тогда вы можете подумать:
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
, чтобы сократить длинные имена, - вызывая
GENERR(COMPONENT2, ERROR2)
, чтобы получить имя ошибки:
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2
Почему PROJECT_LONG_SUBMODULE_ERROR
не будет расширен во время расширения GENERR
(то есть, почему в этом контексте оно окрашено в синий цвет)?
Оно не окрашено в синий; Я просто не стал бы создавать макрос с таким именем, поэтому не было бы для него расширения, точно так же, как не должно быть макроса COMPONENT2
или ERROR2
, который может быть расширен при вызове GENERR
.
Экспериментирование
Рассмотрим исходный файл pp31.c
:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1) -- OK
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PLS_ERR PROJECT_LONG_SUBMODULE_ERROR
#undef GENERR
#define GENERR(component, error) GENERATE_ERROR(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- Broken
#undef GENERR
#define GENERR_PREFIX(prefix, component, error) GENERATE_ERROR(prefix, component, error)
#define GENERR(component, error) GENERR_PREFIX(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PROJECT_LONG_SUBMODULE_ERROR pink_elephant
GENERR(COMPONENT2, ERROR2) -- Broken (too many pink elephants)
#undef PROJECT_LONG_SUBMODULE_ERROR
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, COMPONENT2, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR(COMPONENT2, err)
GENCOMPERR(ERROR2) -- OK
#undef GENCOMPERR
#define CURR_COMP COMPONENT2
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR_PREFIX(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- OK
Когда я запускаю cpp pp31.c
(это cpp
из G CC 9.3.0 работает на Ма c), я получаю еще несколько пустых строк, чем показано ниже, но непустые строки в любом случае интересны:
# 1 "pp31.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "pp31.c"
MODULE_PREFIX_COMP1_ERROR1 -- OK
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
pink_elephant_COMPONENT2_ERROR2 -- Broken (too many pink elephants)
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_CURR_COMP_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
Ключевым моментом является что (с осторожностью) у вас могут быть макросы, которые расширяются до элементов конечного объединенного имени сообщения об ошибке (отсюда PLS_ERR
и CURR_COMP
), но вы не можете иметь макросы для этих элементов (без «розовых слонов»).
Я оставляю за собой право судить о том, действительно ли это хороший дизайн для обработки имен сообщений об ошибках, но я думаю, что он может работать.