セットアップの例を次に示します。マクロまたはテンプレート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) )
したがって、難しさは次のとおり です。可能であればコンパイル時のチェックを実行し、値がコンパイル時に決定される定数でない場合は、ランタイムチェックを実行します。
基本的に、アイデアは悪いパラメータをできるだけ早くキャッチすることです。コンパイル時に不良パラメータをキャッチできる場合は、実行時に検出するよりも優れています。また、定数初期化子にはコンパイル時バージョンが必要です。
これが、単一バージョンを複数の場所で機能させるための2つの(失敗した)試みです(一定の配列サイズとして、列挙型初期化子として、および変数を持つ関数として)。残念ながら、これらはコンパイル時のみ(一定の初期化子)またはランタイムのみで機能します。両方で機能するバージョンを見つけたいと思います。
// 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));}
残念ながら、どちらのバージョンも3つのケースすべてで機能しません。すべての場合に機能するコード構造はありますか?