CppUnit期望Assert Throw异常编译时发出警告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循环的中断条件显然总是"false"。但是没有更优雅的方法来做这件事吗?我通常坚持无警告编译原则,所以这真的让我很困扰。

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

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

提前感谢!

hannes

原因是要使断言成为一个语句。考虑以下两种宏用法:

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

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

在您的代码中,//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捕获了表达式中使用的变量

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

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();将展开,例如为如果(condition_a){/*宏文件*/};Else//<<从未执行过,因为;以上。

所以我想我必须禁用警告。GCC对此有一个解决方案(({ MACRO })),称为语句表达式,但我认为这在VS2010上不起作用。

最新更新