【问题标题】:C Preprocessor: Evaluate macro earlyC 预处理器:及早评估宏
【发布时间】:2015-12-28 01:36:48
【问题描述】:

考虑以下设置:

啊.h

#define A 5
#define B A
#undef A
#define A 3

交流

#include "a.h"
#include <stdio.h>

int main()
{
    printf("%d\n", B);
    return 0;
}

虽然这非常合理地打印 3,但有没有办法让它打印 5,即已经在 a.h 的第二行用 5 替换 A?

【问题讨论】:

  • 我认为你做不到。我会等待比我更有知识的人来证明我错了。
  • 这些不是赋值,而是使用时的文本替换。 A 永远不会有值 5 或 3,B 永远不会等于 5 或​​ 3。
  • @WeatherVane 感谢您的提醒。我相应地编辑了措辞。
  • 您可能会考虑将 a.h 更改为 const int B = A; 而不是 #define B A - 这样它会在编译时捕获 A 的值。另一种选择是使用非标准但广泛实现的(gcc、clang、visual C++ ...)#pragma push_macro("A") 来保留A 的原始值,然后在评估B 之前将其弹出,但这既繁琐又脆弱.
  • const int 有效。 ideone.com/MpiOOz

标签: c macros c-preprocessor


【解决方案1】:

不,没有办法做到这一点。除非您知道A 的所有可能值,并且它们始终是整数,在这种情况下您可以费力地依次测试每个值:

#if A == 0
# define B 0
#elif A == 1
# define B 1
#elif A == 2
# define B 2
/*  ... and a very long etc. */
#endif

如果您的用例仅涉及整数,则您有更多选择。例如,您可以将 B 声明为 static const intenum(取决于语言)而不是宏,这显然会使用宏的当前值。如果你真的想要宏,Boost 预处理库有一个 implementation 上面的 #ifs 的费力序列(巧妙地减少了 log(N) 而不是 N 所需的预处理器语句的数量)。


#define 预处理器指令中没有宏替换; §6.10 段涵盖了这一事实。 C 标准的第 7 条(C++ 标准的第 16 段第 6 段,措辞相同):

除非另有说明,否则预处理指令中的预处理标记不受宏扩展的影响。

#if#include 指令的描述中,标准规定确实会发生宏替换,这就是上述#if 解决方案有效的原因(以及Boost 实现,它也使用计算的#include )。

【讨论】:

    【解决方案2】:

    是的。 Boost 的预处理器库(一组可移植的包含,而不是扩展的预处理器)包括support for "mutable" macro definitions。除了定义一个宏来直接扩展为一个值,您可以将其定义为扩展为一个可变槽的引用,它可以更改,因为它会提前扩展“分配”给它的值。在这种情况下,您对更改值的能力不太感兴趣,而是对这种早期扩展意味着它可以在使用 B 或重新定义之前的某个时刻从 A 中获取值A.

    #include <boost/preprocessor/slot/slot.hpp>
    
    #define A 5
    #define B BOOST_PP_SLOT(1)
    
    // "assign" A to B
    #define BOOST_PP_VALUE A
    #include BOOST_PP_ASSIGN_SLOT(1)
    
    #undef A
    #define A 3
    
    #include "a.h"
    #include <stdio.h>
    
    int main()
    {
        printf("%d\n", B);  // 5
        return 0;
    }
    

    支持仅限于整数。它利用#if 指令强制扩展任何包含的宏这一事实(#line#error 也是如此,尽管它们对此目的不是很有用),并使用它们来构建一个 等价的分配给槽的整数值,存储在隐藏的后端宏中。这样它就可以从A 中“提取”一个值,然后B 可以引用该值本身,即使A 更改或被删除。

    【讨论】:

    • #include BOOST_PP_ASSIGN_SLOT(1)... 这太恶心了!我们不需要混淆 C++ 代码竞赛,我们有提升!
    猜你喜欢
    • 1970-01-01
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 2016-05-08
    • 2020-02-09
    • 1970-01-01
    相关资源
    最近更新 更多