【问题标题】:C macro evaluate at compile timeC 宏在编译时求值
【发布时间】:2017-06-03 23:17:04
【问题描述】:

我需要一个可以在编译时评估的宏,例如:

#define FIND_RANGE(x) \
if x>16 \
32 \
elif x>8 \
16 \
elif x>4 \
8 \
elif x>2 \
4 \
elif x>1 \
2 \
else \
1 \
endif \

所以代码

#define S1 FIND_RANGE(7)
unsinged int i = S1;

将作为

发送到编译器
unsinged int i = 8;

能否完成这个简单的算法,以便在编译时对其进行评估?

【问题讨论】:

  • AFAIK 你不能在标准 C 或 C++ 中做到这一点,但 gcc 的预处理器有 this,也许这可以帮助:
  • (x > 16 ? 32 : x > 8 ? 16 : x > 4 ? 8
  • @M.M 这行得通,非常感谢。

标签: c c-preprocessor


【解决方案1】:

事实证明这是可行的,甚至很简单:

#define POW00          1.0f
#define POW01          2.0f
#define POW02          4.0f
#define POW03          8.0f
#define POW04         16.0f
#define POW05         32.0f
#define POW06         64.0f
#define POW07        128.0f
#define POW08        256.0f  // use some nicer pow2 constant generation

#define SCALE(x) ( \
x > POW07 ? POW08 : \
x > POW06 ? POW07 : \
x > POW05 ? POW06 : \
x > POW04 ? POW05 : \
x > POW03 ? POW04 : \
x > POW02 ? POW03 : \
x > POW01 ? POW02 : \
x > POW00 ? POW01 : POW00 \
) // end SCALE

例子:

int main()
{
   float a = (float)SCALE(7.0f);
}

这在编译时被评估为

float a = 8.0f;

【讨论】:

    【解决方案2】:

    虽然 C 没有 constexpr 函数,但 GCC 和 Clang 都可以在编译时使用 -O1 评估简单函数。相关优化称为constant folding

    以下C代码:

    #include <stdio.h>
    
    static inline unsigned int findRange(unsigned int x)
    {
        if (x > 16)
            return 32;
        else if (x > 8)
            return 16;
        else if (x > 4)
            return 8;
        else if (x > 2)
            return 4;
        else if (x > 1)
            return 2;
        return 1;
    }
    
    int main(void)
    {
        unsigned int i = findRange(7);
        printf("%u\n", i);
        return 0;
    }
    

    生成 x86-64 汇编代码(参考:godbolt.org/g/kVYe0u):

    main:
            sub     rsp, 8
            mov     esi, 8
            mov     edi, OFFSET FLAT:.LC0
            mov     eax, 0
            call    printf
            mov     eax, 0
            add     rsp, 8
            ret
    

    如您所见,对findRange 的调用由值替换,该值是在编译时计算的。

    即使findRange 被定义为具有外部链接的普通(非内联)函数,这仍然有效。

    【讨论】:

    • 感谢您的努力。有一个更简单的解决方案,请参阅 M.M 建议。
    • @Danijel:MM 的建议很简洁,因为它允许将条件打包到单个表达式中,而不是多个语句中。我的回答的重点是证明它不再需要,因为现代编译器将优化代码(产生最终结果),同时它更具可读性和安全性(例如考虑SCALE(x++))。
    【解决方案3】:

    为了消遣,我将bit twiddling hack mentioned by Sander 翻译成一个宏:

    #define XS(x,y) (x | (x>>y))
    #define FR(x) XS(XS(XS(XS(XS(x-1,1),2),4),8),16)+1
    

    所以 FR(7) 应该在编译时给出 8,依此类推。

    (*但出于所有实际目的,Grzegorz Szpetkowski 的答案是可以参考的。)

    【讨论】:

    • 抱歉,没有时间测试这个。由于速度不是这里的问题,我使用了更直接的解决方案:(x &gt; 16 ? 32 : x &gt; 8 ? 16 : x &gt; 4 ? 8 等。
    • 当然,除了概念证明之外,我永远不会使用上述宏。
    【解决方案4】:

    我认为你不能那么容易做到。问题是预处理器可用的条件是预处理器指令。

    您可以做的是创造性地使用#include 指令来创建更高级的构造。创建find-range.mac 为:

    #if x>16
    32
    #elif x>8
    16
    #elif x>4
    8
    #elif x>2
    4
    #elif x>1
    2
    #else
    1
    #endif
    #undef x
    

    然后将其用作:

    int i = 
    #define x 7
    #include "find-range.mac"
    ;
    

    应该扩展为:

    int i =
    8
    ;
    

    另一个没有成功的技巧是通过粘合将FIND_RANGE(x) 替换为FIND_RANGEx,然后适当地定义FIND_RANGEx。这要求x 在一组有限的值中:

    #define FIND_RANGE(x) FIND_RANGE ## x
    #define FIND_RANGE1 1
    #define FIND_RANGE2 2
    #define FIND_RANGE3 4
    #define FIND_RANGE4 4
    #define FIND_RANGE5 8
    #define FIND_RANGE6 8
    #define FIND_RANGE7 8
    #define FIND_RANGE8 8
    // etc...
    

    【讨论】:

      猜你喜欢
      • 2015-07-28
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 1970-01-01
      相关资源
      最近更新 更多