#if、#elif、#else、および #endif ディレクティブ (C/C++)
#if ディレクティブは、#elif、#else、#endif の各ディレクティブと共にソース ファイルの各部分のコンパイルを制御します。 #if の後に記述した式がゼロ以外の値になる場合、#if ディレクティブの直後の一連の行が翻訳単位として保持されます。
構文
conditional :
if-part elif-partsopt else-partopt endif-line
if-part :
if-line text
if-line :
#if constant-expression
#ifdef identifier
#ifndef identifier
elif-parts :
elif-line text
elif-parts elif-line text
elif-line :
#elif constant-expression
else-part :
else-line テキスト
else-line :
#else
endif-line :
#endif
解説
ソース ファイルの各 #if ディレクティブは、末尾の #endif ディレクティブと対応している必要があります。 任意の数の #elif ディレクティブが、#if ディレクティブと #endif ディレクティブの間に出現する可能性がありますが、最大 1 つの #else ディレクティブが許可されます。 #else ディレクティブ (存在する場合) は、#endif の前の最後のディレクティブである必要があります。
#If、#elif、#else、#endif の各ディレクティブは、他の #if ディレクティブの text 部分の中に入れ子にできます。 入れ子になった各 #else、#elif、#endif ディレクティブは、先行する直近の #if ディレクティブに属します。
#If と #ifdef などのすべての条件付きコンパイル ディレクティブは、ファイルの終わりの前にある終了 #endif ディレクティブと一致する必要があります。 それ以外の場合は、エラー メッセージが生成されます。 条件付きコンパイル ディレクティブがインクルード ファイルに含まれている場合も同じ条件を満たす必要があります。つまり、インクルード ファイルの終わりに対になる条件付きコンパイル ディレクティブが必要です。
マクロの置き換えは、#elif コマンドの後の行の一部で実行されるので、マクロの呼び出しは constant-expression で使用できます。
プリプロセッサは、見つかったいずれかの text を選択して、後続の処理に使用します。 text で指定されるブロックは、テキストのシーケンスです。 複数行にまたがる場合があります。 通常、text はコンパイラまたはプリプロセッサに対して意味を持つプログラム テキストです。
プリプロセッサは選択した text を処理し、それをコンパイラに渡します。 text にプリプロセッサ ディレクティブが含まれている場合、プリプロセッサでそれらのディレクティブが実行されます。 プリプロセッサによって選択されたテキスト ブロックだけがコンパイルされます。
プリプロセッサは、#if または #elif ディレクティブに続く定数式を、true (ゼロ以外) の定数式が見つかるまで評価することで、1 つの text 項目を選択します。 これは、# で始まる他のプリプロセッサ ディレクティブも含むすべてのテキストを、関連付けられた #elif, #else, or #endif まで選択します。
見つかった constant-expression がすべて false であるか、または #elif ディレクティブが存在しない場合、プリプロセッサは #else 句の後のテキスト ブロックを選択します。 #else 句がなく、#if ブロック内の constant-expression のすべてのインスタンスが false の場合、テキスト ブロックは選択されません。
constant-expression は、次のような追加の制限がある整数定数式です。
式は、整数型である必要があり、整数定数、文字定数、defined 演算子のみ含めることができます。
この式は
sizeof
または型キャスト演算子を使用できません。ターゲット環境は整数のすべての範囲を表現できるとは限りません。
変換は、型
int
を型long
と同じ方法で表し、unsigned int
をunsigned long
と同じ方法で表します。トランスレーターは、ターゲット環境とは別のコード値のセットに文字定数を翻訳できます。 ターゲット環境のプロパティを確認するには、その環境を構築したアプリを使用して、LIMITS.H マクロの値を確認します。
この式は環境に対してクエリを実行することはできません。また、対象のコンピューターで実装の詳細から分離された状態を維持する必要があります。
プリプロセッサ演算子
定義済み
プリプロセッサ演算子 defined は、次の構文に示すように、特別な定数式で使用できます。
defined( identifier )
defined identifier
この定数式は identifier がそのとき定義されている場合は true (ゼロ以外) と見なされます。 それ以外の場合、条件は False (0) です。 空のテキストとして定義された識別子は、定義されていると見なされます。 defined 演算子は、#if と #elif ディレクティブでは使用できますが、それ以外の場所には使用できません。
次の例では、#if ディレクティブと #endif ディレクティブが、3 つの関数呼び出しの 1 つに対するコンパイルを制御します。
#if defined(CREDIT)
credit();
#elif defined(DEBIT)
debit();
#else
printerror();
#endif
credit
の関数呼び出しは、CREDIT
識別子が定義されている場合、コンパイルされます。 DEBIT
識別子が定義されている場合、debit
の関数呼び出しがコンパイルされます。 どちらの識別子も定義されていない場合、printerror
の呼び出しがコンパイルされます。 CREDIT
と credit
の両方は、大文字小文字が異なるため、C および C++ で別々の識別子になります。
次の例の条件付きコンパイル ステートメントは、DLEVEL
というシンボリック定数が定義済みとします。
#if DLEVEL > 5
#define SIGNAL 1
#if STACKUSE == 1
#define STACK 200
#else
#define STACK 100
#endif
#else
#define SIGNAL 0
#if STACKUSE == 1
#define STACK 100
#else
#define STACK 50
#endif
#endif
#if DLEVEL == 0
#define STACK 0
#elif DLEVEL == 1
#define STACK 100
#elif DLEVEL > 5
display( debugptr );
#else
#define STACK 200
#endif
最初の #if ブロックには、入れ子になった #if、#else、#endif の各ディレクティブの 2 つのセットが表示されます。 最初のセットのディレクティブは、DLEVEL > 5
が true の場合にのみ処理されます。 そうでない場合は、#else の後のステートメントが処理されます。
2 つ目の例の #elif and #else ディレクティブは、DLEVEL
の値に基づいて 4 つの選択肢のいずれかを作成するために使用されます。 STACK
の定義により、定数 DLEVEL
は 0、100、または 200 に設定されます。 DLEVEL
が 5 より大きい場合、次のステートメントがコンパイルされます。
#elif DLEVEL > 5
display(debugptr);
コンパイルされ、STACK
は定義されません。
条件付きコンパイルの一般的な用途は、同じヘッダー ファイルの多重インクルードを防ぐことです。 C++ では、クラスをヘッダー ファイルで定義することが多いため、多重定義を防ぐためにこのようなコンストラクターを使用できます。
/* EXAMPLE.H - Example header file */
#if !defined( EXAMPLE_H )
#define EXAMPLE_H
class Example
{
//...
};
#endif // !defined( EXAMPLE_H )
前のコードは、シンボリック定数 EXAMPLE_H
が定義されているかどうかを確認します。 定義されている場合、ヘッダー ファイルは既にインクルードされており、再処理の必要はありません。 定義されていない場合、EXAMPLE.H が処理されてから、EXAMPLE_H
が処理済みとマークされます。
__has_include
Visual Studio 2017 バージョン 15.3 以降: ライブラリ ヘッダーを含めることができるかどうかを判断します。
#ifdef __has_include
# if __has_include(<filesystem>)
# include <filesystem>
# define have_filesystem 1
# elif __has_include(<experimental/filesystem>)
# include <experimental/filesystem>
# define have_filesystem 1
# define experimental_filesystem
# else
# define have_filesystem 0
# endif
#endif