2012-06-19 15 views
6

アサーションを実行するためにREQUIREマクロに依存するユニットテストフレームワークを使用しています。簡易不要なCプリプロセッサマクロ展開

は、このようなマクロ作品:これと同じ定義さ

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, "REQUIRE") 

#define INTERNAL_REQUIRE(expr, macroName) \ 
PerformAssertion(macroName, #expr, expr); 

PerformAssertionの最初の2つのパラメータは、タイプのものである:const char*。 2番目のパラメータ(#expr)の理由は、アサートされた正確な式をログに記録できるためです。ここに問題があります。プリプロセッサは、式がconst char *として渡される前に式を展開するので、もともとアサーションされたのと同じ式ではありません。例えば

REQUIRE(foo != NULL); 

は、この呼び出しにつながる:あなたが見ることができるように

PerformAssertion("REQUIRE", "foo != 0", foo != 0); 

、表現の一部が、例えば、展開されています式foo != NULLはログにfoo != 0と表示されます。 NULL0と定義されたマクロ)は、アサーションメッセージテキストを構築する前にCプリプロセッサによって拡張されました。メッセージテキストの拡張を無視するか無視する方法はありますか?

編集:ここでは解決策は、好奇心、誰のために、です:

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, #expr, "REQUIRE") 

#define INTERNAL_REQUIRE(expr, exprString, macroName) \ 
PerformAssertion(macroName, exprString, expr); 
+2

2つの引数の形式の代わりに 'INTERNAL_REQUIRE(expr、#expr、" REQUIRE ")'を使用するだけですか? – kennytm

+1

うん、それはやるよ。 – Jeff

答えて

5

は、内部が必要に呼び出しの前に文字列化を作ってみましょう。問題は、NULLを展開する2番目の展開で内部要求に渡されることです。その前にストリングが起こった場合。 requireマクロでは、NULLを展開しません。ここで

+0

完璧に働いた、ありがとう!どのようにそれがより詳細に働いたか説明してもらえますか? – Jeff

2

は何が起こっているのです:あなたは「stringization」演算子#が適用されるマクロが第二レベルであることから、以下のように、一連の操作が動作します。

  • プリプロセッサはREQUIRE(NULL)の引数を識別し、argument substitutionを行い、 C 6.10.3.1に従って。この時点で、NULL0として展開されているため、置換えはINTERNAL_REQUIRE(0, "REQUIRE")のようになります。
  • プリプロセッサは、INTERNAL_REQUIREでマクロチェーンの展開を続けます。この時点で、マクロがNULLで呼び出されたという事実は失われます。プリプロセッサに関する限り、INTERNAL_REQUIREに渡される式は0です。

    パラメータ置換リストで、トークンまたは##の前処理に続いて前処理#または##が先行しない限り、この問題を解決する鍵は、標準からこの段落にある

トークン(下記参照)は、そこに含まれるすべてのマクロが展開された後、対応する引数に置き換えられます。

これは、正確な式をキャプチャしたい場合は、マクロ展開の最初のレベルで行う必要があることを意味します。

+0

私の好奇心を満たし、Daniのソリューションがなぜ機能するのかを説明していただきありがとうございます。 – Jeff

関連する問題