简体   繁体   English

c ++编译时断言

[英]c++ compile-time assertion

I'm using a macro that might be dangerous: 我正在使用一个可能很危险的宏:

#define REMAINDER(v, size) ((v) & (size -1))

obviously it assumes that size is a power of 2. 显然它假设大小是2的幂。

I would like to make sure size is indeed a power of 2, but at compile time. 我想确保大小确实是2的幂,但在编译时。 (testing at run time is easy, but is NOT what I want). (在运行时测试很容易,但不是我想要的)。

A sufficient test for me would be that size is always a constant (never a variable). 对我来说一个充分的测试就是大小总是一个常数(从不变量)。

I would use BOOST_STATIC_ASSERT , but I cannot figure out how to use it for what I need. 我会使用BOOST_STATIC_ASSERT ,但我无法弄清楚如何将它用于我需要的东西。

First things first: that micro optimization is not needed. 首先要做的事情是:不需要微观优化。 Any decent compiler with optimizations enabled will convert a % b into that construct when b is a compile time constant that is actually a power of 2. b是编译时常量(实际上是2的幂)时,任何启用了优化的合适编译器都会将a % b转换为该构造。

Then on the particular assert, you can use the same construct to assert it [*]: 然后在特定断言上,您可以使用相同的构造来断言它[*]:

BOOST_STATIC_ASSERT( !(size & (size-1)) );

[*] Note that as Matthieu M points out this only works if size is an unsigned type. [*]请注意,正如Matthieu M指出的那样,只有在size为无符号类型时才有效。 And that should be asserted --the lesser requirement that the argument is non-negative cannot be asserted at compile time: 应该断言 - 在编译时不能断言参数是非负的较小要求:

BOOST_STATIC_ASSERT( (X(0)-1) > X(0) ); // where X is the type of the argument

EDIT after last comment: 最后评论后编辑:

You are missing the point here. 你错过了这一点。 For a static assertion macro to work, the size must be a compile time constant. 要使静态断言宏起作用, size必须是编译时常量。 If it is a compile time constant then just assert when the constant is defined which is also the best place, as it will serve as documentation, and will point to the precise point of code that needs modification: 如果它是一个编译时常量,那么只要在定义常量时断言这也是最佳位置,因为它将作为文档,并指向需要修改的代码的精确点:

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 %
   //...
};

At the same time that asserting that the invariant is held at compile time is important for efficiency, obscuring the code is not: Just leave the modulo operation in place, as the compiler will optimize: 同时声明在编译时保持不变量对于效率很重要,模糊代码不是:只需将模运算保留在原位,因为编译器将优化:

std::size_t hash_map::index_of( std::size_t hash ) const {
   return hash % size;
}

Because size is a compile time constant, and it is a power of two (you asserted that before) the optimizer will translate % into the optimized operation, while the code is still readable by humans that need to maintain it. 因为size是一个编译时常量,并且它是2的幂(你之前断言过),优化器会将%转换为优化操作,而代码仍然需要维护它的人可读。

EDIT: Unless you have profiling proof that this is a bottleneck in your code AND that your compiler isn't optimizing thing like (v % 8) appropriately, just write the obvious code. 编辑:除非你有分析证明这是你的代码中的瓶颈,并且你的编译器没有适当地优化(v%8),只需编写明显的代码。

Otherwise since you're dealing with integers here can you use a template instead of a macro? 否则,因为你在这里处理整数,你可以使用模板而不是宏吗? Then you should be able to static assert inside the template so it'll flag when it's instantiated incorrectly. 然后你应该能够在模板内部静态断言,这样它就会在它被错误地实例化时标记出来。

For example something like: 例如:

template <int size>
int remainder(int v)
{
    BOOST_STATIC_ASSERT(!(size & (size - 1)));

    return v & (size -1);
};

assert on: 断言:

size && (size & (size - 1) == 0)

EDIT: Explanation. 编辑:解释。

If size is a power of two, only one bit is set. 如果size是2的幂,则只设置一位。 Subtracting 1 will yield a value where all bits up to the original is set. 减1将产生一个值,其中所有位都设置为原始位。 ANDing these gives 0. ANDing这些给出0。

If size is not a power of two, at least two bits are set. 如果大小不是2的幂,则设置至少两位。 Subtracting 1 will yield a value where all bits up to the original's lowest set bit is set. 减1将产生一个值,其中所有位都设置为原始的最低设置位。 ANDing these will not yield 0, since the second and later (from right) bits are still set. 对这些进行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))

Don't treat your users like idiots. 不要像白痴一样对待你的用户。 Document your functions and macros, and move on. 记录您的功能和宏,然后继续。


Alternatively if you really must molly-coddle people, use a template: 或者,如果你真的必须愚弄人,请使用模板:

template <size_t SIZE>
size_t remainder(size_t v) {
   // Perform whatever assertions you like on SIZE here

   return v & (SIZE - 1);
}

Note that I've had to make some assumptions about the type of input and output. 请注意,我必须对输入和输出的类型做出一些假设。

Peephole Optimization : Optimization performed on a very small set of instructions 窥视孔优化 :在非常小的指令集上执行优化

This includes: 这包括:

  • Constant Folding : eg, here size - 1 would be evaluated during compilation if size is constant 常量折叠 :例如,如果size是常量,则在编译期间将评估size - 1
  • Strength Reduction : which consists in replacing slow operations by faster ones... when the result is equivalent (obviously) 强度减少 :包括用更快的速度替换慢速操作......当结果相同时(显然)

Now, let's see an example of this using the LLVM backend optimizer : 现在,让我们看一下使用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
}

Let's analyze, would you ? 我们分析吧,好吗?

  • u / 4 yields and i32 %u, 3 (which translated back in C gives u & 3 ) u / 4收益率and i32 %u, 3 (换算成C表示u & 3
  • i / 4 yields srem i32 %i, 4 (which translated back in C gives i % 4 ) i / 4产生srem i32 %i, 4 (在C中翻译为i % 4

What a smart compiler ! 多么聪明的编译器 It didn't forget that performing bitwise operations on signed integers would not yield the result I wanted (whenever the integer is negative, and branches are more expensive than divisions). 它并没有忘记对有符号整数执行按位运算不会产生我想要的结果(只要整数是负数,并且分支比分支更昂贵)。

Morale: This kind of optimization is near useless, and you even got it wrong. 士气:这种优化几乎没用,你甚至弄错了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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