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 )

好吧,我完全理解这是如何工作的,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() );                        \
  }                                                             \
}

提前致谢!

-汉内斯

4

2 回答 2

2

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

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

使用他们的代码,你会得到一个错误//b,因为代码扩展为do { ... } while (false) doSomething();- 你会错过;after 条件。

使用您的代码,//b可以愉快地编译,但//a可能会给您一个“空语句”警告,因为该行将扩展为{ ... };,在块之后带有多余的内容;

为什么他们强迫你使用//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 捕获了您在表达式中使用的变量。

于 2013-03-11T08:31:47.027 回答
1

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

http://cnicholson.net/2009/02/stupid-c-tr​​icks-adventures-in-assert/给出了解释。

实际上,将多行宏包装在do { } while (false);. 这是一种允许使用这些宏的解决方法,例如,在 un-bracedif else中。

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

结果将是出乎意料地只有第一行被执行,这肯定会导致意想不到的行为。所以我猜他们并不是完全无能...

http://kernelnewbies.org/FAQ/DoWhile0还解释了为什么我的解决方案不起作用。if ( MULTI_LINE_MACRO();condition_a) { /* macro stuff */ } ; if (condition_a) { /* macro stuff */ } ; else // << 因为 ; 从未执行 以上。

所以我想我必须禁用警告。GCC 对此 ( ({ MACRO })) 有一个解决方法,称为Statement Expression,但我认为这不适用于 VS2010。

于 2013-03-11T08:33:17.717 回答