繁体   English   中英

c ++编译时断言

[英]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);
}

请注意,我必须对输入和输出的类型做出一些假设。

窥视孔优化 :在非常小的指令集上执行优化

这包括:

  • 常量折叠 :例如,如果size是常量,则在编译期间将评估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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM