c++ - 宏参数上的 Foreach 宏

标签 c++ c macros foreach

我想知道是否可以在宏参数上编写宏 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/

相关文章:

c++ - 在 C++ 中检测本地网络上的客户端-服务器连接设备

c++ - 为什么对 CreateWindow 的调用会失败,我该如何解决?

c - 查找由 malloc 函数分配的大小

rust - Rust-混合使用默认宏和个人默认实现

c++ - C++ 中的双重调度

c# - C#面板/图片框的问题

c - 迁移到 4.1.23 的 Netfilter 内核模块 - 无法检索 ip header

c - 从二维字符数组指针获取字符,该指针是 C 中结构指针的属性

c++ - 为什么这个宏会产生语法错误?

c - X-Macros 的实际使用