酒吧;
在进行任何替换之前,会解析宏参数列表中的括号和逗号。然后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 "逗号宏.c"
# 1 ""
# 1 ""
# 1 "逗号宏.c"
# 11 "逗号宏.c"
typedef 条形;
这看起来可以用 C99 风格的可变参数宏来实现。但是,这可能违反了 cmets 中讨论的可移植性要求,更不用说这是 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 "逗号宏.c"
# 1 ""
# 1 ""
# 1 "逗号宏.c"
# 9 "逗号宏.c"
typedef 条形;