171

次のようなマクロを使用して、一部の関数の名前が特定のマクロ変数の値に依存するプログラムを作成しようとしています。

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

残念ながら、マクロNAME()はそれを

int some_function_VARIABLE(int a);

それよりも

int some_function_3(int a);

したがって、これは明らかに間違った方法です。幸いなことに、VARIABLE の可能な値の数は少ないので、単純に#if VARIABLE == nすべてのケースを個別にリストすることができますが、それを行う賢い方法はありますか?

4

3 に答える 3

244

標準Cプリプロセッサ

$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"





extern void mine_3(char *x);
$

2つのレベルの間接参照

別の回答へのコメントで、Cade Roux 、なぜこれに2つのレベルの間接参照が必要なのかを尋ねました。ばかげた答えは、それが標準が機能することを要求する方法だからです。文字列化演算子でも同等のトリックが必要であることに気付く傾向があります。

C99標準のセクション6.10.3は「マクロ置換」をカバーし、6.10.3.1は「引数置換」をカバーします。

関数のようなマクロを呼び出すための引数が識別された後、引数の置換が行われます。置換リストのパラメーターは、前に#または##前処理トークンが続くか、前処理トークンが後に続く場合を除き##(以下を参照)、そこに含まれるすべてのマクロが展開された後、対応する引数に置き換えられます。置換される前に、各引数の前処理トークンは、前処理ファイルの残りの部分を形成しているかのように完全にマクロ置換されます。他の前処理トークンは利用できません。

呼び出しNAME(mine)では、引数は'mine'です。'mine'に完全に拡張されています。次に、置換文字列に置き換えられます。

EVALUATOR(mine, VARIABLE)

これで、マクロEVALUATORが検出され、引数は「mine」および「VARIABLE」として分離されます。その後、後者は完全に「3」に展開され、置換文字列に置き換えられます。

PASTER(mine, 3)

この操作は、他のルール(6.10.3.3'##演算子')でカバーされています。

関数のようなマクロの置換リストで、パラメーターの直前または直後に##前処理トークンがある場合、パラメーターは対応する引数の前処理トークンシーケンスに置き換えられます。[...]

オブジェクトのようなマクロ呼び出しと関数のようなマクロ呼び出しの両方で、置換リストを再検査して置換するマクロ名を増やす前に、置換リスト内の前処理トークンの各インスタンス##(引数からではない)が削除され、前の前処理トークンが連結されます次の前処理トークンを使用します。

したがって、置換リストには、の後に;がx続きます。だから私たちは持っています:####y

mine ## _ ## 3

トークンを削除し、##いずれかの側でトークンを連結すると、「mine」と「_」および「3」が組み合わされて、次のようになります。

mine_3

これは望ましい結果です。


元の質問を見ると、コードは('some_function'の代わりに'mine'を使用するように適合されています):

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

NAMEへの議論は明らかに「私のもの」であり、それは完全に拡張されています。
6.10.3.3の規則に従って、次のことがわかります。

mine ## _ ## VARIABLE

これは、##演算子が削除されると、次のようにマップされます。

mine_VARIABLE

質問で報告されたとおりです。


従来のCプリプロセッサ

ロバート・リューガー は尋ねます

トークン貼り付け演算子を持たない従来のCプリプロセッサでこれを行う方法はあります##か?

たぶん、そうでないかもしれません—それはプリプロセッサに依存します。標準プリプロセッサの利点の1つは、この機能が確実に機能することですが、標準プリプロセッサにはさまざまな実装がありました。1つの要件は、プリプロセッサがコメントを置き換えるときに、ANSIプリプロセッサが行う必要があるようにスペースを生成しないことです。GCC(6.3.0)Cプリプロセッサはこの要件を満たしています。XCode8.2.1のClangプリプロセッサはそうではありません。

それが機能するとき、これは仕事をします(x-paste.c):

#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

fun,との間にスペースがないことに注意してください。これVARIABLEは重要です。存在する場合は出力にコピーさmine_ 3れ、名前として最終的になりますが、もちろん構文的には有効ではありません。(さて、髪を戻してもらえますか?)

GCC 6.3.0(実行cpp -traditional x-paste.c中)では、次のようになります。

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_3(char *x);

XCode 8.2.1のClangを使用すると、次のようになります。

# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2





extern void mine _ 3(char *x);

それらのスペースはすべてを台無しにします。両方のプリプロセッサが正しいことに注意してください。さまざまなプリスタンダードプリプロセッサが両方の動作を示したため、コードを移植しようとすると、トークンの貼り付けが非常に煩わしく、信頼性の低いプロセスになりました。表記のある標準はそれを##根本的に単純化します。

これを行う他の方法があるかもしれません。ただし、これは機能しません。

#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

GCCは以下を生成します:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_VARIABLE(char *x);

閉じますが、サイコロはありません。もちろん、YMMVは、使用しているプリ標準のプリプロセッサによって異なります。率直に言って、連携していないプリプロセッサで立ち往生している場合は、標準のプリプロセッサの代わりに標準のCプリプロセッサを使用するように調整する方がおそらく簡単です(通常、コンパイラを適切に構成する方法があります)。仕事をする方法を考え出すのに多くの時間を費やします。

于 2009-09-29T00:23:14.140 に答える
34

使用する:

#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)

int NAME(some_function)(int a);

正直なところ、これが機能する理由を知りたくありません。なぜそれが機能するのかを知っていれば、あなたはこの種のことを知っている職場のになり、誰もがあなたに質問をするでしょう。=)

于 2009-09-29T00:17:25.853 に答える