2017-01-19 130 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