前回に引き続き、C++のプリプロセッサの復習です。
前回は#includeと#defineについて記事にしましたが、【まとめ】にも記載の通り、#defineと関連するプリプロセッサは多くあります。
今回は「#ifdef (#if defined)」「#ifndef (#if ! defined)」「#undef」について記事にしようと思います。
実際の開発でもデバッグなどで活用の幅が広いので、普段から使い方に慣れておくと、人のソースを読み込む際に役立ちます。
前回の記事はちょっと雑すぎたので、まずプリプロセッサディレクティブの一覧を示します。
Contents
プリプロセッサ・ディレクティブ
#○○のような形式で記述する命令語をプリプロセッサ・ディレクティブといいます。
プリプロセッサ・ディレクティブは、cファイルやcppファイルをコンパイラに渡す前に処理を実行するため、関数などのオーバーヘッドが発生せず有用に使うことができればかなり効果的に動きます。
全部で14種類あり、それぞれの簡単な説明を下記に示します。
#define | 識別子とトークン文字列の関連付けを行い、以降コード中に識別子が見つかるたびに指定したトークン文字列と置換を行う。 |
---|---|
#error | コンパイル時にユーザー指定のエラーメッセージを出力し、コンパイルを終了する。 |
#import | タイプライブラリーの組み込みを行う。 |
#undef | #defineで作成した識別子を削除します。 |
#elif | #else ifの略。#ifと#else、もしくは#ifと#endifの間に記述可能。 |
#if | 整数定数、文字定数、演算子を記述でき、条件付きコンパイルに使用します。 |
#include | ヘッダーファイルなどのインクルードファイルを組み込みます。 |
#using |
|
#else | #if/#elifと#else、もしくは#if/#elifと#endifの間に記述可能。 |
#ifdef | #defineで定義されていればTRUEとなるディレクティブです。 |
#line | コンパイラが管理する行番号およびファイル名を変更します。 |
#endif | #ifと対になるディレクティブ。#ifの終わりを示す。 |
#ifndef | #defineで定義されていなければTRUEとなるディレクティブです。 |
#pragma |
|
#define / #ifdef / #ifndef
#defineディレクティブは「識別子とトークン文字列の関連付けをしたり、マクロを作成できる。」ということは前回お話ししました。
1 |
#define DEBUG 1 |
上記のように記述すれば、以降ソースコード内に「DEBUG」を見つけると「1」に置換します。
#ifdef
#ifdefと#ifndefは、「識別子が#defineによって定義されているか」を判定します。
よく見るコードでは以下のようなものです。
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> #define DEBUG 1 int main(int argc, char *argv[]) { #ifdef DEBUG std::cout << "これはDEBUGが定義されていれば出力されます。" << std::endl; #endif std::cout << "デバッグ中に関わらず出力します。" << std::endl; } |
このようにデバッグ中は識別子「DEBUG」を定義しておき、確認用のメッセージなどをいれることでデバッグがしやすくなります。
リリースの際は「#define DEBUG 1」を削除すれば、コンパイラが「#ifdef DEBUG ~ #endif」を無視してくれるので余分なコードが生成されることもありません。
#ifndef
既になんとなく分かっている方も多いかもしれませんが、#ifndefディレクティブは「DEBUGが定義されていなければ~」という判定を行います。
1 2 3 4 5 6 7 8 9 |
#include <iostream> #define RELEASE 1 int main(int argc, char *argv[]) { #ifndef RELEASE std::cout << "RELEASEが定義されていなければ出力される。" << std::endl; #endif } |
新しくコードを作成する場合は#ifを使おう
この記事を作成するためにMSDNを見ていると、#ifdefや#ifndefは非推奨とまではいかなくとも、#if, #elif, #endifを使用するようにされていました。
現在は過去のC++との互換性維持のために残されているだけとなっているようですね。
新しくコードを作る際に、同様の動作をさせたい場合は以下のように作成するべきです。
1 2 3 4 5 6 7 8 9 |
#include <iostream> #define DEBUG 1 int main() { #if defined(DEBUG) std::cout << "DEBUGが定義されていれば出力します。" << std::endl; #endif } |
1 2 3 4 5 6 7 |
#include <iostream> int main() { #if !defined(RELEASE) std::cout << "RELEASEが定義されていなければ出力します。" << std::endl; #endif } |
まとめ
MSDNでは#ifとdefined(identifier)を使用するようにと記載されていますが、実際にはまだまだ#ifdef/#ifndefは使われていますし、見る機会もかなり多いと思います。
if文もそうですが入れ子などが複雑になってくると、かなり分かりづらくなりますので急に慌てないようにしっかり理解していなければいけませんね