可変引数マクロは、一部のコンピュータプログラミング言語、特にCプリプロセッサの機能であり、マクロが可変個の引数を受け入れるように宣言することができます。
可変引数マクロは、1999 年にC言語標準のISO/IEC 9899:1999 ( C99 ) 改訂版で導入され、2011 年にC++言語標準のISO/IEC 14882:2011 ( C++11 ) 改訂版で導入されました。[ 1 ]引数のない可変引数マクロのサポートは、C++20およびC23で追加されました。[ 2 ] [ 3 ]
宣言構文は可変引数関数と同様で、3つのピリオド「 」を連続して...使用することで、1つ以上の引数を渡す必要があることを示します。マクロ展開時には、マクロ置換リスト内の特殊識別子の各出現箇所が、__VA_ARGS__渡された引数に置き換えられます。
...さらに、通常のマクロ引数は, [ 4 ]の前に記述できますが、通常の引数は の後に記述できません...。
可変引数リスト内の個々の引数にアクセスしたり、渡された引数の数を調べたりする手段は提供されていません。ただし、渡された引数の数を数えるマクロを作成することは可能です。[ 5 ]
C99およびC++11規格では少なくとも 1 つの引数が必要ですが、C++20およびC23以降では、関数マクロによってこの制限が解除されています __VA_OPT__。 __VA_OPT__マクロは引数が存在する場合はその引数に置き換えられ、そうでない場合は省略されます。ただし、一般的なコンパイラでは、この追加前に引数を 0 個渡すことも許可されています。[ 4 ] [ 6 ]
C プリプロセッサの規則では、引数内のマクロ名が__VA_OPT__再帰的に展開されるのを防いでいます。ただし、任意の固定数の再帰的展開までは、この制限を回避することが可能です。[ 7 ]
C および C++ コードをコンパイルする際に可変引数マクロをサポートするコンパイラはいくつかあります。GNU Compiler Collection 3.0 [ 4 ] Clang (全バージョン) [ 8 ] Visual Studio 2005 [ 6 ] C ++ Builder 2006 、およびOracle Solaris Studio (旧Sun Studio ) Forte Developer 6 update 2 (C++ バージョン 5.3) [ 9 ]などです。GCC もObjective-C をコンパイルする際にこのようなマクロをサポートしています。
引数ゼロをサポートするマクロのサポートは、GNU Compiler Collection 8 [ 10 ] 、 Clang 6 [ 11 ]、およびVisual Studio 2019 [ 12 ]__VA_OPT__に追加されました。
printf呼び出し元のファイル名と行番号を引数として受け取るような関数 が必要な場合はdbgprintf()、以下の解決策が適用されます。
実装した機能:
void debugPrint ( const char * file , int line , const char * fmt , ...);C++11の可変引数マクロのサポートには制限があるため、以下の単純な解決策は失敗する可能性があり、避けるべきです。
#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat, __VA_ARGS__)理由は
dbgprintf ( "こんにちは" )展開すると
debugPrint ( __FILE__ , __LINE__ , "Hallo" , )閉じ括弧の前のカンマは構文エラーになります。
GNU C++ は、この問題を解決する非移植拡張機能をサポートしています。
#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat, ##__VA_ARGS__)C++20 では、以下の構文がサポートされています。
#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)cformat可変引数の一部として文字列を使用することで、上述の非互換性を回避できます。これは少々複雑な方法ですが、移植性は高いです。
#define dbgprintf(...) debugPrint(__FILE__, __LINE__, __VA_ARGS__)dbgprintf()次のように呼ばれる可能性がある
dbgprintf ( "Hello, world" );展開すると
debugPrint ( __FILE__ , __LINE__ , "Hello, world" );別の例としては、次のものがあります。
dbgprintf ( "%d + %d = %d" , 2 , 2 , 5 );展開すると
debugPrint ( __FILE__ , __LINE__ , "%d + %d = %d" , 2 , 2 , 5 );可変引数マクロがない場合、ラッパーをprintf直接記述することはできません。標準的な回避策は、 C/C++ のstdargs機能を使用し、関数呼び出しを行うことですvprintf。
C99の可変引数マクロで引数が空の場合、末尾にカンマを生成すると移植性の問題が生じます。一部のコンパイラ (例えば、新しい標準準拠のプリプロセッサ[ 6 ]を使用しない場合の Visual Studio ) は末尾のカンマを黙って削除します。他のコンパイラ (例えば、GCC [ 4 ]## ) は、カンマの前にカンマを付ける機能をサポートしています__VA_ARGS__。
#define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)以下のアプリケーションが動作します
MYLOG ( "風船が多すぎます %u" , 42 );展開すると
fprintf ( stderr , "%s(%u): " "風船が多すぎます %u" " \n " , __FILE__ , __LINE__ , 42 );これは以下と同等です
fprintf ( stderr , "%s(%u): 風船が多すぎます %u \n " , __FILE__ , __LINE__ , 42 );しかし、このアプリケーションを見てください。
MYLOG ("注意!" )展開すると
fprintf ( stderr , "%s(%u): " "注意!" " \n " , __FILE__ , __LINE__ , );これはGCCで構文エラーを発生させます。
GCC は以下の(移植性のない)拡張機能をサポートしています。
#define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)__VA_ARGS__が空の場合に末尾のカンマを削除します。
C99で可変引数が導入される以前は、関数に渡せる引数の数を可変にするために、二重括弧を使うのが一般的でしたprintf()。
#define dbgprintf(x) debugPrint xdbgprintf()次のように呼ばれることもあります。
dbgprintf (( "Hello, world %d" , 27 ));これは以下のように展開されます。
debugPrint ( "Hello, world %d" , 27 );Rustでは、可変引数インターフェース (可変引数マクロとも呼ばれる) [ 13 ]により、任意の数の引数を取ることができます。Rust には可変引数関数や可変引数テンプレートがないため、可変引数パラメータを実現する唯一の方法は、 を介して繰り返しパターンを使用する上記の可変引数インターフェースを使用することですmacro_rules!。たとえば、 では、は繰り返しパターンを表し、は区切り文字を表し、は 0 回以上の繰り返し (クリーネ星 )を表し、 は1 回以上の繰り返し (クリーネプラス) を表します。$(...),*$(),*+
可変引数マクロを実装する例の1つとして、以下が挙げられますvec!。これは以下と同等です。
// "make_vec!" という名前のマクロを定義しますmacro_rules! make_vec { // 任意の Rust 式に一致し、 // パターンをカンマで区切って 0 回以上繰り返します( $( $x : expr ), * ) => { { //ユーザー定義の Vec に解決されないように、 // Vec は完全修飾されている必要がありますlet mut temp = :: std :: vec :: Vec :: new (); // 引数ごとに temp へのプッシュを 1 回繰り返します$( temp . push ( $x ); ) * // マクロtempから temp を返します} }; }