在 C 编程中,将宏递归地应用于宏参数并不简单。然而,采用复杂的解决方法,可以实现具有所需功能的 MAP 宏。
为了启动递归,我们定义一个 MAP_OUT 宏,用作占位符宏扩展:
#define MAP_OUT
接下来,我们创建两个宏 A 和 B 来演示递归:
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
计算 A(blah) 会产生以下输出:
blah B (blah)
预处理器将 B(blah) 视为纯文本,因为它还不是宏调用。当此文本被重新处理时,它会扩展为生成:
blah blah A (blah)
通过不断地将输出反馈回预处理器,我们可以无限期地维持递归。
为了自动执行重复评估,我们使用 EVAL宏:
#define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__))) #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__))) #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__))) #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__))) #define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
EVAL 宏通过宏调用树传播其参数,将其计算计数乘以 365。
为了防止无限递归,我们需要一个终止机制。我们定义一个名为 MAP_END 的特殊宏:
#define MAP_END(...)
计算时,该宏不执行任何操作,有效地停止递归。
为了在递归宏和终止宏之间进行选择,我们引入 MAP_NEXT:
#define MAP_NEXT0(item, next, ...) next MAP_OUT #define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0) #define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
MAP_NEXT 将当前项目与列表末尾标记 () 进行比较。如果匹配则返回 MAP_END,否则返回下一个参数。
组合这些元素,我们可以创建 A 和 B 宏的实用版本:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__) #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
MAP0 和 MAP1 将操作 f 应用于当前项 x。然后,他们检查下一项,即 peek,以确定是继续还是结束递归。
最后,我们将所有内容与顶级 MAP 宏联系在一起:
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
MAP 位置参数列表上的列表结束标记并将其传递给 EVAL。
通过利用这些技术,您可以在 C 中实现递归宏功能,启用复杂的基于宏的处理。
以上是尽管存在宏扩展的限制,如何在 C 中实现递归宏功能?的详细内容。更多信息请关注PHP中文网其他相关文章!