如何使预处理器宏贪婪?

时间:2016-04-08 18:42:07

标签: c++ macros c-preprocessor doxygen greedy

我们有以下预处理器宏。它用于帮助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<RS>,Bar(并且它不会生成文档)。

如何让预处理器宏贪婪?

2 个答案:

答案 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的第一个参数周围放一对额外的括号。