1

这是一个示例设置……一个宏或一个模板 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));}

不幸的是,这两个版本都不适用于所有三种情况。是否有适用于所有情况的代码结构?

4

2 回答 2

0

嗯......不是一个完整的答案,但我认为你可以从中得出你想要的:

#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++ 行为。

另外,我认为下一个代码维护者可能对此不满意......

PS我认为你也应该看看boost。如果我没记错的话,它有很多“魔术预处理器宏”(例如循环),所以它可能实现了类似的东西。

--edit--
好的,这个呢?:

#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;
}
于 2010-08-03T23:28:28.607 回答
0

看起来你真的会使用 c++0xconstexpr函数......

于 2010-08-03T22:29:23.373 回答