Макросы как аргументы директив препроцессора - PullRequest
3 голосов
/ 15 ноября 2009

Столкнувшись с вопросом, возможно ли выбрать #include s в препроцессоре, я сразу подумал, невозможно .
Только для того, чтобы позже выяснить, что это действительно возможно, и вам нужно только следить за расширением аргументов (о котором может позаботиться, например, 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 стандартный, уточненный вопрос.

Ответы [ 2 ]

7 голосов
/ 15 ноября 2009

Из § 16.2-4 («Включение исходного файла») проекта C ++ 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) не упоминается. C ++ § 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, аргументов для макро-функции и тела макро-функции при замене. Я думаю, что это все.

2 голосов
/ 15 ноября 2009

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

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

...