Search code examples
c++unit-testingcompiler-warningscppunitc4127

CppUnit expect exception with Assert Throw compiles with warning C4127


Currently I am writing unit tests in C++ with CppUnit. Recently I needed to check that an exception is thrown in a specific case using CppUnits macro:

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

the warning during the compilation of the test surprised me (on VS2010, but will be a warning on other compilers as well...):

warning C4127: conditional expression is constant

I looked into the macro definition of CppUnit and found the following:

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 )

Well, I totally understand how this works, the do while loop is executed only once, because of the false, and the break is used to not execute the Asserter::fail() part. But why are they doing it like this? It - of course - triggers the compiler warning, as the break condition for the while loop is obviously always "false". But isn't there a more elegant way to do this? I usually adhere to the no-warning-compilation principle, so this really bugs me.

So my question really is, why didn't they implement it like this:

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

Thanks in advance!

-Hannes


Solution

  • OK, I guess I found the answer by myself:

    http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ gives the explanation.

    It is actually a common practice to wrap multi-line macros in do { } while (false);. This is a workaround to allow for using those macros, e.g., in un-braced if else.

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

    The result would be that unexpectedly only the first line gets executed, which definitely leads to unexpected behaviour. So they were not entirely incompetent I guess...

    http://kernelnewbies.org/FAQ/DoWhile0 also explains why my solution would not work. The MULTI_LINE_MACRO(); within the if would expand, e.g., to if (condition_a) { /* macro stuff */ } ; else // << never executed because of the ; above.

    So I guess I have to disable the warning. GCC has a workaroud for this ( ({ MACRO }) ), called a Statement Expression, but I don't think this works on VS2010.