標準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)
では、引数は「私のもの」です。それは完全に「私のもの」に拡張されます。次に、置換文字列に置き換えられます。
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プリプロセッサはこの要件を満たしています。XCode 8.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の代わりに標準のCプリプロセッサを使用する方が簡単です(通常、コンパイラを適切に構成する方法があります)。仕事をする方法を考え出すために多くの時間を費やします。