c++templatescompile-timestatic-assert

in-expression compile-time check for constant expressions for older G++


I have the following macros for certain compile-time checks:

/* in-expression compile-time check evaluating to 0 */
#ifdef __cplusplus
template<bool> struct Chk_sa;
template<> struct Chk_sa<true>{};
#define Chk(test) (sizeof((Chk_sa<!!(0+(test))>())) * 0)
#else
#define Chk(test) (sizeof(struct { int (Chk):((0+(test)) ? 1 : -1); }) * 0)
#endif

/* ensure value x is a constant expression */
#ifdef __cplusplus
template<bool,bool> struct CEx_sa;
template<> struct CEx_sa<true,false>{};
template<> struct CEx_sa<false,true>{};
#define CEx(x) (sizeof((CEx_sa<!!(0+(x)), !(0+(x))>())) * 0 + (x))
#else
#define CEx(x) (sizeof(struct { int (CEx):(((0+(x)) && 1) + 1); }) * 0 + (x))
#endif

/* usage like this: */
struct ChkTest {
        int expr1[Chk(3*3) + 1];  // supposed to be [1]
        int expr2 : CEx(3*3);     // supposed to be : 9
};

However, the C++ part of this fails with a parse error already for uses of the Chk macro, at the ) before the * 0 part. (I included the CEx part for completeness, as I’ll need both fixed.)

How can I change these to work with older G++ (GCC) versions? Specifically, these work on Debian etch (G++ 4.1.1) and fail on Debian sargs (G++ 3.3.5) and older.

(Note: I’m a C programmer but don’t really know C++.)


Update: the precise error message was requested. I saved the snippet above into a file x:

$ gcc -x c -c x
$ gcc -x c++ -c x
x:22: error: parse error before `)' token
x:23: error: parse error before `)' token

Update: this is notably not an is_constexpr but a compile-time check to ensure that the expression is compile-time constant which in itself is usable inside a constant expression and aborts compilation if it’s not a compile-time constant.


Solution

  • I managed to figure it out:

     template<bool> struct mbccChkExpr_sa;
    -template<> struct mbccChkExpr_sa<true>{};
    -#define mbccChkExpr(test)      (sizeof((mbccChkExpr_sa<!!(0+(test))>())) * 0)
    +template<> struct mbccChkExpr_sa<true> { typedef int Type; };
    +#define mbccChkExpr(test)      (static_cast<mbccChkExpr_sa<!!(0+(test))>::Type>(0))
    

    and:

     template<bool,bool> struct mbccCEX_sa;
    -template<> struct mbccCEX_sa<true,false>{};
    -template<> struct mbccCEX_sa<false,true>{};
    -#define mbccCEX(x)             (sizeof((mbccCEX_sa<!!(0+(x)), !(0+(x))>())) * 0 + (x))
    +template<> struct mbccCEX_sa<true,false> { typedef int Type; };
    +template<> struct mbccCEX_sa<false,true> { typedef int Type; };
    +#define mbccCEX(x)             (static_cast<mbccCEX_sa<!!(0+(x)), !(0+(x))>::Type>(0) + (x))
    

    I still wonder if I can somehow put the type of x into the latter so I can do static_cast<…>(x) instead of static_cast<…>(0) + (x) but this already works, also on OpenWatcom and sarge’s g++.