[英]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.