[英]How do you make C++ create an expression that uses compile-time checking for constants and asserts for variables?
這是一個示例設置…宏或模板CHECKEXPR_RETURNVAL(EXPR,VAL),用於在返回VAL時檢查EXPR為TRUE。
這在很多地方都非常有用-例如在此高度簡化的示例中:
#define ISPOW2(VAL) ((0!=VAL)&&(0==(VAL&(VAL-1))))
#define _ALIGNPOW2(VAL,ALIGN) ((VAL+(ALIGN-1))&(~(ALIGN-1)))
#define ALIGNPOW2(VAL,ALIGN) CHECKEXPR_RETURNVAL( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
因此,困難是這樣的: 我想盡可能進行編譯時檢查,並且如果該值不是在編譯時確定的常量,則進行運行時檢查。
基本上,該想法是盡快捕獲不良參數。 如果您可以在編譯時捕獲到錯誤的參數,那比在運行時發現錯誤的參數要好。 同樣,常量初始化程序需要編譯時版本。
這是我兩次(失敗)嘗試,使單個版本可以在多個地方工作(作為恆定數組大小,作為枚舉初始化程序以及在具有變量的函數中)。 不幸的是,它們要么僅在編譯時(常量初始化程序)工作,要么僅在運行時工作-我想找出一個對兩者都適用的版本。
// CHECKEXPR_RETURNVAL - version "A"
#define CTCHECK_EXPR(EXP)(CTCheckBool<EXP>::ExistsZeroIfTrue)
template <bool bExpression> struct CTCheckBool {};
template <> struct CTCheckBool<true> {enum{ExistsZeroIfTrue=0};};
// Note: Plus ("+") is used rather than comma operator because
// the comma operator can not be used for constant initializers
#define CHECKEXPR_RETURNVAL_A(EXP,VAL) (CTCHECK_EXPR(EXP) + (VAL))
// Test Out version "A" -- works only for Compile Time Constants
#define ALIGNPOW2_A(VAL,ALIGN) CHECKEXPR_RETURNVAL_A( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_A[ALIGNPOW2_A(2,8)];
enum { AlignedVal_A = ALIGNPOW2_A(57,16) };
int TestAlignPow2_A(int val, int align)
{return(ALIGNPOW2_A(val,align));} // Compile Error
// CHECKEXPR_RETURNVAL - version "B"
template<typename T> T CHECKEXPR_RETURNVAL_B(bool bExpr,T val)
{ ASSERT(bExpr); return(val); }
// Test Out version "B" -- works only for Runtime Computed Values
#define ALIGNPOW2_B(VAL,ALIGN) CHECKEXPR_RETURNVAL_B( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_B[ALIGNPOW2_B(2,8)]; // Compile Error
enum { AlignedVal_B = ALIGNPOW2_B(57,16) }; // Compile Error
int TestAlignPow2_B(int val, int align)
{return(ALIGNPOW2_B(val,align));}
不幸的是,這兩個版本都不適用。 是否存在適用於所有情況的代碼結構?
似乎您真的會使用c ++ 0x constexpr
函數...
好吧...這不是一個完整的答案,但是我想您可以從中得出您想要的:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
fprintf(stderr, "wow\n");
}
};
template<> void S<0>::doIt(){
fprintf(stderr, "oops\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
您可以根據表達式獲得編譯器錯誤:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
ssdfkjehiu //deliberately invalid code
fprintf(stderr, "oops\n");
}
};
template<> void S<1>::doIt(){
fprintf(stderr, "wow\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);//uncomment it to make code compile
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
但是,導致錯誤的行將位於冗長的模板錯誤消息的中間。 例:
1.cpp(6) : error C2065: 'asdlfkjhasd' : undeclared identifier
1.cpp(4) : while compiling class template member function 'void S<I>::doIt(void)'
with
[
I=0
]
1.cpp(19) : see reference to class template instantiation 'S<I>' being compiled
with
[
I=0
]
1.cpp(6) : error C2146: syntax error : missing ';' before identifier 'fprintf'
如您所見,錯誤是由第19行引起的,該行在消息的中間提到。 這有點不方便。
我不能保證兩個示例都不依賴於某些未定義的C ++行為。
另外,我認為下一個代碼維護者可能對此不滿意...
附言:我認為您也應該看看增強功能。 如果我沒記錯的話,它有很多“魔術預處理器宏”(例如循環),因此有可能實現了類似的功能。
- 編輯 -
好吧,這個呢?
#include <stdio.h>
#include <string>
template <typename T> void a(T &i){
fprintf(stderr, "variable\n");
}
void a(const char* const i){
fprintf(stderr, "constant\n");
}
void a(bool i){
fprintf(stderr, "constant\n");
}
int main(int argc, char** argv){
int i;
float f;
std::string s;
const char* s1 = "b";
a(3);
a(3+2);
a(1.0f);
a('a');
a("b");
a(i);
a(f);
a(s);
a(s1);
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.