このようなマクロがあるとします
#define FOO(type,name) type name
のように使用できます
FOO(int, int_var);
しかし、必ずしもそれほど単純ではありません:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
もちろんできます:
typedef std::map<int, int> map_int_int_t;
FOO(map_int_int_t, map_var); // OK
これは人間工学的ではありません。さらに、型の非互換性に対処する必要があります。マクロでこれを解決する方法はありますか?
山括弧は、比較演算子<
、>
、<=
および>=
、マクロ展開では、括弧内のように山括弧内のコンマを無視できません。 (これは、通常はバランスの取れたペアとして発生しますが、角括弧と中括弧でも問題になります。)マクロ引数を括弧で囲むことができます。
FOO((std::map<int, int>), map_var);
問題は、パラメーターがマクロ展開内でかっこで囲まれたままになり、ほとんどのコンテキストで型として読み取れないことです。
これを回避する良い方法は、C++では、関数型を使用して括弧で囲まれた型名から型名を抽出できることです。
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);
関数型を作成すると余分な括弧は無視されるため、型名にカンマが含まれていない場合は、このマクロを括弧の有無にかかわらず使用できます
FOO((int), int_var);
FOO(int, int_var2);
もちろん、Cでは、型名に括弧の外側にコンマを含めることができないため、これは必要ありません。したがって、言語間のマクロの場合、次のように記述できます。
#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
括弧を使用できず、MikeのSINGLE_ARGソリューションが気に入らない場合は、COMMAを定義するだけです。
#define COMMA ,
FOO(std::map<int COMMA int>, map_var);
これは、次のようにマクロ引数の一部を文字列化する場合にも役立ちます。
#include <cstdio>
#include <map>
#include <typeinfo>
#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
" has typeid name \"%s\"", typeid(type).name())
int main()
{
FOO(std::map<int COMMA int>, std::printf);
}
std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE"
。
プリプロセッサが可変長マクロをサポートしている場合:
#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name
FOO(SINGLE_ARG(std::map<int, int>), map_var);
それ以外の場合は、もう少し面倒です。
#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need
FOO(SINGLE_ARG2(std::map<int, int>), map_var);
FOO
を次のように定義するだけです
#define UNPACK( ... ) __VA_ARGS__
#define FOO( type, name ) UNPACK type name
次に、型引数を括弧で常に呼び出します。
FOO( (std::map<int, int>), map_var );
もちろん、マクロ定義のコメントで呼び出しを例示することは良い考えです。
これは P99 で可能です:
#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()
上記のコードは、引数リストの最後のコンマのみを効果的に削除します。確認する clang -E
(P99にはC99コンパイラが必要です)。
これを行うには、少なくとも2つの方法があります。まず、複数の引数を取るマクロを定義できます。
#define FOO2(type1, type2, name) type1, type2, name
そうすると、より多くの引数を処理するためにより多くのマクロを定義することになります。
次に、引数を括弧で囲むことができます。
#define FOO(type, name) type name
F00((std::map<int, int>) map_var;
これを行うと、余分な括弧が結果の構文を台無しにすることがあります。
簡単な答えは、できないということです。これは、テンプレート引数に<...>
を選択した場合の副作用です。 <
と>
も不均衡なコンテキストで表示されるため、括弧を処理するようにマクロメカニズムを拡張して処理することはできません。 (委員会のメンバーの中には、(^...^)
と言う別のトークンを主張した人もいましたが、<...>
を使用して問題の大部分を納得させることはできませんでした。)