【问题标题】:Incrementing Preprocessor Macros递增预处理器宏
【发布时间】:2011-02-17 05:06:21
【问题描述】:

我正在尝试制作一个简单的预处理器循环。 (我意识到这是一个可怕的想法,但是哦。)

// Preprocessor.h

#ifndef PREPROCESSOR_LOOP_ITERATION

#define MAX_LOOP_ITERATION 16 // This can be changed.

#define PREPROCESSOR_LOOP_ITERATION 0

#endif

#if (PREPROCESSOR_LOOP_ITERATION < MAX_LOOP_ITERATION)
#define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1) // Increment PREPROCESSOR_LOOP_ITERATION.
#include "Preprocessor.h"
#endif

问题在于它看起来不像PREPROCESSOR_LOOP_ITERATION 正在递增,所以它只是无限地包含自己。如果我将该行更改为实际整数(如17),预处理器会正确跳过#include 指令。

我做错了什么?

【问题讨论】:

    标签: c++ loops c-preprocessor


    【解决方案1】:

    “问题”是宏被延迟评估。考虑你的宏定义:

    #define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1)
    

    这定义了一个名为PREPROCESSOR_LOOP_ITERATION 的宏,它的替换列表是五个预处理标记(PREPROCESSOR_LOOP_ITERATION+1) 的序列。定义宏时,宏不会在替换列表中展开。宏替换仅在您调用宏时发生。考虑一个更简单的例子:

    #define A X
    #define B A
    
    B // this expands to the token X
    
    #undef A
    #define A Y
    B // this expands to the token Y
    

    还有一条附加规则,如果在替换列表中遇到被替换的宏的名称,则它不会被视为宏,因此不会被替换(这有效地禁止了宏替换期间的递归)。因此,在您的情况下,每当您调用 PREPROCESSOR_LOOP_ITERATION 宏时,它都会被替换为

    ( PREPROCESSOR_LOOP_ITERATION + 1 )
    

    然后宏替换停止并继续使用下一个标记进行预处理。

    您可以通过定义一系列宏并使用串联 (##) 运算符来使用预处理器执行有限的算术运算,但这非常乏味。您应该考虑使用Boost.Preprocessor 库来帮助您解决这个问题。它适用于 C 和 C++ 代码。它允许有限的迭代,但它所允许的却非常有用。与您的用例最接近的功能可能是BOOST_PP_ITERATE。序列 (BOOST_PP_SEQ) 处理程序等其他工具对编写生成代码非常有帮助。

    【讨论】:

      【解决方案2】:

      编辑: 正如 James 指出的那样,由于对宏的懒惰评估,我的原始解决方案不起作用。如果您的编译器支持它,则宏 __COUNTER__ 每次调用时都会递增 1,您可以使用它来执行简单的预处理器循环,如下所示:

      // Preprocessor.h
      #define MAX_LOOP_ITERATION 16 // Be careful of off-by-one
      
      // do stuff
      
      #if (__COUNTER__ < MAX_LOOP_ITERATION)
      #include "Preprocessor.h"
      #endif
      

      我在 Visual C 中通过运行 cl /P Preprocessor.h 验证了这一点。

      【讨论】:

      • 宏求值是惰性的,所以这里的#if指令中,PREPROCESSOR_LOOP_ITERATION被替换为TEMPVALUE,没有定义为宏,所以宏替换就停止了。
      • 如你所说。将我的答案更新为可行的方法,尽管可能依赖于编译器。
      • 附带说明,__COUNTER__ 宏的一些其他用途出现在此处的另一个问题中:stackoverflow.com/questions/652815/…
      【解决方案3】:

      说真的,想办法做到这一点。

      预处理器应该被降级为包括守卫和简单的条件编译。

      它曾经有用的所有其他东西在 C++ 中都有更好的方法(内联、模板等)。

      您声明I realize this is a horrible idea ... 的事实应该是一个死的赠品,您应该重新考虑您在做什么:-)

      我的建议是您退后一步,告诉我们您正在尝试解决的真正问题。我怀疑实现递归宏不是问题,它是解决您遇到的问题的一种方法。了解根本问题将开启各种其他奇妙的可能性。

      【讨论】:

      • +1。如果你在编译时确实需要这种滥用,使用更强大的预处理器,例如m4
      • 真的不存在“真正的”问题。为了制作预处理器循环,我想制作一个预处理器循环。
      • @Maxpm:你的空闲时间太多了。回去工作,或与家人共度时光,或去攀岩之类的:-)
      • 我理解答案,但我不同意。在可变参数模板之前,预处理器循环是最接近的事情。 (关于可维护性和可扩展性,我的意思是。)
      • interesting things 中的 all sorts 只能使用 macros 才能完成(好吧,最后一个有点开玩笑,但另外两个很严肃)。预处理器绝对很烂:它的行为在很多情况下非常复杂而且非常有限,但是如果你想在不使用任何外部工具的情况下生成代码,它通常是完成这项工作的最佳工具。
      猜你喜欢
      • 2012-11-16
      • 1970-01-01
      • 2013-11-03
      • 1970-01-01
      • 2015-04-15
      • 2012-03-27
      • 2015-07-31
      • 1970-01-01
      相关资源
      最近更新 更多