C++编译时常数检测
在某些情况下,库源可用,并且它通常必须支持可变参数,但实际上这些参数通常是常量。
然后,可以通过对常量参数进行特殊处理来优化事物(例如,使用静态数组而不是堆分配),但为此必须首先确定某些东西是否是常量(或者可能定义一些宏,但不太方便) )。
这是一个有效的实现。
更新:也在这里: http://codepad.org/ngP7Kt1V
- 它真的是有效的 C++ 吗?
- 有没有办法摆脱这些宏? (is_const() 不能是函数,因为函数依赖在数组大小表达式中不起作用;它也不能是模板,因为它也不接受可变参数。)
更新:N 不为 0,编译器不会为 if(N==0)
分支生成任何代码。 如果我们愿意的话,我们可以用同样的方式切换到完全不同的数据结构。 当然它并不完美,但这就是我发布这个问题的原因。
#include <stdio.h>
struct chkconst {
struct Temp { Temp( int x ) {} };
static char chk2( void* ) { return 0; }
static int chk2( Temp ) { return 0; }
};
#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(int))
#define is_const_0i(X) (sizeof(chkconst::chk2(X))>sizeof(char))
#define is_const(X) is_const_0( (X)^((X)&0x7FFFFFFF) )
#define const_bit(X1,bit) (is_const_0i((X1)&(1<<bit))<<bit)
#define const_nibl(X1,bit) const_bit(X1,bit) | const_bit(X1,(bit+1)) | const_bit(X1,(bit+2)) | const_bit(X1,(bit+3))
#define const_byte(X1,bit) const_nibl(X1,bit) | const_nibl(X1,(bit+4))
#define const_word(X1,bit) const_byte(X1,bit) | const_byte(X1,(bit+8))
#define const_uint(X1) const_word(X1,0) | const_word(X1,16)
#define const_switch_word( X1, X2 ) (is_const(X1) ? const_word(X1,0) : X2)
#define const_switch_uint( X1, X2 ) (is_const(X1) ? const_uint(X1) : X2)
const int X1 = 222;
const int X2 = printf( "" ) + 333;
char Y1[ const_switch_word(X1,256) ];
char Y2[ const_switch_word(X2,256) ];
template< int N >
void test( int N1 ) {
char _buf[N>0?N:1];
char* buf = _buf;
if( N==0 ) {
buf = new char[N1];
}
printf( "%08X %3i %3i\n", buf, N, N1 );
}
#define testwrap(N) test< const_switch_word(N,0) >( N )
int main( void ) {
printf( "%i %i %i\n", X1, is_const(X1), sizeof(Y1) );
printf( "%i %i %i\n", X2, is_const(X2), sizeof(Y2) );
testwrap( X1 );
testwrap( X2 );
}
There're cases when a library source is available, and it has to support variable parameters in general, but in practice these parameters are commonly constants.
Then it may be possible to optimize things by special handling of constant parameters (eg. use static arrays instead of heap allocation), but for that its necessary to determine whether something is a constant first (or maybe define some macros, but its less convenient).
So here's a working implementation.
Update: also here: http://codepad.org/ngP7Kt1V
- Is it really a valid C++ ?
- Is there a way to get rid of these macros? (is_const() can't be a function because the function dependence won't work in array size expression; also it can't be a template because that won't accept a variable parameter either. )
Update: Here's an update with something more like intended usage.
The compiler won't generate any code for the if(N==0)
branch if N
is not 0.
Same way we can switch to completely different data structures if we want.
Sure its not perfect, but that's why I posted this question.
#include <stdio.h>
struct chkconst { struct Temp { Temp( int x ) {} }; static char chk2( void* ) { return 0; } static int chk2( Temp ) { return 0; } }; #define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(int)) #define is_const_0i(X) (sizeof(chkconst::chk2(X))>sizeof(char)) #define is_const(X) is_const_0( (X)^((X)&0x7FFFFFFF) ) #define const_bit(X1,bit) (is_const_0i((X1)&(1<<bit))<<bit) #define const_nibl(X1,bit) const_bit(X1,bit) | const_bit(X1,(bit+1)) | const_bit(X1,(bit+2)) | const_bit(X1,(bit+3)) #define const_byte(X1,bit) const_nibl(X1,bit) | const_nibl(X1,(bit+4)) #define const_word(X1,bit) const_byte(X1,bit) | const_byte(X1,(bit+8)) #define const_uint(X1) const_word(X1,0) | const_word(X1,16) #define const_switch_word( X1, X2 ) (is_const(X1) ? const_word(X1,0) : X2) #define const_switch_uint( X1, X2 ) (is_const(X1) ? const_uint(X1) : X2) const int X1 = 222; const int X2 = printf( "" ) + 333; char Y1[ const_switch_word(X1,256) ]; char Y2[ const_switch_word(X2,256) ]; template< int N > void test( int N1 ) { char _buf[N>0?N:1]; char* buf = _buf; if( N==0 ) { buf = new char[N1]; } printf( "%08X %3i %3i\n", buf, N, N1 ); } #define testwrap(N) test< const_switch_word(N,0) >( N ) int main( void ) { printf( "%i %i %i\n", X1, is_const(X1), sizeof(Y1) ); printf( "%i %i %i\n", X2, is_const(X2), sizeof(Y2) ); testwrap( X1 ); testwrap( X2 ); }如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您使用 GCC,请使用 __builtin_constant_p 来告诉您某些内容是否是编译时常量。该文档包括示例,例如
If you're working with GCC, use
__builtin_constant_p
to tell you whether something is a compile time constant. The documentation includes examples likeis_const 应该更可靠。以 gcc-4.4 为例,如下:
prints:
GCC 是一个相当雄心勃勃的折叠常量表达式,按照标准的说法,它们不是整数常量表达式。 is_const 的一个可能更好的定义可能是:
除此之外,你的技术很棒,因为我终于可以编写一个 SUPER_ASSERT 宏,如果断言表达式在编译时检查,则在编译期间检查,否则在运行时检查:
我会研究一下const_switch_xxx() 稍后再说。我不知道如何实现另一种方式,解构/重建技巧非常棒。
is_const should be more reliable. On gcc-4.4 for example, the following:
prints:
GCC is quite ambitious folding constant expressions which are not integral constant expressions by the words of the standard. A potentially better definition of is_const could be:
Aside from that, your technique is awesome, because I can finally write a SUPER_ASSERT macro which is checked during compilation if the assertion expression if compile-time and during runtime otherwise:
I'll look into that const_switch_xxx() thing later. I have no idea how to implement another way, the deconstruct/reconstruct trick is brilliant.
如果您可以传入模板参数,那么它一定是 constexpr(编译时表达式的标准术语)。如果它不是通过模板参数传递的,那么它不是 constexpr。这是没有办法解决的。
更容易的是使用 alloca 手动滚动堆栈分配的可变长度数组类。这将保证数组的堆栈分配,无论它们是否是静态的。此外,您还可以获得与 vector/boost::array 相同的迭代功能。
请注意,我还没有实际测试过这段代码,但它比在你的 OP 中维护这个庞然大物更容易获取、使用和维护。
If you can pass in a template parameter then it is guaranteed to be a constexpr (the Standard's term for compile-time expressions). If it's not passed by template parameter, then it's not a constexpr. There is no way around this.
What would be much easier is to hand-roll a stack allocated variable length array class using alloca. This will guarantee stack allocation for arrays, regardless of whether or not they're static or not. In addition, you can get much of the same iteration functionality of a vector/boost::array.
Note that I haven't actually tested this code, but it would be MUCH easier to grab, use, and maintain than to maintain that monstrosity in your OP.