我们有以下预处理器宏。它用于帮助Doxygen文档,因为Doxygen在使用C ++和一些模板类型的问题时遇到了麻烦:
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
当X
是非模板或只有一个模板参数时,它很有用。但是,如果X
是包含多个参数的模板:
DOCUMENTED_TYPEDEF(Foo<R,S>,Bar);
然后它会导致编译错误,因为字符串被分成Foo<R
和S>,Bar
(并且它不会生成文档)。
如何让预处理器宏贪婪?
答案 0 :(得分:4)
你不会喜欢这个:
#define COMMA ,
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(Foo<R COMMA S>,Bar)
测试:
$ gcc -E comma-macro.c # 1 "comma-macro.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "comma-macro.c" # 9 "comma-macro.c" typedef Foo<R , S> Bar;
在进行任何替换之前,会对括号和逗号分析宏参数列表。然后在COMMA
参数中替换x
,并将x
替换为宏体。那个时候,争论打破了; COMMA
被逗号标点符号替换是不相关的。但是,该逗号将分隔出由该宏生成的任何宏调用中出现的参数,因此如果必须保护这些参数,则需要更疯狂的东西。
您可以隐藏类似函数的宏背后的COMMA
,例如PAIR
:
#define COMMA ,
#define PAIR(A, B) A COMMA B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(PAIR(Foo<R, S>), Bar)
乍一看它更具吸引力,但可能存在缺点。它更混淆了。读者想知道,PAIR
背后是否存在语义?虽然COMMA
看起来过于迟钝而没有语义,但对于那些因与预处理器战斗而产生战斗伤痕的人来说,其目的很明显。
关于PAIR
,我们可能会隐藏它,并最终得到像Zwol的答案中的语法。但是我们需要DOCUMENTED_TYPEDEF
的多个变体。
另外,顺便说一句,让我们放弃在宏的右侧不需要的无用COMMA
:
#define PAIR(A, B) A, B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF_2(x2, y) class y : public PAIR x2 {};
#else
# define DOCUMENTED_TYPEDEF_2(x2, y) typedef PAIR x2 y;
#endif
DOCUMENTED_TYPEDEF_2((<R, S>), Bar)
$ gcc -std=c90 -E -Wall -pedantic comma-macro.c # 1 "comma-macro.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "comma-macro.c" # 11 "comma-macro.c" typedef <R, S> Bar;
这看起来可能适用于C99风格的可变参数宏。但是,这可能违反了评论中讨论的可移植性要求,更不用说这是C ++。为了未来的访客:
#define PNEUMATIC_COMMA_GUN(A, ...) A, ## __VA_ARGS__
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(xv, y) class y : public PNEUMATIC_COMMA_GUN xv {};
#else
# define DOCUMENTED_TYPEDEF(xv, y) typedef PNEUMATIC_COMMA_GUN xv y;
#endif
DOCUMENTED_TYPEDEF((<R, S, T, L, N, E>), Bar)
$ gcc -std=c99 -E -Wall -pedantic comma-macro.c # 1 "comma-macro.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "comma-macro.c" # 9 "comma-macro.c" typedef <R, S, T, L, N, E> Bar;
答案 1 :(得分:3)
无法更改预处理器将参数解析为宏的方式。不在括号内的逗号总是将宏参数分开。
DOCUMENTED_TYPEDEF((Foo<R,S>), Bar);
但当然只有在内部括号出现在宏的扩展中时才有效。如果这会在你正在展示的环境中引起问题,我不记得我的头脑。
如果可以要求C99可变参数宏,您可以使用它们去除额外的括号:
#define STRIP_PARENS(...) __VA_ARGS__
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public STRIP_PARENS x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef STRIP_PARENS x y;
#endif
DOCUMENTED_TYPEDEF((Foo<R,S>), Bar);
但现在你总是必须在DOCUMENTED_TYPEDEF的第一个参数周围放一对额外的括号。