我想知道是否可以在宏参数上编写宏 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
技巧
阻止预处理器评估最终结果。
把它们放在一起
有了这些部分,现在就可以实现有用的版本
上面示例中的 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__)
这些宏将操作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/