gpt4 book ai didi

c++ - 宏参数上的 Foreach 宏

转载 作者:行者123 更新时间:2023-11-30 16:22:32 26 4
gpt4 key购买 nike

我想知道是否可以在宏参数上编写宏 foreach 。这是想要做的事情:

#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ?

以及可能的用法:

int a = 1, b = 3, d = 0;
PRINT_ALL(a,b,d);

这是我迄今为止所取得的成就

#define FIRST_ARG(arg,...) arg
#define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))

这是一个递归宏,这是非法的。另一个问题是递归的停止条件

最佳答案

是的,使用一种奇特的解决方法可以在 C 中使用递归宏。最终目标是创建一个 MAP 宏,其工作方式如下:

#define PRINT(a) printf(#a": %d", a)
MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */

基本递归

首先,我们需要一种技术来发出看起来像宏的东西调用,但尚未:

#define MAP_OUT

假设我们有以下宏:

#define A(x) x B MAP_OUT (x)
#define B(x) x A MAP_OUT (x)

计算宏A (blah)会产生输出文本:

blah B (blah)

预处理器看不到任何递归,因为 B (blah) 调用是此时只是纯文本,B 甚至不是当前的名称宏。将此文本反馈回预处理器会扩展调用,产生输出:

blah blah A (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__)))

每个级别都会乘以之前级别的努力,评估输入总共365次。换句话说,调用 EVAL (A (blah)) 将生成单词 blah 的 365 个拷贝,然后是最终未评估的 B (blah)。这提供了递归的基本框架,至少在一个一定的堆栈深度。

结束检测

下一个挑战是当递归到达末尾时停止递归列表。

基本思想是发出以下宏名称而不是正常的宏名称当需要退出时递归宏:

#define MAP_END(...)

评估该宏不会执行任何操作,从而结束递归。

要在两个宏之间进行实际选择,请使用以下MAP_NEXT宏将单个列表项与特殊列表结尾标记进行比较()。如果该项目匹配,则宏返回 MAP_END,否则返回 下一个参数(如果该项目是其他项目):

#define MAP_GET_END() 0, MAP_END
#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_GET_END 宏旁边。如果这样做形成了一个宏调用,所有内容都通过一个插槽移动MAP_NEXT0 参数列表,更改输出。 MAP_OUT 技巧阻止预处理器评估最终结果。

把它们放在一起

有了这些部分,现在就可以实现有用的版本上面示例中的 AB 宏:

#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__)

这些宏将操作f应用于当前列表项x。他们然后检查下一个列表项,peek,看看它们是否应该继续。

最后一步是将所有内容连接到顶级 MAP 宏中:

#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))

该宏在列表末尾放置一个 () 标记,以及一个额外的0 用于 ANSI 合规性(否则,最后一次迭代将具有非法0 长度列表)。然后它通过 EVAL 传递整个内容并返回结果。

我已将此代码上传为 library on github为了您的方便。

关于c++ - 宏参数上的 Foreach 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54384444/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com