第45章 マクロの落ち穂拾い

 今回はマクロについての補足をつらつらとします。トークン結合子以上にマニアックな話かもしれませんね(汗)。


 では、今回の要点です。


 では、いってみましょう。


 例えば、ある範囲でしか有効にならないマクロを作りたいとします。マクロは定義したところ以下ずっと使えるので、これをどうにかしたいわけです。

 そういうときは、マクロの定義を解除すればいいのです。そのための命令が #undef です。

プログラム1
// Undef1.cpp
#include <iostream.h>

struct SStudent
{
    char szName[16];
    int  nAge;
};

void Func(SStudent& student)
{
#define NAME(i)  (student.szName[i])

    int i;

    for(i = 0; NAME(i) != 0; i++)
        cout << NAME(i);
    cout << endl;

#undef NAME
}

int main()
{
    SStudent student = { "赤井 孝", 17 };
    Func(student);
    cout << NAME(0) << endl;  // エラー

    return 0;
}

 このように、#undef 以降は NAME マクロを使うことは出来なくなります

 また、#undef 以降で NAME マクロを定義しても、二重定義エラーは出ません。つまり、前の定義を別の定義で置き換えたいときにも #undef は使えるわけです。


 そしてもう1つ。前回の assert マクロに関連してですが、デバッグ文のエラーメッセージにマクロの引数を表示したいと思います。例えば、ある値がある範囲から外れていたら abort するような文を作りたいと思い、次のようなマクロを定義するとします。

#define assert_minmax(val, min, max) \
    if((min) < = (val) && (val) <= (max)) ; \
    else _assert_minmax(???, ???, ???, __FILE__, __LINE__)

???のところにはエラーメッセージに使う文字列を入れたいとします。1つ目が val を、2つ目が min を、3つ目が max を文字列化したものとします。

 しかし、

#define assert_minmax(val, min, max) \
    if((min) <= (val) && (val) <= (max)) ; \
    else _assert_minmax("val", "min", "max", __FILE__, __LINE__)

としたところで、マクロの引数は文字列の中まで展開しないので無駄です(注:コンパイラの中には展開する仕様にしてあるものもあるかもしれません)。

 そこで、こういうときは次のようにします。

#define assert_minmax(val, min, max) \
    if((min) < = (val) && (val) <= (max)) ; \
    else _assert_minmax(#val, #min, #max, __FILE__, __LINE__)

 このように、マクロ引数の前に # をつければ文字列として展開されます(注:古いコンパイラではサポートしていない可能性もあります)。すなわち、

assert_minmax(index, 0, m_nSize - 1);

if((0) <= (index) && (index) <= (m_nSize - 1)) ;
else _assert_minmax("index", "0", "m_nSize - 1", <ファイル名>, <行番号>);

と展開されます(注:見やすいように途中で改行しましたが、実際には1行に展開されます)。" や \ のようにエスケープシーケンス(第1部第78章参照)が必要なものも、勝手に \ をつけてくれるので心配要りません。

 あとは _assert_minmax 内でこれを表示してやればいいわけですね。


 では、今回の要点です。


 それでは、次回まで。


第44章 デバッグ文 | 第46章 ハンドルされない例外

Last update was done on 2001.5.5

この講座の著作権はロベールが保有しています