2012-12-12 10 views
72

は、我々はカンマ/ C++のマクロ

FOO(int, int_var); 

のように使用できますが、常にではないとして、単にそのようでした。この

#define FOO(type,name) type name 

ようなマクロがあるとしましょう:

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 

非常に人間工学的ではありません。プラスタイプの非互換性を扱う必要があります。どのようにマクロでこれを解決するか?

+0

文字をエスケープしてリテラルにする意味があると思います。 – Jite

+0

少なくともC++では、どこにでもtypedefを置くことができるので、なぜそれが "あらかじめ"あるのかは分かりません。 –

答えて

78

アングルブラケットは、 <,>,<=および>=のマクロ展開では、カッコ内のように山カッコ内のコンマを無視できません。 (これは、これらは通常、バランスの取れたペアを発生するにもかかわらず、また、角括弧と括弧のための問題です。)あなたは、括弧内のマクロ引数囲むことができます。

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); 

関数型を形成することは、余分な括弧を無視するため、このマクロを使用することができますまたはタイプ名がカンマ含まない括弧なし:タイプ名がかっこ外カンマを含めることができないので、Cで

FOO((int), int_var); 
FOO(int, int_var2); 

が、もちろん、これは必須ではありません。だから、あなたが書くことができ、クロスランゲージマクロ用:

#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 
+0

これは素晴らしいです。しかし、どうやってこれについて知りましたか?私はたくさんのトリックを試してきましたが、関数型が問題を解決するとは考えていませんでした。 –

+0

@WilliamCustode私が思い出したように、私は関数型と関数宣言の文法を最も厄介な解析問題を参照して勉強していたので、冗長な括弧がその文脈の型に適用できることを知っていたのは偶然でした。 – ecatmur

+0

テンプレートを使って作業しているときにこの方法に問題が見つかりました。 'template void SomeFunc(FOO(std :: map )要素){}' ここでこのソリューションを適用すると、構造体マクロの後ろは依存型になり、型名の接頭辞が型に必要になります。それを追加することはできますが、タイプ控除が壊れているので、関数を呼び出すためにタイプ引数を手動でリストアップする必要があります。私はカンマのマクロを定義するテンプルの方法を使用して終了しました。それはかなり見えないかもしれませんが、それは完全に機能しました。 –

38

はあなたのプリプロセッサは、可変引数マクロをサポートしている場合:

#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); 
+0

ああ、まあ...なぜ?なぜかっこで囲むだけではないのですか? –

+11

@VladLazarenko:なぜなら、あなたはいつも括弧内に任意のコードを置くことができないからです。特に、宣言子の型名のまわりに括弧を入れることはできません。これはまさにこの引数がなるものです。 –

+1

...また、マクロ_definition_を変更するだけで、それを呼び出す場所(あなたのコントロール下にないかもしれない、または1000sのファイルなどに広がっているかもしれない)を変更することができないかもしれないからです。これは、たとえば、同じ名前の関数から任務を引き継ぐマクロを追加する場合に発生します。 – BeeOnRope

2

これを実行するには、少なくとも2つの方法があります。あなたが行う場合は、あなたがより多くの引数を処理するために、より多くのマクロを定義してしまうことがありますことを

#define FOO2(type1, type2, name) type1, type2, name 

:まず、あなたは複数の引数を取るマクロを定義することができます。

第二に、あなたは、引数の前後に括弧を挿入することができます

#define FOO(type, name) type name 
F00((std::map<int, int>) map_var; 

あなたは余分な括弧は、結果の構文を台無しにすることを見つけることを行った場合。

+0

最初の解決策では、マクロが過負荷にならないため、各マクロの名前が異なる必要があります。第2に、型名を渡す場合、変数(または型定義)を宣言するために使用される可能性が非常に高いので、かっこで問題が発生します。 –

1

簡単な答えはできないということです。これは、テンプレート引数として<...>の選択の副作用です。 <>もアンバランスなコンテキストで表示されるため、かっこを処理するようにマクロメカニズムを拡張することができませんでした。 (委員会メンバーの中には、別のトークン、例えば(^...^)と主張していた者もいたが、多くの問題を<...>で確信させることはできなかった)

2

これはP99で可能です:

#include "p99/p99.h" 
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__) 
FOO() 

上記のコードは、効果的に引数リストの最後にカンマを取り除きます。 clang -Eで確認してください(P99にはC99コンパイラが必要です)。

81

あなたは括弧を使用することはできませんし、マイクの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"を印刷します。

+9

#define COMMAうわー、ちょうど私の仕事の時間を節約しました...なぜ私はこの数年前に考えなかったのですか?このアイデアを共有してくれてありがとうこれにより、異なる引数カウントで機能をセットアップするマクロを構築することさえできます。 – moliad

+14

ホラー用プラス1 – namezero

+0

引数を文字列にしたい場合は... – kiw

12

ただ、例えば、型引数の周りにカッコで常にそれを呼び出すその後FOO

として
#define UNPACK(...) __VA_ARGS__ 

#define FOO(type, name) UNPACK type name 

を定義します

FOO((std::map<int, int>), map_var); 

もちろん、マクロ定義に関するコメントで呼び出しを例示することをお勧めします。

+0

なぜこれが遠いのか分かりませんが、Mike Seymoursよりもはるかに良いソリューションです。すばやくシンプルで、ユーザーから完全に隠されています。 – iFreilicht

+1

@iFreilicht:1年後に少し投稿されました。 ;-) –

+1

また、どのように、なぜそれが動作するのかを理解することも難しいので – VinGarcia