Вот пример настройки ... макрос или шаблон CHECKEXPR_RETURNVAL (EXPR, VAL), который проверяет, что EXPR равен TRUE при возврате VAL.
Это полезно во многих местах - как в этом очень упрощенном примере:
#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));}
К сожалению, ни одна из версий не работает для всех трех случаев. Существует ли структура кода, которая будет работать для всех случаев?