関数類似マクロ

オブジェクト類似マクロよりも複雑な関数類似マクロ定義では、括弧内の仮パラメーターの名前を、 コンマで区切って宣言します。仮パラメーター・リストは空であってもかまいません。 そのようなマクロは引き数を取らない関数をシミュレートするために使用できます。C では、数が可変の引き数を持つ関数類似のマクロのサポートが追加されています。

C++ C++ では、C との互換性のための言語拡張として、数が可変の引き数を持つ関数類似マクロがサポートされています。

関数類似マクロの定義
小括弧に囲まれたパラメーター・リストおよび置換トークンが後ろに続く ID。パラメーターを置換コード内に組み込みます。 空白で、ID (マクロの名前) とパラメーター・リストの左括弧とを分離することはできません。 コンマで各パラメーターを分離することが必要です。

移植性のため、1 つのマクロに は、パラメーターが 31 を超えないようにする必要があります。 このパラメーター・リストの最後には、省略符号 (...) を使用できます。 この場合、ID __VA_ARGS__ を置換リストに挿入することができます。

関数類似マクロの起動
小括弧に入れられ、コンマで区切られた引き数のリストが後に続く ID。引き数の 数は、定義内のパラメーター・リストが省略符号で終了していない限り、マクロ定義内のパラメーターの数に一致している必要があります。 後者の場合、起動における引き数の数は、マクロ定義内のパラメーター数よりも多くなければなりません。 この過剰分の引き数は後続引き数 と呼ばれます。プリプロセッサーは、関数類似マクロの起動を確認すると、引き数の置換を行い ます。 置換コード内のパラメーターは、対応する引き数に置き換えられます。後続の引き数がマクロ定義 によって許可されている場合は、それらの引き数は、その間にコンマを挟んでマージされ、それら後続引き数があたかも 1 つの 引き数であるかのように、__VA_ARGS__ で置き換えられます。引き数自体に含まれるマクロの起動はすべて、 引き数が置換コード内の対応するパラメーターと置き換わる前に、完全に置き換えられます。

マクロ引き数は空 (ゼロ個のプリプロセス・トークンで構成) であってもかまいません。例えば、次のような場合です。

#define SUM(a,b,c) a + b + c
SUM(1,,3)  /* No error message.
              1 is substituted for a, 3 is substituted for c. */
この言語フィーチャーは、C++ の直交拡張です。

ID リストが省略符号で終了していない場合は、マクロ起動における引き数の数は、 対応するマクロ定義内のパラメーターの数と同じでなければなりません。指定された 引き数がすべて置換された後に残る引き数はすべて (分離するコンマを含む)、パラメーターの置換時に可変引き数と呼ばれる 1 つの引き数に結合されます。可変引き数は、パラメーター・リスト内に現れる すべての ID __VA_ARGS__ と置き換わります。 次の例は、このことを示したものです。

#define debug(...)   fprintf(stderr, __VA_ARGS__)
 
debug("flag");     /*   Becomes fprintf(stderr, "flag");   */

マクロ起動引き数リストにおけるコンマは、以下の場合には、分離文字として作用しません。

以下の行は、ab という 2 つのパラメーター と置換トークン (a + b) を持つものとして マクロ SUM を定義します。

#define SUM(a,b) (a + b)

この定義により、プリプロセッサーは以下のステートメントを変更すること になります (そのステートメントが前の定義の後に現れる場合)。

c = SUM(x,y);
c = d * SUM(x,y);

プリプロセッサーの出力においては、これらのステートメントは次のように表示されます。

c = (x + y);
c = d * (x + y);

置換テキストが正しく評価されるようにするためには、小括弧を使用してください。例えば、

#define SQR(c)  ((c) * (c))

上記の定義では、式を正しく評価するため、定義内の各パラメーター c のまわりに小括弧を必要とします。

y = SQR(a + b);

プリプロセッサーはこのステートメントを次のように展開します。

y = ((a + b) * (a + b));

定義内に小括弧がないと、プリプロセッサーは評価の正しい順序を保てず、プリプロセッサーの出力は 次のようになります。

y = (a + b * a + b);

# および ## 演算子の引き数は、 関数類似マクロのパラメーターの置換の前に 変換されます。

プリプロセッサー ID は、いったん定義されると定義されたままとなり、 言語のスコープ決定規則とは関係なく、有効となります。 マクロ定義のスコープは定義から始まり、 対応する #undef ディレクティブに遭遇するまで終了しません。 対応する #undef ディレクティブがない場合、そのマクロ定義の スコープは、変換単位の終わりまで続きます。

再帰マクロは、完全には展開されません。 例えば、以下の定義

   #define x(a,b) x(a+1,b+1) + 4

は、

   x(20,10)

を、以下のように展開します。

   x(20+1,10+1) + 4

マクロ x を、それ自体の中で繰り返し展開しようとするよりも、 上述の展開を行います。 マクロ x が展開された後で、そのマクロは、関数 x() の呼び出しとなります。

置換トークンを指定するのに、定義は必須ではありません。 以下の定義は、現在のファイル内のこれ以降の行から、 トークン debug のすべてのインスタンスを除去します。

#define debug

2 番目のプリプロセッサー #define ディレクティブを用いて、 定義済みの ID またはマクロの定義を変更することができます。 ただし、2 番目のプリプロセッサー #define ディレクティブの前に、 プリプロセッサー #undef ディレクティブがある場合に限ります。 #undef ディレクティブは、最初の定義を無効にして、 同じ ID を再定義で使用できるようにします。

プログラムのテキストの中については、プリプロセッサーはマクロ起動のための 文字定数またはストリング定数のスキャンを行いません。

#define ディレクティブの例

以下のプログラムには、2 つのマクロ定義と、その定義されている両方のマクロを 参照するマクロ起動が含まれています。

/**
 ** This example illustrates #define directives.
 **/
 
#include <stdio.h>
 
#define SQR(s)  ((s) * (s))
#define PRNT(a,b) ¥
  printf("value 1 = %d¥n", a); ¥
  printf("value 2 = %d¥n", b) ;
 
int main(void)
{
  int x = 2;
  int y = 3;
 
     PRNT(SQR(x),y);
 
   return(0);
}

プリプロセッサーによって解釈された後、このプログラムは、 以下のものに等価のコードによって置き換えられます。

#include <stdio.h>
 
int main(void)
{
  int x = 2;
  int y = 3;
 
     printf("value 1 = %d¥n", ( (x) * (x) ) );
     printf("value 2 = %d¥n", y);
 
   return(0);
}

このプログラムの出力は次のようになります。

value 1 = 4
value 2 = 3

関連参照

IBM Copyright 2003