#条件ビルド
C/C++などでコードを書いている時、ある条件の時だけ有効あるいは無効にしたいコードというものがある。これはデバッグ用マクロのような話ではない。デバッグコードはリリース時に消すが、リリース用に条件Aと条件Bのバイナリを別々に作る場合のことだ。
#define INUSE_CASE_A //CASE Aの時だけ有効にするコード
#define INUSE_CASE_B //CASE Bの時だけ有効にするコード
#ifdef INUSE_CASE_A
CASE A用のコード
#endif
#ifdef INUSE_CASE_B
CASE B用のコード
#endif
こんなやつ。
#でも #ifndefには気を付けよう。
開発が進み、条件Cが追加された。
条件CはAまたはBの時だけ有効で、Cが定義済みなら無効にしたい。
#ifndef INUSE_CASE_C
この条件はAまたはBの時だけ有効になるコードだろうか?
正解はCが未定義の全部で有効なコードだ。
AとBが未定義でもC以外だから有効だ。それは意図したものだろうか?
開発が進み、条件Dができたとする。
AとBで有効にしたいコードはDでも有効にするべきだろうか?
#trueとfalseとnotの話
複数条件で#ifdefならtrue/falseだが、#ifndefは「以外のすべて」になる。
ついつい**#ifndef一発でいけるやん!**と思いがちだが、注意が必要だ。
#if defined(INUSE_CASE_A) || defined(INUSE_CASE_B)
AまたはBの時だけ有効にしたい処理
#endif
これならAまたはBが定義済みの場合のみ有効になる。
AとBが両方必須なら && にすればいい。これなら間違いない。
少なくとも、意図せぬ見えない条件は追加されない。
例はマクロ定義だが、コードの条件判定でも基本は同じである。
また、たとえ正常に動作すると知っていても not 判定は避けたい。
//Ex1
while(true) {
char* buf = get_buffer_ptr(); //内部終了条件でnullptrを返す
if(!buf) {
終了処理
break;
}
buf を使った処理;
}
//Ex2
bool bExec = true
while(bExec) {
char* buf = get_buffer_ptr(); //内部終了条件でnullptrを返す
if(buf) {
buf を使った処理;
} else {
終了処理
bExec = false;
}
}
一見するとEx1のほうがシンプルだが、否定条件やbreak脱出を意識する。
否定条件は「それ以外の全部」が何を指すか考えなければならない。
否定条件を使いたくなったら「それ以外の全部でいいか?」を意識したい。