C宏在编译时评估

时间:2017-01-19 09:55:28

标签: c c-preprocessor

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

#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;

可以完成这个简单的算法,以便在编译时进行评估吗?

4 个答案:

答案 0 :(得分:7)

虽然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被定义为具有外部链接的正常(非内联)函数,这也有效。

答案 1 :(得分:2)

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

但是,您可以创造性地使用#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...

答案 2 :(得分:2)

为了娱乐,我将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的回答是可以参考的。)

答案 3 :(得分: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;