2017-01-19 134 views
3

我需要将在编译时进行评估宏,是这样的:C代码宏评估在编译时

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

这个简单的算法是否可以在编译时进行评估?

+0

AFAIK你不能做到这一点标准的C或C++,但gcc的预处理器有[this](https://gcc.gnu.org/onlinedocs/cpp/Directives-Within-Macro-Arguments.html#Directives-Within-Macro-Arg ),也许这可以帮助: –

+2

'(x> 16? 32:x> 8? 16:x> 4? 8'等 –

+1

它看起来像你想[围绕2的下一个力量](http://stackoverflow.com/questions/466204/rounding-up-to-nearest-power-of-2),或[特别是例如](http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2) –

回答

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; 
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被定义为具有外部链接的正常(非内联)功能时,该功能仍然有效。

+0

感谢您的努力。有一个更简单的解决方案,请参阅M.M的建议。 – Danijel

+1

@Danijel:M.M的建议非常简洁,因为它允许将条件打包为单个表达式,而不是多个语句。我的答案的重点在于证明它不再是必需的,因为现代编译器会优化代码(产生最终结果),同时它更具可读性和安全性(例如考虑SCALE(x ++))。 –

+0

当然,明白了...... – Danijel

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

对于位娱乐的,我翻译该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的回答都是可以参考的。)

+0

对不起,没有时间来测试这个。因为速度不是问题,所以我使用了一个更直接的解决方案:'(x> 16?32:x> 8?16:x> 4?8'等。 – Danijel

+0

当然,我绝对不会使用上面的宏也是一个概念的证明。 – dbrank0