如何使预处理器宏贪婪

How to make a preprocessor macro greedy?

本文关键字:贪婪 处理器 何使预      更新时间:2023-10-16

我们有以下预处理器宏。它用于帮助Doxygen文档,因为Doxygen在C++和一些模板类型defs:方面存在问题

#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(并且它不会生成文档)。

如何使预处理器宏变得贪婪?

你不会喜欢这样的:

#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逗号宏.c#1"逗号宏.c"#1"<内置>"#1"<命令行>"#1"逗号宏.c"#9"逗号宏.c"typedef Foo<R、S>酒吧

在进行任何替换之前,都会分析宏参数列表中的括号和逗号。然后COMMA被替换为x变元,x被替换为宏体。到那时,争论就结束了;CCD_ 8被逗号标点符号替换是不相关的。但是,逗号分隔该宏生成的任何宏调用中出现的参数,因此,如果必须保护这些参数,则需要更疯狂的方法。

您可以将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-迂腐的逗号宏.c#1"逗号宏.c"#1"<内置>"#1"<命令行>"#1"逗号宏.c"#11"逗号宏.c"typedef<R、 S>酒吧

这看起来可能适用于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-迂腐的逗号宏.c#1"逗号宏.c"#1"<内置>"#1"<命令行>"#1"逗号宏.c"#9"逗号宏.c"typedef<R、 S、T、L、N、E>酒吧

无法更改预处理器解析宏参数的方式。不在括号内的逗号总是分隔宏参数。

可能能够做的是

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