如何用C预处理器编写while循环?


71

我是从教育/黑客的角度提出这个问题的(我真的不想这样编码)。

是否可以仅使用C预处理程序指令来实现while循环。我知道宏不能递归扩展,那么如何实现呢?

Answers:



117

如果要实现while循环,则需要在预处理器中使用递归。进行递归的最简单方法是使用延迟表达式。延迟表达式是需要更多扫描才能完全扩展的表达式:

为什么这很重要?当宏被扫描和扩展时,它会创建一个禁用上下文。此禁用上下文将导致标记为蓝色,该标记指向当前正在扩展的宏。因此,一旦将其涂成蓝色,宏将不再扩展。这就是为什么宏不递归扩展的原因。但是,禁用上下文仅在一次扫描期间存在,因此通过延迟扩展,我们可以防止宏变成蓝色。我们只需要对表达式进行更多扫描即可。我们可以使用以下EVAL宏来做到这一点:

接下来,我们定义一些用于执行某些逻辑(例如if等)的运算符:

现在,使用所有这些宏,我们可以编写一个递归WHILE宏。我们使用WHILE_INDIRECT宏来递归地引用自身。这可以防止宏被涂成蓝色,因为它将在不同的扫描(和使用不同的禁用上下文)下扩展。该WHILE宏带有一个谓词宏,一个运算符宏和一个状态(即可变参数)。它将继续将此运算符宏应用于状态,直到谓词宏返回false(为0)。

出于演示目的,我们将创建一个谓词,以检查何时参数数量为1:

接下来,我们创建一个运算符,我们将只连接两个令牌。我们还创建了一个最终运算符(称为M),用于处理最终输出:

然后使用WHILE宏:

当然,任何种类的谓词或运算符都可以传递给它。


3
您能为宏添加一些解释吗?这确实很麻烦,但很难获得。所有这些交叉引用。我没有最后一行。什么是PRED因为它是由以定义x在这里:M(EVAL(WHILE(**PRED**, OP, x, y, z))) //Expands to xyz它is'nt秋毫
dhein

1
关于这一切如何工作的更多解释,在这之间有更多步骤:herehere。这些文章中的示例与此答案中的示例并不完全相同,但是它们足够接近。
Joris

漂亮的答案-只要可行。但不是xyz,而是扩展为WHILE ( PRED, OP, xy, z ),比照。coliru.stacked-crooked.com/a/1a7b4e7879bd7d4b
Armali

@Armali糟糕,有错字。应该OBSTRUCT(WHILE_INDIRECT)不会DEFER(WHILE_INDIRECT)。请参阅:coliru.stacked-crooked.com/a/a381ddcbcae0b890
保罗·富尔茨II


6

我为此使用元模板编程,一旦掌握了它,它就会很有趣。谨慎使用时非常有用。如前所述,由于它的访问已经完成,因此甚至可以使编译器陷入无限循环或堆栈溢出!就像发现自己的编译正在消耗超过30 GB的内存和所有CPU来编译无限循环代码一样,这简直就是天方夜谭!


6

好吧,这不是一个while循环,而是一个计数器循环,尽管如此,在干净的CPP中也可以循环(没有模板,也没有C ++)


4

这是滥用规则的行为,无法合法完成。编写自己的C预处理程序。使它以您想要的方式解释一些#pragma指令。


2
这不回答这个问题,其中明确表示,“C预处理”,而不是“一个C预处理器”。显然,这是指现有的工具链。
SOFe

2

当编译器胡思乱想并且不会为我展开某些循环时,我发现此方案很有用

#定义REPEAT20(x){x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x;}

REPEAT20(val = pleaseconverge(val));

但是恕我直言,如果您需要比这复杂得多的东西,那么您应该编写自己的预处理器。例如,您的预处理器可以为您生成一个适当的头文件,并且很容易将这一步骤包含在Makefile中,以使所有内容都可以通过一个命令顺利地编译。我做完了

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.