Макросы как аргументы директив препроцессора

Столкнувшись с вопросом, можно ли выбрать #includes в препроцессоре, я сразу подумал невозможно.
.. Только чтобы позже узнать, что это действительно возможно, и вам нужно только следить за расширения аргументов (о которых может позаботиться, например, Boost.Preprocessor).

Хотя я бы не стал делать это для включений, если это возможно, я хотел бы знать, почему это работает. На данный момент мне не удается получить полезное представление о стандарте C++ или C.
Разрешены ли параметризованные макросы для любой директивы препроцессора? (кроме #define/#undef)
Может ли кто-нибудь указать, где это разрешено, и подвести итог?

Пример любопытного использования Boost.Preprocessor для простоты:

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>

#define INC_LOCAL(a,b)  BOOST_PP_STRINGIZE(BOOST_PP_CAT(BOOST_PP_CAT(a,b),.h))
#define INC_GLOBAL(a,b) BOOST_PP_CAT(BOOST_PP_CAT(<,a),BOOST_PP_CAT(b,>))

#include INC_LOCAL(loc,al)   // #include "local.h"
#include INC_GLOBAL(vect,or) // #include <vector>

Обновление: ссылка на стандарт C, уточненный вопрос.


person Georg Fritzsche    schedule 15.11.2009    source источник


Ответы (2)


Из § 16.2-4 («Включение исходного файла») проекта С++ 2003:

Предварительная обработка директивы формы

# include pp-tokens new-line 

(который не соответствует ни одной из двух предыдущих форм) разрешен. Токены предварительной обработки после include в директиве обрабатываются так же, как и в обычном тексте (каждый идентификатор, определенный в настоящее время как имя макроса, заменяется своим замещающим списком токенов предварительной обработки).

§ 6.10.2-4 C99 говорит то же самое.

«Две предыдущие формы», упомянутые выше, это # include <h-char-sequence> и # include "q-char-sequence". Этот раздел кажется слишком простым, чтобы подводить итоги.

Для других директив расширение макроса не выполняется ни для какого токена предварительной обработки identifier (обратите внимание, что это поведение определяется не грамматикой, а C++ § 16/C § 6.10):

# if constant-expression new-line [group] 
# ifdef identifier new-line [group] 
# ifndef identifier new-line [group] 
# elif constant-expression new-line [group] 
# else new-line [group] 
# endif new-line 
# include pp-tokens new-line 
# define identifier replacement-list new-line 
# define identifier lparen [identifier-list] ) replacement-list new-line 
# undef identifier new-line 
# line pp-tokens new-line 
# error [pp-tokens] new-line 
# pragma [pp-tokens] new-line 
# new-line 

#line явно макрорасширяется C++ § 16.4-5/C § 6.10.4-5. Расширение для #error (C++ § 16.5/C § 6.10.5) и #pragma (C++ § 16.6/C § 6.10.6) не упоминается. С++ § 16.3-7/C 6.10.3-8 гласит:

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

C++ § 16.3.1 / C § 6.10.3.1-1 сообщает нам, что когда аргументы макрофункции подставляются в replacement-list, они сначала раскрываются макросом. Точно так же в C++ § 16.3.4/C § 6.10.3.4 макрос препроцессора расширяет replacement-list после подстановки.

Таким образом, расширение макроса выполняется для #if, #elif, #include, #line, аргументов функции макроса и тела функции макроса при замене. Я думаю, это все.

person outis    schedule 15.11.2009
comment
Ну, pp-tokens приводит меня к preprocessing-token, откуда только preprocessing-op-or-punc выглядит хорошо, но дает только некоторые операторы. - person Georg Fritzsche; 15.11.2009
comment
Итак, are processed just as in normal text подразумевает расширение макроса на что-то вроде identifier(identifier, ...)? - person Georg Fritzsche; 15.11.2009
comment
Конечно. Результат может иметь неопределенное поведение. - person outis; 15.11.2009
comment
Я рад, что вы хоть что-то поняли... хотя я время от времени использую макросы BOOST_PP, для меня это все волшебство! Думаю, мне придется читать и перечитывать этот ответ снова и снова :/ - person Matthieu M.; 15.11.2009
comment
Стандарты чтения — это приобретаемый навык. Чем больше вы подвергаетесь этому, тем легче становится. То же самое верно для любого дискурса. - person outis; 15.11.2009
comment
Я надеюсь на это, я работаю над этим, так как у меня есть моя копия. Это, безусловно, лучше, чем всегда действовать в соответствии с тем, что кто-то сказал, что это работает. - person Georg Fritzsche; 16.11.2009

Это очень фундаментальная особенность препроцессора C — например, такая директива, как #ifdef, не имеет смысла, кроме, когда используется с аргументом, который, возможно, является макросом (если вам нужно знать, что аргумент не является разрешено быть макросом, какова может быть цель #ifdef быть?!).

Я не уверен, как глава и стих стандарта ISO C помогут вам - стандарт C++, насколько я помню, в любом случае не меняет работу препроцессора.

person Alex Martelli    schedule 15.11.2009
comment
Исправление: теперь я запутался, разрешены ли параметризованные макросы в любой pp-директиве, и я хотел уточнить это. - person Georg Fritzsche; 15.11.2009