简体   繁体   English

您如何使C ++创建一个表达式,该表达式使用编译时检查常量并声明变量?

[英]How do you make C++ create an expression that uses compile-time checking for constants and asserts for variables?

Here's an example setup… a macro or a template CHECKEXPR_RETURNVAL(EXPR,VAL) that checks that EXPR is TRUE while returning VAL. 这是一个示例设置…宏或模板CHECKEXPR_RETURNVAL(EXPR,VAL),用于在返回VAL时检查EXPR为TRUE。

This is useful in a variety of places -- like in this highly simplified example: 这在很多地方都非常有用-例如在此高度简化的示例中:

#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) )

So, the difficulty is this: I want to do compile time checks if possible, and if the value is not a constant that is determinate at compile time, then do a runtime check. 因此,困难是这样的: 我想尽可能进行编译时检查,并且如果该值不是在编译时确定的常量,则进行运行时检查。

Basically, the idea is to catch bad parameters as soon as possible; 基本上,该想法是尽快捕获不良参数。 if you can catch a bad parameter at compile time that's better than finding out at run time. 如果您可以在编译时捕获到错误的参数,那比在运行时发现错误的参数要好。 Also, the compile time version is required for constant initializers. 同样,常量初始化程序需要编译时版本。


Here are my two (failed) attempts to make single version work in multiple places (as a constant array size, as an enum initializer, and in a function with variables). 这是我两次(失败)尝试,使单个版本可以在多个地方工作(作为恒定数组大小,作为枚举初始化程序以及在具有变量的函数中)。 Unfortunately, they either work for the compile time only (constant initializer) or the runtime only -- I would like to figure out a version that will work for both. 不幸的是,它们要么仅在编译时(常量初始化程序)工作,要么仅在运行时工作-我想找出一个对两者都适用的版本。

// 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));}

Unfortunately, neither version works for all three cases. 不幸的是,这两个版本都不适用。 Is there a code structure that will work for all the cases ? 是否存在适用于所有情况的代码结构?

似乎您真的会使用c ++ 0x constexpr函数...

Well... Not a complete answer, but I think you can derive what you want from this: 好吧...这不是一个完整的答案,但是我想您可以从中得出您想要的:

#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;
}

You can get a compiler error based on expression: 您可以根据表达式获得编译器错误:

#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;
}

But the line that causes error will be in a middle of a lengthy template error message. 但是,导致错误的行将位于冗长的模板错误消息的中间。 Example: 例:

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'

As you see, error is caused by line 19, which is mentioned in the middle of the message. 如您所见,错误是由第19行引起的,该行在消息的中间提到。 This is a bit inconvenient. 这有点不方便。

I cannot guarantee that both examples doesn't rely on some undefined C++ behavior. 我不能保证两个示例都不依赖于某些未定义的C ++行为。

Also, I think that the next code maintainer may not be happy about this... 另外,我认为下一个代码维护者可能对此不满意...

PS I think you should also take a look at boost. 附言:我认为您也应该看看增强功能。 If I remember correctly, it had quite a lot of "magic preprocessor macro" (loops, for example), so it is possible that it implemented something similar. 如果我没记错的话,它有很多“魔术预处理器宏”(例如循环),因此有可能实现了类似的功能。

--edit-- - 编辑 -
Okay, what about this one?: 好吧,这个呢?

#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.

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