17

Microsoft Visual Studioコンパイラとgccは、次の小さなスニペットを異なる方法で前処理することを発見しました。

# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)

'gcc -E'は、次のようになります。

a + {a + b}

、一方、'cl / E'は、マクロ引数の欠落に関する警告を発行し、次の出力を生成します。

a + {a, b} +

ネストされたマクロ展開からのコンマは、引数の区切り文字とは見なされないようです。残念ながら、clプリプロセッサに実装されているアルゴリズムの説明が見つからなかったため、私の提案が正しいかどうかはわかりません。clプリプロセッサがどのように機能するか、そしてそのアルゴリズムとgccのアルゴリズムの違いを知っている人はいますか?そして、観察された行動をどのように説明することができますか?

4

3 に答える 3

9
# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)

これを手動で段階的に展開してみましょう。

M(a, b)
--> M2(a, P(a, b))
--> M2(a, {a, b})

標準は言う:

リスト内の個々の引数はコンマ前処理トークンで区切られますが、一致する内括弧の間のコンマ前処理トークンは分離されません

括弧だけが言及されているので...

--> M3(a, {a, b})
--> a + {a + b}

重要:

M3(a, {a, b})

ここで、標準からの以前の引用によれば、3つの「引数」がM3に渡されます(トークン/引数を説明するために一重引用符を使用):

M3('a', '{a', 'b}')

に拡張されます

'a' + '{a' + 'b}'

そしてこれはcpp(4.6.1)が逐語的に与えるものです:

# 1 "cpp.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "cpp.cpp"




a + {a + b}

cpp(または)は正しいですが、MSVCは正しくありませんgccg++

貴族として、バグレポートが存在することを確認してください。

于 2012-07-13T11:52:46.220 に答える
4

このような動作を説明する唯一のロジックは次のようになります。

CL の方法:

 M(a,b) 
 M2(a,P(a,b)) 
 M3(a,P(a,b))
 M3(a,{a,b}) -> M3 gets 2 arguments ( 'a' and '{a,b}') instead of 3.
    |  \ /
  arg1  |
      arg2 

Gcc の方法:

M(a,b) 
M2(a,P(a,b)) 
M3(a,P(a,b))
M3(a,{a,b}) -> Gcc probably thinks there are 3 arguments here ('a', '{a', 'b}').
   |  | |
 arg1 | |
   arg2 |
     arg3
于 2012-07-13T11:41:19.310 に答える
1

gccはそれを正しく理解していると思いますが、Microsoftが行っていることは正しくありません。

行のマクロ置換が行われる場合

M2(a, P(a, b))

標準(セクション6.10.3.1)では、マクロの置換リスト( "M3(x、y)")の2番目のパラメーター( "y")をその引数( "P(a、b)")で置き換える前に、マクロが必要です。その引数の置換が実行されます。これは、「P(a、b)」が挿入される前に「{a、b}」に処理されることを意味します。

M3(a, {a, b})

その後、さらに置き換えられます

a + {a + b}
于 2012-07-13T11:52:04.467 に答える