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
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-bracedif else.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.