C / C ++マクロのカンマ


103

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

#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

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


リテラルにする意味を持つ文字をエスケープする必要があると思います。
Jite

少なくともC ++では、typedefをどこにでも置くことができるので、「事前」でなければならないという理由がわかりません。
Vaughn Cato

回答:


108

アングルブラケットも表す(又はで起こる)ことができるので、比較演算子は<><=および>=それが括弧内にないように、マクロ展開は、角括弧内のカンマを無視することはできません。(大括弧と中括弧は、通常は対になったペアとして発生しますが、これも問題です。)マクロ引数を括弧で囲むことができます。

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

これは素晴らしいです。しかし、これをどのようにして知りましたか?私はたくさんのトリックを試してきましたが、関数の種類で問題が解決するとは思いもしませんでした。
2014

@WilliamCustodeを思い出すと、私は関数型と関数宣言の文法を最も厄介な解析問題に関して参照して研究していたので、そのコンテキストの型に冗長な括弧が適用される可能性があることを知っていたのは偶然でした。
ecatmur

テンプレートを操作しているときに、この方法に問題が見つかりました。私が欲しかったコードがこれだったとしましょう:template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}このソリューションをここに適用すると、マクロの背後にある構造体が依存型になり、typenameプレフィックスが型に必要になります。追加することはできますが、型の推論が壊れているため、関数を呼び出すには、型の引数を手動でリストする必要があります。カンマのマクロを定義するテンプルの方法を使用してしまいました。きれいには見えないかもしれませんが、完璧に機能しました。
Roger Sanders、

答えに関する小さな問題:カンマは内部[] では無視されると記載されていますが、無視され{}ず、()悲しいことにしか機能しません。参照:ただし、角括弧や中括弧でバランスを取る必要はありません...
VinGarcia

残念ながら、これはMSVCでは機能しません:godbolt.org/z/WPjYW8。MSVCは複数の括弧を追加することを許可しておらず、解析に失敗しているようです。エレガントではありませんが、高速です(テンプレートのインスタンス化が少ない)解決策は、コンマで囲まれた引数をラッパーマクロにラップすることです#define PROTECT(...) argument_type<void(__VA_ARGS__)>::type。引数を渡すことは、複数のマクロを介しても簡単に可能になり、単純なタイプの場合は、PROTECTを省略できます。ただし、このように評価すると、関数型は関数ポインタになります
Flamefire

119

括弧を使用できず、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"


16
#define COMMAすごい、あなたは私に何時間もの仕事を節約してくれた...何年も前に私が考えなかったのはなぜですか。このアイデアを共有してくれてありがとう。これにより、引数の数が異なる関数を完全にセットアップするマクロを構築することもできます。
moliad 2013

28
ホラーのプラス1
namezero

1
@kiwあなた#define STRVX(...) STRV(__VA_ARGS__)との場合#define STRV(...) # __VA_ARGS__std::cout << STRV(type<A COMMA B>) << std::endl;印刷されtype<A COMMA B>std::cout << STRVX(type<A COMMA B>) << std::endl;印刷されますtype<A , B>。(STRV「可変文字列化」用でありSTRVX、「拡張可変長文字列化」用です。)
not-a-user

1
@ not-a-userはい、ただし可変個のCOMMAマクロでは、最初からマクロは必要ありません。それが私が結局終わったものです。
kiw 2015年

私はそれを決して使用しませんが、陽気であることを+1します。
Rafael Baptista、2016

58

プリプロセッサが可変個のマクロをサポートしている場合:

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

ああ、まあ…なんで?なぜかっこで囲まないのですか?

15
@VladLazarenko:括弧内に常に任意のコードを入れることができるわけではないため。特に、宣言子で型名を括弧で囲むことはできません。これがまさにこの引数になります。
マイクシーモア

2
...また、マクロ定義のみを変更でき、それを呼び出すすべての場所を変更できない場合があるためです(制御できない場合や、数千のファイルに分散している場合など)。これは、たとえば、マクロを追加して、同じ名前の関数から職務を引き継ぐときに発生します。
BeeOnRope 2017

32

FOOとして定義する

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

次に、常にタイプ引数を括弧で囲んで呼び出します。例えば、

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

もちろん、マクロ定義のコメントで呼び出しを例示するのは良い考えです。


なぜこれがこれほどまでにダウンしているのかわからない、それはマイク・シーモアよりはるかに優れたソリューションです。それは迅速かつシンプルで、ユーザーから完全に隠されています。
iFreilicht 2016

3
@iFreilicht:それは1年少し後投稿されました。;-)
乾杯とhth。-アルフ

5
そして、それがどのようにそしてなぜ機能するかを理解するのも難しいので
VinGarcia

@VinGarcia、なぜ/どのように機能するか説明できますか?呼び出すときに括弧が必要なのはなぜですか?UNPACKこのように使用するとどうなります) UNPACK type nameか?type使用するとタイプが正しく取得されるのはなぜ) UNPACK type nameですか?ここで何が起こっているのですか?
ユーザー

@userなし、たぶんCheersとhthがあなたに答えることができる
VinGarcia

4

これを行うには、少なくとも2つの方法があります。最初に、複数の引数を取るマクロを定義できます。

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

その場合、より多くの引数を処理するために、より多くのマクロを定義することになる場合があります。

次に、引数を括弧で囲むことができます。

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

その場合、余分な括弧が結果の構文を台無しにすることがあります。


最初のソリューションでは、マクロはオーバーロードしないため、各マクロには異なる名前を付ける必要があります。2つ目は、型名を渡す場合、変数(またはtypedef)の宣言に使用される可能性が非常に高いため、かっこで問題が発生します。
James Kanze

4

これはP99で可能です。

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

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


3

簡単な答えは、できないということです。これは、<...>テンプレート引数の選択の副作用です。マクロのメカニズムは、それは括弧を扱うようにそれらを扱うように拡張することができませんでしたので、また、アンバランスな状況に表示されます。(委員会のメンバーの中には、別のトークンについて主張した人もいますが、を使用して問題の大部分を納得させることができませんでした。)<>(^...^)<...>


2
(^...^)これは1つの幸せそうな顔です:)
CygnusX1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.