[英]c++ compile-time assertion
我正在使用一個可能很危險的宏:
#define REMAINDER(v, size) ((v) & (size -1))
顯然它假設大小是2的冪。
我想確保大小確實是2的冪,但在編譯時。 (在運行時測試很容易,但不是我想要的)。
對我來說一個充分的測試就是大小總是一個常數(從不變量)。
我會使用BOOST_STATIC_ASSERT
,但我無法弄清楚如何將它用於我需要的東西。
首先要做的事情是:不需要微觀優化。 當b
是編譯時常量(實際上是2的冪)時,任何啟用了優化的合適編譯器都會將a % b
轉換為該構造。
然后在特定斷言上,您可以使用相同的構造來斷言它[*]:
BOOST_STATIC_ASSERT( !(size & (size-1)) );
[*]請注意,正如Matthieu M指出的那樣,只有在size
為無符號類型時才有效。 應該斷言 - 在編譯時不能斷言參數是非負的較小要求:
BOOST_STATIC_ASSERT( (X(0)-1) > X(0) ); // where X is the type of the argument
最后評論后編輯:
你錯過了這一點。 要使靜態斷言宏起作用, size
必須是編譯時常量。 如果它是一個編譯時常量,那么只要在定義常量時斷言,這也是最佳位置,因為它將作為文檔,並指向需要修改的代碼的精確點:
template <typename N>
class hash_map {
public:
const std::size_t size = N;
BOOST_STATIC_ASSERT( !(size & (size-1) ) ); // N must be power of 2 for fast %
//...
};
同時聲明在編譯時保持不變量對於效率很重要,模糊代碼不是:只需將模運算保留在原位,因為編譯器將優化:
std::size_t hash_map::index_of( std::size_t hash ) const {
return hash % size;
}
因為size
是一個編譯時常量,並且它是2的冪(你之前斷言過),優化器會將%
轉換為優化操作,而代碼仍然需要維護它的人可讀。
編輯:除非你有分析證明這是你的代碼中的瓶頸,並且你的編譯器沒有適當地優化(v%8),只需編寫明顯的代碼。
否則,因為你在這里處理整數,你可以使用模板而不是宏嗎? 然后你應該能夠在模板內部靜態斷言,這樣它就會在它被錯誤地實例化時標記出來。
例如:
template <int size>
int remainder(int v)
{
BOOST_STATIC_ASSERT(!(size & (size - 1)));
return v & (size -1);
};
斷言:
size && (size & (size - 1) == 0)
編輯:解釋。
如果size是2的冪,則只設置一位。 減1將產生一個值,其中所有位都設置為原始位。 ANDing這些給出0。
如果大小不是2的冪,則設置至少兩位。 減1將產生一個值,其中所有位都設置為原始的最低設置位。 對這些進行AND運算不會產生0,因為仍然設置了第二個和后一個(從右)位。
1000 & 0111 == 0000
1100 & 1011 == 1000
// Only use this if size is a power of 2
#define REMAINDER(v, size) ((v) & (size -1))
不要像白痴一樣對待你的用戶。 記錄您的功能和宏,然后繼續。
或者,如果你真的必須愚弄人,請使用模板:
template <size_t SIZE>
size_t remainder(size_t v) {
// Perform whatever assertions you like on SIZE here
return v & (SIZE - 1);
}
請注意,我必須對輸入和輸出的類型做出一些假設。
窺視孔優化 :在非常小的指令集上執行優化
這包括:
現在,讓我們看一下使用LLVM后端優化器的示例:
// C
int iremainder(int i) { return i % 4; }
unsigned uremainder(unsigned u) { return u % 4; }
// LLVM IR
define i32 @iremainder(i32 %i) nounwind readnone {
entry:
%0 = srem i32 %i, 4 ; <i32> [#uses=1]
ret i32 %0
}
define i32 @uremainder(i32 %u) nounwind readnone {
entry:
%0 = and i32 %u, 3 ; <i32> [#uses=1]
ret i32 %0
}
我們分析吧,好嗎?
u / 4
收益率and i32 %u, 3
(換算成C表示u & 3
) i / 4
產生srem i32 %i, 4
(在C中翻譯為i % 4
) 多么聰明的編譯器 ! 它並沒有忘記對有符號整數執行按位運算不會產生我想要的結果(只要整數是負數,並且分支比分支更昂貴)。
士氣:這種優化幾乎沒用,你甚至弄錯了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.