さて、私はこの構文の代替が言及されていないことにかなり驚いています。もう1つの一般的な(しかし古い)メカニズムは、定義されていない関数を呼び出し、アサーションが正しい場合はオプティマイザーを使用して関数呼び出しをコンパイルすることです。
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
このメカニズムは機能しますが(最適化が有効になっている限り)、リンクするまでエラーを報告しないという欠点があります。その場合、関数you_did_something_bad()の定義が見つかりません。これが、カーネル開発者が負のサイズのビットフィールド幅や負のサイズの配列(後者はGCC 4.4でのビルドの破壊を停止した)などのトリックを使い始めた理由です。
コンパイル時のアサーションの必要性に同情して、GCC 4.3は、この古い概念を拡張できるerror
関数属性を導入しましたが、選択したメッセージでコンパイル時エラーを生成します-不可解な「負のサイズの配列はもうありません」 "エラーメッセージ!
#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)
実際、Linux 3.9以降、compiletime_assert
この機能を使用するマクロが呼び出さbug.h
れ、それに応じてほとんどのマクロが更新されています。それでも、このマクロは初期化子として使用できません。ただし、by ステートメント式(別のGCC C拡張)を使用すると、次のことができます。
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})
このマクロはパラメーターを1回だけ評価し(副作用がある場合)、「5を指定しないように言った!」というコンパイル時エラーを作成します。式が5に評価されるか、コンパイル時定数でない場合。
では、なぜ負のサイズのビットフィールドの代わりにこれを使用しないのですか?悲しいかな、現在、ステートメント式の使用には多くの制限があります。たとえば、ステートメント式が完全に定数である(つまり、完全に評価できる)場合でも、定数列挙子(列挙型定数、ビットフィールド幅など)としての使用を含みます。コンパイル時およびそれ以外の場合は__builtin_constant_p()
テストに合格)。また、関数本体の外では使用できません。
うまくいけば、GCCがこれらの欠点をすぐに修正し、定数ステートメント式を定数初期化子として使用できるようになります。ここでの課題は、正当な定数式とは何かを定義する言語仕様です。C ++ 11は、このタイプまたはモノのみにconstexprキーワードを追加しましたが、C11には対応するものがありません。C11はこの問題の一部を解決する静的アサーションを取得しましたが、これらの欠点のすべてを解決することはありません。したがって、gccがconstexpr機能を-std = gnuc99&-std = gnuc11などの拡張機能として使用できるようにして、ステートメント式での使用を許可できることを願っています。al。