2-6. プリプロセッサ

公開日: 17:15 2. 応用編/2-6. プリプロセッサ

プリプロセッサはディレクティブであり、実際のコンパイルが始まる前に情報を前処理するようコンパイラに命令します。

すべてのプリプロセッサディレクティブは#で始まり、前には空白文字プリプロセッサディレクティブはC++の構文ではないので、最後にセミコロン(;)がつきません。

これまで#includeディレクティブをすべての例で見てきました。このマクロはヘッダファイルをソースファイルへ組み込むために使われます。

C++では#include、#define、#if、#else、#lineなどのプリプロセッサディレクティブがサポートされています。重要なディレクティブについて見ていきます。

#defineプリプロセッサ

#defineプリプロセッサディレクティブはシンボル定数を作成します。シンボル定数はマクロと呼ばれ、一般的な形式は次のようなものです。
#define マクロ名 置換テキスト

ファイルでこの行が現れた時、後続に出てくる全てのマクロ名はプログラムがコンパイルされる前に置換テキストに置き換えられます。
#include <iostream>
using namespace std;

#define PI 3.14159

int main ()
{
 
    cout << "PIの値 :" << PI << endl; 

    return 0;
}
このコードの前処理して結果を確認してみましょう。ソースファイルがあれば、-Eオプションを付けてコンパイルし結果をtest.pにリダイレクトしてみましょう。test.pを開くと多くの情報があり、ファイル下部で次のように値が置き換われば成功です。

$g++ -E test.cpp > test.p
...
int main ()
{
 
    cout << "PIの値 :" << 3.14159 << endl; 

    return 0;
}

擬似関数マクロ

#defineを使って引数を取るマクロを定義できます。
#include <iostream>
using namespace std;

#define MIN(a,b) (((a)<(b)) ? a : b)

int main ()
{
   int i, j;
   i = 100;
   j = 30;
   cout <<"最小値は " << MIN(i, j) << endl;

    return 0;
}
上のコードをコンパイルし実行すると、次の結果が得られます。
最小値は 30

条件付きコンパイル

ソースコードの一部を選んでコンパイルできるディレクティブがあります。この処理は条件付きコンパイルと呼ばれます。

条件付きプリプロセッサの構築は多くの場合if選択構造のようになります。つぎのプリプロセッサコードを見てみましょう。
#ifndef NULL        // NULLが定義されていなければ
   #define NULL 0   // NULLを0と定義
#endif

プログラムをデバッグ用にコンパイルし単一のマクロを使ってデバッグを切り替える事ができます。
#ifdef DEBUG
   cerr <<"Variable x = " << x << endl;
#endif

#ifdef DEBUGより前にシンボル定数DEBUGが定義されていた場合、cerr構文をコンパイルさせます。次のようにして#if 0構文を使いプログラムの一部をコメントアウトできます。
#if 0
   コードがコンパイルされるのを防ぐ
#endif

次の例を試してみましょう。
#include <iostream>
using namespace std;
#define DEBUG

#define MIN(a,b) (((a)<(b)) ? a : b)

int main ()
{
   int i, j;
   i = 100;
   j = 30;
#ifdef DEBUG
   cerr <<"Trace: main関数の開始" << endl;
#endif

#if 0
   /* コメント部分 */
   cout << MKSTR(HELLO C++) << endl;
#endif

   cout <<"最小値は " << MIN(i, j) << endl;

#ifdef DEBUG
   cerr <<"Trace: main関数を抜ける" << endl;
#endif
    return 0;
}

上のコードをコンパイルし実行すると、次の結果が得られます。
Trace: main関数の開始
最小値は 30
Trace: main関数を抜ける

#と##演算子

#と##のプリプロセッサ演算子はC++とANSI/ISO準拠のCに於いて利用可能です。#演算子は置換テキスト記号を引用符で囲まれた文字列に変換させます。 次のマクロ定義を見てみましょう。
#include <iostream>
using namespace std;

#define MKSTR( x ) #x

int main ()
{
    cout << MKSTR(HELLO C++) << endl;

    return 0;
}
上のコードをコンパイルし実行すると、次の結果が得られます。 HELLO C++ どのように動いているのか確認してみます。C++プリプロセッサ次の行を
  cout << MKSTR(HELLO C++) << endl;

次のように置き換えていることがわかります。
  cout << "HELLO C++" << endl;

##演算子は2つの記号を連結するのに使われます。
#define CONCAT( x, y )  x ## y

CONCATがプログラムに出てきた時、引数が連結されマクロを置き換えるのに使われます。例えば、CONCAT(HELLO,C++) は"HELLO C++"に置き換えられます。


#include <iostream>
using namespace std;

#define concat(a, b) a ## b
int main()
{
   int xy = 100;
   
   cout << concat(x, y);
   return 0;
}
上のコードをコンパイルし実行すると、次の結果が得られます。
100

動作を確認してみましょう。C++プリプロセッサの変形は簡単に確認できます。
  cout << concat(x, y);

これが次のようになります。
  cout << xy;

C++の事前定義されたマクロ

C++は事前定義されたマクロを備えています。
マクロ説明
__LINE__コンパイルされた時に現在の行数が入ります。
__FILE__コンパイルされた時に現在のファイル名が入ります。
__DATE__ソースファイルからオブジェクトコードへ変換された日付が月/日/年の形式の文字列で入ります。
__TIME__プログラムがコンパイルされた時の時間が時:分:秒の形式の文字列で入ります。

上のマクロをすべて使ってみましょう。
#include <iostream>
using namespace std;

int main ()
{
    cout << "Value of __LINE__ : " << __LINE__ << endl;
    cout << "Value of __FILE__ : " << __FILE__ << endl;
    cout << "Value of __DATE__ : " << __DATE__ << endl;
    cout << "Value of __TIME__ : " << __TIME__ << endl;

    return 0;
}
コンパイルし実行すると、次の結果が得られます。
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 27 2016
Value of __TIME__ : 18:52:48
  • ?±??G???g???[?d????u?b?N?}?[?N???A

0 件のコメント :

コメントを投稿