4

Cの #define ステートメント、またはより具体的には gcc 4.2 のオプションの引数に問題があります。

bool func1(bool tmp) { return false; }
void func2(bool tmp, bool tmp2) {}
#define CALL(func, tmp, ...) func(tmp, ##__VA_ARGS__)

int main() {
   // this compiles
   CALL(func2, CALL(func1, false), false);

   // this fails with: Implicit declaration of function 'CALL'
   CALL(func2, false, CALL(func1, false));
}

これは明らかに不自然な例ですが、問題を示しています。オプションの引数を正しく「解決」する方法を知っている人はいますか?


追加情報: ##beforeを削除して、次の__VA_ARGS__ようにすると:

bool func2(bool tmp, bool tmp2) { return false; }
#define CALL(func, tmp, ...) func(tmp, __VA_ARGS__)

int main() {
   CALL(func2, false, CALL(func2, false, false));
}

それはコンパイルされますが、解決されるため、引数がゼロでは機能しなくなりましたfunc(tmp, )

編集:すべてのコードを変換して、以前のコードの代わりに P99 に依存するようにした直後 (これにより、コードが大幅に壊れてしまいました)、誤ってこれが機能することを発見しました:

bool func1(bool tmp) { return false; }
void func2(bool tmp, bool tmp2) {}
#define CALL2(func, tmp, p...) func(tmp, ##p)
#define CALL(func, tmp...) CALL2(func, tmp)

int main() {
   // works
   CALL(func2, CALL(func1, false), false);

   // ...also works
   CALL(func2, false, CALL(func1, false));
}

任意の数のパラメーターをコンパイルして動作します (そして正しい値が渡されて返されます)、しかし...これは合法であると考えられますか?

4

3 に答える 3

14

演算子は正確なトークン置換を行うため、その##場合、 C関数"CALL(func1, false)"の最後の引数としてトークンを送信しようとしています。func1

これに関する問題は、これがCALLマクロであり、リスト内で可変長マクロ呼び出しをネストできないことです。##__VA_ARGS__

内部マクロが名前付き引数として渡されたときに機能する理由は、プリプロセッサが内部マクロの名前付き引数を解析しますが、##__VA_ARGS__単純なトークン置換があるリストでは解析しないためです。

これを解決する 1 つの方法は、インナーの結果をCALLプレースホルダー変数に代入し、それをマクロに渡すことです。

int main() {
   CALL(func2, CALL(func1, false), false);

   bool result = CALL(func1, false);
   CALL(func2, false, result);
}

これを解決する別の方法は、関数__VA_ARGS__への唯一の引数として使用funcすることです。これにより、ネストされたマクロ呼び出しを次のように渡すことができます。

#define CALL(func, ...) func(__VA_ARGS__)

int main() {
   CALL(func2, false, CALL(func2, false, false));
}

あなたのジレンマをもう少し詳しく分析してみましょう。

CALL(func2, false, CALL(func1, false))

この特定のマクロ呼び出しでCALLは、 が呼び出され、 が渡され、さらに("func2", "tmp", CALL(func1, false)) が呼び出されます。func1tmpCALL(func1, false)

これは、プリプロセッサと実際の C コンパイラの間に線が引かれる場所です。

プリプロセッサは、置換を開始すると解析が完了するため、コンパイラはCALL(func1, false)マクロではなく実際の C 関数として受け取ります。コンパイラはマクロについて認識せずプリプロセッサだけが認識しているためです。

于 2011-01-01T12:07:42.397 に答える
4

, ##コンストラクトで gcc 拡張機能を使用しています。移植性を念頭に置いている場合、それを使用することはおそらく良い考えではありません。

少し努力すれば、受け取った引数の数に反応して正しい置換を行うマクロを作成できます。P99はそのためのヘルパーを提供します。

#define CALL(...) P99_IF_EQ_2(P99_NARG(__VA_ARGS__))(dosomethingwithtwo(__VA_ARGS__))(dosomethingwithmore(__VA_ARGS__))

しかし、あなたの場合、簡単な解決策があると思います:

#define CALL(func, ...) func(__VA_ARGS__)
于 2011-01-01T13:43:53.570 に答える
0

そのため、元の投稿の最後に記載されているデザインを使用するようにコードを変換したところ、突然再び機能し始めました。P99ラッパーにタイプミスがあったと仮定します。

これで、任意の数の引数と任意の量のネストに対して、任意のパラメーターとして正しくコンパイルおよび機能するようになりました。みんな、ありがとう!

于 2011-01-01T17:57:26.797 に答える