Welcome to Scribd. Sign in or start your free trial to enjoy unlimited e-books, audiobooks & documents.Find out more
Standard view
Full view
of .
Look up keyword
Like this
0 of .
Results for:
No results containing your search query
P. 1
Using Assertions to Detect Bugs

Using Assertions to Detect Bugs

|Views: 99|Likes:
Published by Jo Stichbury
Part of the Code Clinic series. First published in 2008.
Part of the Code Clinic series. First published in 2008.

More info:

Published by: Jo Stichbury on Jun 29, 2010
Copyright:Attribution Non-commercial


Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less





Using Assertions to Detect Bugs
by Jo Stichbury
A good way to detect bugs in C++ is to use assertionstatements. An assertion checks that the state of objects,function parameters or return values, is as expected.Typically, an assertion evaluates a statement and, if it isfalse, halts execution of the code.
The denition of two assertion macros are found in
. The
macro performs anassertion check in both debug and release builds, whilethe
macro checks code in debugbuilds only, and has no effect in release builds.
// For debug and release builds
#dene __ASSERT_ALWAYS(c,p) (void)((c)||(p,0))
...// For debug builds only
#if dened(_DEBUG)#dene __ASSERT_DEBUG(c,p) (void)((c)||(p,0))
#dene __ASSERT_DEBUG(c,p)
is a conditional expression that returns atrue or false value. Parameter
is a function, called if
is false, which should halt the ow of execution. In effect,
the code behaves as follows:
if (!c)p
You’ll notice that the assertion macros do not panic bydefault, but allow you to specify the code to run shouldthe assertion fail. My advice is always to use a panic inan assertion statement (or write a custom function that
rst logs the specic failure to le and then panics). This
is because you should immediately terminate the running
code and ag up the failure, rather than return an error,
leave or do nothing. Assertions help you detect invalid
states or bad program logic so you can x your code asearly as possible. It makes sense to stop the code atthe point of error, thus forcing you to x the problem (or
remove the assertion statement if your assumption is
invalid). If the assertion simply returns an error on failure,not only does it alter the program ow, but it also makes it
harder to track down the bug.
Here's one example of how to use the
void CExampleClass::TestValue(TInt aValue)
{#ifdef _DEBUG
// Dene panic literal in debug build only// to avoid a compiler warning, since it’s used// in the debug assertion only_LIT(KPanicDescriptor, “TestValue”);
Of course, this is somewhat awkward, especially if youexpect to use a number of assertions to validate your
code, so it’s sensible to dene a panic utility function for
your module:
enum TExampleEnginePanic
ECorrupt, // =0,ENotInitialized, // =1,EInvalidTestValue, // =2};
static void ExamplePanic(TExampleEnginePanicaCategory)
_LIT(KExamplePanic, “EXAMPLE-ENGINE”);User::Panic(KExamplePanic, aCategory);
The assertion in
is now written as follows:
void CExampleClass::TestValue(TInt aValue)
The advantage of using an identiable panic descriptor
and enumerated values for different assertion conditionsis traceability, for yourself and callers of your code. Thisis particularly useful for others using your libraries, sincethey may not have access to your code in its entirety,
but merely to the header les. If your panic string is clear
and unique, they should be able to locate the appropriate
class and use the panic category enumeration to nd the
associated failure, which you will have named and docu-mented clearly to explain why the assertion failed.There may be cases where there’s nothing more a clientprogrammer can do other than report the bug to you, theauthor of the code; alternatively, the problem could be
down to their misuse of your API, which they’ll be able to
If you don’t want or need an extensive set of enumerated
panic values, and know that external callers will neverneed to trace a panic, you may consider using a morelightweight assertion macro. A good example of wherethis may be appropriate is when you want to test theinternal state of an object, which could not possibly be
modied by an external caller, and thus should always bevalid unless you have a bug in your own code. In these
cases, you may consider using the
macro, de-
ned in
as follows:
#dene ASSERT(x) __ASSERT_DEBUG(x,User::Invariant())
In debug builds only, if condition x is false,
is called, which itself panics withcategory
and reason 0. The macro can be used asfollows:
Personally, I like this macro because it doesn’t need you
to provide a panic category or descriptor. Some Sym-bian developers consider it to be infuriating, because itallows you to scatter assertions throughout your codewith no easy way to diagnose what went wrong. Whileit’s OK to do so if you’re sure you’re going to be the only
developer that sees them re, and immediately x the
code accordingly, if they do make it ‘downstream’, they
make problems harder to trace. I think it’s better to use
them than nothing at all (which may be the case if youhave to go back and add supporting code to use
) but I include the following caution here
for completeness:When adding an assertion, consider whether anyone else
will ever see it. If so, make it traceable.
As an alternative to using
to test the internalstate of your object, you may wish to consider using the
Avoid ‘side effects’ 
Don’t put code with side effects into assertion statements
that execute in debug builds only. By this, I mean codethat is evaluated before a condition can be veried. For
 The reason for this is clear; the code may well behaveas you expect in debug mode, but in release builds theassertion statements are removed by the preprocessor,and with them potentially vital steps in your programminglogic. Rather than use the abbreviated cases above, you
should perform the evaluations rst and then pass the
returned values into the assertion.
In fact, you should follow this rule for both
statements, despitethe fact that the latter are compiled into release code, be-cause, while you may initially decide the assertion appliesin release builds, this may change during the develop-ment or maintenance process. You could be storing up afuture bug for the sake of avoiding an extra line of code.
It goes without saying that your code should be tested
thoroughly. A user, or another software developer usingyour code, expects you to have debugged your codebefore release; they don’t want to do it for you. The use ofassertion statements in debug builds can help you detect
programming errors inside your code and x them before
delivery.So why would you need to use assertions in productioncode? Why does Symbian C++ have an
macro to execute checks in both debug and release
builds? For one thing, won’t the impact on the execution
speed and code size of adding extra checking to yourcode be prohibitive? Doesn’t the outcome of a failed as-sertion make for a poor user experience when it resultsin an application’s untimely end? Should you ever use
First of all, consider what
is check-ing. You’ve used debug assertions to verify your internalcode logic, so the only reason for using release buildassertions is to check the validity of incoming parameterdata. While invalid input is a bug from the perspective ofyour code, it may be caused by an exceptional conditionin the calling code, which that code can handle gracefully
if you return it an error or leave instead of ring an asser
-tion and terminating the running code.Let’s consider a simple example. Your code offers a
method to open and write to a le; the caller passes in thefull le name and path, as well as the data to be writtento the le. If the le does not exist, it is generally more
appropriate to notify the caller through a returned errorcode or leave value than to assert in a release build. Thecalling code can then deal with the error.
TInt CTestClass::WriteToFile(const TDesC&aFilename, const TDesC8& aData)
TInt r = KErrNone;if (KNullDesC8==aData){// No data to write - invalid!r = KErrArgument;
RFile le;__ASSERT_DEBUG(iFs,Panic(EUninitializedValue));r = le.Open(*iFs, aFilename, EFileWrite);if (KErrNone==r){// Only executes if le can be opened
return (r);

Activity (4)

You've already reviewed this. Edit your review.
1 thousand reads
1 hundred reads
Jason Viar liked this
mravindrarao liked this

You're Reading a Free Preview

/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->