2

現在、CppUnitを使用してC++で単体テストを作成しています。最近、CppUnitsマクロを使用して、特定のケースで例外がスローされることを確認する必要がありました。

CPPUNIT_ASSERT_THROW(
    boost::get<FooClassInBoostVariant>(m_boostVariantFooOrBar),
    boost::bad_get);

テストのコンパイル中の警告は私を驚かせました(VS2010では、他のコンパイラでも警告になります...):

warning C4127: conditional expression is constant

CppUnitのマクロ定義を調べたところ、次のことがわかりました。

do {                                                            \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
     expression;                                                \
  } catch ( const ExceptionType & ) {                           \
     cpputExceptionThrown_ = true;                              \
  }                                                             \
                                                                \
  if ( cpputExceptionThrown_ )                                  \
     break;                                                     \
                                                                \
  CPPUNIT_NS::Asserter::fail(                                   \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
} while ( false )

まあ、私はこれがどのように機能するかを完全に理解しています。falseのため、do whileループは1回だけ実行され、breakはAsserter :: fail()部分を実行しないために使用されます。しかし、なぜ彼らはこのようにそれをしているのですか?whileループのブレーク条件は明らかに常に「false」であるため、これは-もちろん-コンパイラの警告をトリガーします。しかし、これを行うためのよりエレガントな方法はありませんか?私は通常、警告なしのコンパイルの原則を順守しているので、これは本当に私を悩ませます。

だから私の質問は本当に、なぜ彼らはそれをこのように実装しなかったのかということです:

{                                                               \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
    expression;                                                 \
  } catch ( const ExceptionType & ) {                           \
    cpputExceptionThrown_ = true;                               \
  }                                                             \
                                                                \
  if ( !cpputExceptionThrown_ ) {                               \
    CPPUNIT_NS::Asserter::fail(                                 \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
  }                                                             \
}

前もって感謝します!

-ハンネス

4

2 に答える 2

2

その理由は、アサーションを1つのステートメントにするためです。マクロの次の2つの使用法を検討してください。

CPPUNIT_ASSERT_THROW(foo(), MyException);  // a
CPPUNIT_ASSERT_THROW(foo(), MyException)   // b - without trailing `;`!
doSomething();

//bそれらのコードでは、コードがに展開されるため、でエラーが発生しますdo { ... } while (false) doSomething();-条件の後が欠落していること;になります。

あなたのコードで、//bはうまくコンパイルされますが、その行がブロックの後にsuperfluosで//a展開されるので、「空のステートメント」警告を与える可能性があります。{ ... };;

なぜ彼らがあなたに使用を強制//aするのか私にはわかりませんが、各行//bの後にあるのは一貫しているので、私はもっと好きです。;アサーションのある行を通常のステートメントと区別する必要はありません。

PS:わかりませんが、単純なブロックが許可されていない場所にアサーションマクロを配置できるようにする{ ... }ブロックとステートメント の間にさらに違いがあるかもしれません。do {...} while(false)

編集: C ++ 11では、ラムダを使用できます(1か所で定義して呼び出す):

#define CPPUNIT_ASSERT_THROW(expression, ExceptionType)         \
[&]() -> void {                                                 \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
     expression;                                                \
  } catch ( const ExceptionType & ) {                           \
     cpputExceptionThrown_ = true;                              \
  }                                                             \
                                                                \
  if ( cpputExceptionThrown_ )                                  \
     return;                                                    \
                                                                \
  CPPUNIT_NS::Asserter::fail(                                   \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
}() 

ただし、たとえば、式で使用する変数をラムダがキャプチャするために、注意が必要な場合があります。

于 2013-03-11T08:31:47.027 に答える
1

OK、私は自分で答えを見つけたと思います:

http://cnicholson.net/2009/02/stupid-c-tr​​icks-adventures-in-assert/に説明があります。

実際には、複数行のマクロをでラップするのが一般的な方法do { } while (false);です。これは、ブレースなしなどでこれらのマクロを使用できるようにするための回避策if elseです。

if (condition_a)
    MULTI_LINE_MACRO();
else
    MULTI_LINE_MACRO_2();

その結果、予期せず最初の行だけが実行され、予期しない動作が発生することは間違いありません。だから彼らは完全に無能ではなかったと思います...

http://kernelnewbies.org/FAQ/DoWhile0は、私のソリューションが機能しない理由も説明しています。if内はMULTI_LINE_MACRO();、たとえばif(condition_a){/*マクロのもの*/};に展開されます。else // <<;のため、実行されませんでした。その上。

だから私は警告を無効にする必要があると思います。GCCには、ステートメント式({ MACRO })と呼ばれるこの()の機能がありますが、これはVS2010では機能しないと思います。

于 2013-03-11T08:33:17.717 に答える