Here’s an example setup… a macro or a template CHECKEXPR_RETURNVAL(EXPR,VAL) that checks that EXPR is TRUE while returning VAL.
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 ?