CppUnit期望Assert Throw编译异常并发出警告C4127

时间:2013-03-11 07:46:01

标签: c++ unit-testing compiler-warnings cppunit c4127

目前我正在使用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 )

好吧,我完全理解这是如何工作的,do while循环只执行一次,因为false,而break用于不执行Asserter :: fail()部分。但他们为什么这样做呢?它 - 当然 - 触发编译器警告,因为while循环的中断条件显然总是“假”。但是,有没有更优雅的方式来做到这一点?我通常坚持无警告编译原则,所以这真的让我烦恼。

所以我的问题是,他们为什么不这样实现它:

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

提前致谢!

-Hannes

2 个答案:

答案 0 :(得分:2)

原因是使断言成为一个陈述。考虑宏的这两种用法:

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

使用他们的代码,您会收到//b的错误,因为代码会扩展为do { ... } while (false) doSomething(); - 在条件之后您将错过;

使用您的代码,//b很乐意编译,但是//a会给您一个“空语句”警告,因为该行会扩展为{ ... };,并使用superfluos {{1块之后。

为什么他们强制你使用;我不知道 - 但我更喜欢//a更多因为只有//b之后的;每一行。一个人不必将断言与正常陈述区分开来。

<强> PS: 我不确定但{ ... }块和do {...} while(false)语句之间可能存在更多差异,这些语句允许将断言宏放在不允许使用简单块的位置。

使用C ++ 11

编辑,您可以使用lambda(在一个地方定义并调用它):

#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() );                        \
}() 

但是,可能会有一些警告,例如:由于lambda捕获了你在表达式中使用的变量。

答案 1 :(得分:1)

好吧,我想我自己找到了答案:

http://cnicholson.net/2009/02/stupid-c-tricks-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)         {/ *宏观内容* /}         ;     否//&lt;&lt;从来没有因为;上方。

所以我想我必须禁用警告。海湾合作委员会有一个工作组(({ MACRO })),名为Statement Expression,但我认为这不适用于VS2010。