Panics are used to highlight a programming error in the mostnoticeable way, stopping the thread to ensure that the code is fixed, ratherthan potentially causing serious problems by contin
Trang 114.7 Summary
This chapter covered the important concepts behind the ECOM ture, which provides services to locate, resolve, instantiate and manageinstances of polymorphic plug-in DLLs
architec-The chapter discussed how to define an abstract interface and how
to implement it in an ECOM plug-in DLL The example showed how
to implement two concrete implementation classes for a fictional tographic interface, within a single polymorphic plug-in DLL For thisreason, an ECOM plug-in DLL is also known as an ”ECOM InterfaceImplementation Collection”, and assigned a targettype of ECOMIIC.The chapter also described how an interface client can use fac-tory methods provided by the interface to instantiate concrete instancesthereof, without having any knowledge of ECOM itself The factory instan-tiation functions, provided by the interface, call the ECOM frameworkusing REComSession::CreateImplementationL() The ECOMframework uses a default or customized resolver to examine the registry
cryp-of interface implementations and instantiate an object cryp-of the appropriateclass An interface client should simply include the header files whichdefine the interface and link against ECOM.lib
Chapter 13 discusses generic polymorphic plug-in DLLs and Symbian
OS DLL loading in more detail
Trang 2Panics
We experience moments absolutely free from worry These brief
respites are called panic
Cullen Hightower
One dictionary definition of panic is ”a sudden overpowering fright;sudden unreasoning terror often accompanied by mass flight” The wordpanic itself derives from Pan, the Greek god of nature and goatherds, whowas half man and half goat He was considered by some to represent animage of the devil Panic was said to resemble the mental or emotionalstate induced by him
On Symbian OS, when a thread is panicked, the code in it stopsrunning Panics are used to highlight a programming error in the mostnoticeable way, stopping the thread to ensure that the code is fixed, ratherthan potentially causing serious problems by continuing to run There is
no recovery from a panic Unlike a leave, a panic can’t be trapped Apanic is terminal
If a panic occurs in the main thread of a process, the entire process
in which the thread runs will terminate If a panic occurs in a secondarythread, it is only that thread which closes However, on hardware, ifany thread is deemed to be a system thread, i.e essential for the system
to run, a panic in the thread will reboot the phone.1 Otherwise, if it’snot a system thread, on target hardware and in release builds on theWindows emulator, the end result of a panic is seen as a ”Programclosed” message box, which displays the process name, along with apanic category and error code In debug emulator builds, you can choose
to break into the code to debug the cause of the panic – this is known asjust-in-time debugging
1 Forcing a reboot if a system-critical thread panics is a policy decision rather than a shortcoming of the operating system.
Trang 3User::Set-is enabled and a panic will terminate the entire program and enterthe debugger You can disable this by calling User::SetJustIn-Time(EFalse), whereupon the panic will appear as it does in releasebuilds and simply terminate the thread in which it occurred with anappropriate message box Just-in-time debugging can be re-enabled bycalling User::SetJustInTime(ETrue).
When just-in-time debugging is enabled, the panic calls a functioncalled ThreadPanicBreakPoint() This function presents the debug-ger with an information structure which contains information about the
ID and name of the thread that panicked, the panic category and reason(which are described shortly), and the name of the thread that caused thepanic The function then breaks into the code and launches the debuggerwithin the context of the function that called the panic, using asm int
3 You can use the debugger to look through the call stack to see wherethe panic arose and examine the appropriate state
There are some subtle differences in behavior between Symbian OSv8.0, which contains the new hard real-time kernel (known at the time
of going to press as ”EKA2”, which stands for ”EPOC Kernel Architecture2”) and previous releases of Symbian OS (EKA1)
Panics on EKA1
A call to the static function User::Panic() panics the currently ning thread A thread may panic any other thread in the system byacquiring an RThread handle to it (as discussed in Chapter 10) and call-ing RThread::Panic() Both functions take two parameters: a paniccategory string, which is limited to 16 characters, and an error code,expressed as a TInt
run-Panics on EKA2
A call to the static function User::Panic() panics the currently runningthread A thread may panic any thread in the same process by callingRThread::Panic(), but can no longer panic threads in any other
Trang 4GOOD PANIC STYLE 249
process.2 The panic functions take a panic category string, which cancontain an unlimited number of characters, and an error code, expressed,
as usual, as a TInt
15.2 Good Panic Style
Even without breaking into the debugger, you should still be able to trackdown the cause of a panic using the panic category string and errornumber It’s good style to make your panic category string descriptiveand unique, so other developers can locate the string in your header files,and with it, any associated panic error codes (which should also havesuitably descriptive names)
Thus, you might have a general panic header file for your library whichincludes the following:
static void Panic(TClangerEnginePanic aCategory);
#endif // CLANGERPANIC_H
Which defines the Panic() function separately as follows:
static void Panic(TClangerEnginePanic aCategory)
{ User::Panic(KClangerPanic, aCategory);
}
(You’ll notice by the way that the panic enumeration is a T Classbecause an enumeration is a type Chapter 1 discusses the differencesbetween class types on Symbian OS and how to use them.)
When the library code is passed invalid arguments, it may invokePanic() with the appropriate error code, resulting in a panic and
2 Except where a server thread uses RMessagePtr to panic a misbehaving client thread.
Trang 5250 PANICS
termination of the thread in which it is running The category and error will
be reported, and may then be traced back by searching the library’s headerfiles for "CLANGER-ENGINE", located inside ClangerPanic.h You’llsee I’ve commented each error’s enum value with its associated number,just to make the lookup easier I’ve tried to give each a descriptive name,though obviously they could be further documented, using in-sourcecomments, for clarity
Of course, if a client programmer has access to the source code forclanger.dll, they can also search it for calls to Panic() which use
a particular error value, to track down where a panic originated
15.3 Symbian OS Panic Categories
Symbian OS itself has a series of well-documented panic categoriesand associated error values You can find details of platform-specificpanics in your preferred SDK From Symbian OS v7.0, there is a specialPanics section in the C++ API Reference of each SDK, which contains acomprehensive list of the Symbian OS system panics Typical values youmay encounter include:
• KERN-EXEC 3 – raised by an unhandled exception (such as an accessviolation caused, for example, by dereferencing NULL, memory mis-alignment or execution of an invalid instruction) inside a system call
to the kernel executive; if an unhandled exception occurs insidecode which is instead executing in user mode, the panic is seen asUSER-EXEC 3
• E32USER-CBASE 46 – raised by the active scheduler as a result of astray signal (described further in Chapters 8 and 9)
• E32USER-CBASE 90 – raised by the cleanup stack when the objectspecified to be popped off is not the next object on the stack Thefollowing code illustrates the cause of the panic; this issue is describedmore fully in Chapter 3, which describes the cleanup stack
class CExample; // defined elsewhere
void CauseAPanicL()
{ TInt val = 1;
CExample* ptr1 = new (ELeave) CExample();
Trang 6PANICKING ANOTHER THREAD 251
CleanupStack::Pop(ptr2); // so the code never gets here }
As a user, if making the chess move you’ve carefully worked outcauses your game to panic and close, you’re probably not interested inthe category or error code Such precise details are irrelevant and the
”program closed” dialog is more irritating than helpful
Don’t use panics except as a means to eliminate programming errors, for example by using them in assertion statements Panick- ing cannot be seen as useful functionality for properly debugged software; a panic is more likely to annoy users than inform them.
15.4 Panicking Another Thread
I’ve shown how to call User::Panic() when you want the thread topanic itself But how, and when, should you use RThread::Panic()?
As I described earlier, the RThread::Panic() function can be used tokill another thread and indicate that the thread had a programming error.Typically this is used by a server to panic a client thread when the clientpasses a badly-formed request You can think of it as server self-defense;rather than go ahead and attempt to read or write to a bad descriptor, theserver handles the client’s programming error gracefully by panicking theclient thread It is left in a good state which it would not have been if
it had just attempted to use the bad descriptor Generally, in a case likethis, the malformed client request has occurred because of a bug in theclient code, but this strategy also protects against more malicious ”denial
of service” attacks in which a client may deliberately pass a badly-formed
or unrecognized request to a server to try to crash it The Symbian OSclient–server architecture is discussed in Chapters 11 and 12; the latterchapter includes sample code that illustrates how a server can panicits client
If you have a handle to a thread which has panicked, you candetermine the exit reason and category using ExitReason() and Exit-Category() This can be useful if you want to write test code to checkthat the appropriate panics occur, say from assertions in your librarycode, to defend it against invalid parameters Since you can’t ”catch” apanic, it’s not as straightforward as running the code in the main thread,passing in the parameters and checking that the code panics with thecorrect value The checking code would never run, because the panicwould terminate the main thread
Trang 7252 PANICS
A solution is to run deliberately badly-behaved client code in aseparate test thread, programmatically checking the resulting exit reasonsand categories of the panicked thread against those you would expect
to have occurred You should disable just-in-time debugging for theduration of the test, so that only the test thread, rather than the emulator,
is terminated For example:
TInt TestPanics(TAny* /*aData*/)
{// A panic occurs if code is called incorrectly
// Save current just-in-time status
TBool jitEnabled = User::JustInTime();
// Disable just-in-time debugging for this test
User::SetJustInTime(EFalse);
_LIT(KPanicThread, "PanicThread");
// Create a separate thread in which to run the panic testing
RThread testThread;
TInt r = testThread.Create(KPanicThread, TestPanics,
KDefaultStackSize, NULL, NULL);
Trang 8SUMMARY 253
15.5 Faults, Leaves and Panics
A fault is raised if a critical error occurs such that the operating systemcannot continue normal operation On hardware, this results in a reboot
A fault can only occur in kernel-side code or a thread which is essential tothe system, for example the file server, so typically you will not encounterthem unless you are writing device drivers or uncover a bug in the OS Ineffect, a fault is another name for a serious system panic
Chapter 2 discusses leaves in more detail, but, in essence, they occurunder exceptional conditions such as out of memory or the absence of acommunications link Unlike a panic, a leave should always be caught(”trapped”) by code somewhere in the call stack, because there shouldalways be a top-level TRAP However, if a leave is not caught, this impliesthat the top-level TRAP is absent (a programming error) and the threadwill be panicked and terminate
15.6 Summary
This chapter discussed the use of panics to terminate the flow of execution
of a thread Panics cannot be ”caught” like an exception and are severe,resulting in a poor user experience For that reason, panics are only useful
to track down programming errors and, on Symbian OS, are typicallycombined with an assertion statement, as discussed in the next chapter.This chapter described the best way to identify panics and illustratedhow to test the panics that you’ve added to your own code for defensiveprogramming It gave a few examples of commonly-encountered systempanics and directed you to the Panics section of the system documentationfor a detailed listing of Symbian OS system panics
Trang 10exclamation above, and died.
In C++, assertions are used to check that assumptions made about codeare correct and that the state, for example, of objects, function parameters
or return values, is as expected Typically, an assertion evaluates astatement and, if it is false, halts execution of the code and perhapsprints out a message indicating what failed the test, or where in code thefailure occurred
On Symbian OS, you’ll find the definition of two assertion macros1ine32def.h:
#define ASSERT_ALWAYS(c,p) (void)((c)||(p,0))
1 At first sight, these definitions seem more complex than they need to be, when the following simpler definition could be used:
#define ASSERT_ALWAYS(c,p) ((c)||(p))
The reason for the (p,0) expression is for cases where the type returned from p is void (the case when p is a typical Panic() function) or a value that can’t be converted to an integer type for evaluation The cast to void is present to prevent the return value of the expression being used inadvertently.
Trang 11256 BUG DETECTION USING ASSERTIONS
You’ll notice that the assertion macros do not panic by default, butallow you to specify what procedure to call should the assertion fail.This gives you more control, but you should always terminate the runningcode and flag up the failure, rather than return an error or leave Assertionshelp you detect invalid states or bad program logic so you can fix yourcode as early as possible It makes sense to stop the code at the point
of error, thus forcing you to fix the problem (or remove the assertionstatement if your assumption is invalid) If the assertion simply returns anerror on failure, not only does it alter the program flow, but it also makes
it harder to track down the bug
You should always raise a panic when an assertion statement fails.
Here’s one example of how to use the debug assertion macro:
void CTestClass::TestValue(TInt aValue)
{
#ifdef _DEBUG _LIT(KPanicDescriptor, "TestValue"); // Literal descriptor
#endif ASSERT_DEBUG((aValue>=0), User::Panic(KMyPanicDescriptor, KErrArgument));
}
Of course, this is somewhat awkward, especially if you expect to use
a number of assertions to validate your code, so you’ll probably define apanic utility function for your module, with its own panic category stringand a set of panic enumerators specific to the class So, for example, you’dadd the following enumeration to CTestClass, so as not to pollute theglobal namespace:
enum TTestClassPanic
{ EInvalidData, // =0 EUninitializedValue // =1
};
Then define a panic function, either as a member of the class or as a staticfunction within the file containing the implementation of the class:
Trang 12You could then write the assertion in TestValue() as follows:
void CTestClass::TestValue(TInt aValue)
enu-to locate the appropriate class and use the panic category enumeration enu-tofind the associated failure, which you will have named and documentedclearly to explain why the assertion failed
There may be cases where there’s nothing more a client programmercan do other than report the bug to you, the author of the code;alternatively, the problem could be down to their misuse of your library,which they’ll be able to correct I’ll discuss the pros and cons of usingassertions to protect your code against badly-programmed calling codelater in this chapter
If you don’t want or need an extensive set of enumerated panic values,and you don’t expect external callers to need to trace a panic, you mayconsider using a more lightweight and anonymous assertion A goodexample of this is to test the internal state of an object, which couldnot possibly be modified by an external caller, and thus should always
be valid unless you have a bug in your code Assertions can be addedearly in the development process, but left in the code, in debug builds, tovalidate the code as it is maintained and refactored In these cases, youmay consider using the ASSERT macro, defined in e32def.h as follows:
#define ASSERT(x) ASSERT_DEBUG(x, User::Invariant())
I like this macro because it doesn’t need you to provide a paniccategory or descriptor If condition x is false, in debug builds only, itcalls User::Invariant() which itself panics with category USER andreason 0 The macro can be used as follows:
Trang 13258 BUG DETECTION USING ASSERTIONS
ASSERT(iClanger>0);
As an alternative to using ASSERT to test the internal state of yourobject, you may wish to consider using the TEST_INVARIANT macro,which I discuss in more detail in Chapter 17
An alternative, useful definition of ASSERT which you may see insome Symbian OS code is as follows:
#ifdef _DEBUG
#ifdef ASSERT
#undef ASSERT
#endif
#define ASSERT_FILE (s) _LIT(KPanicFileName,s)
#define ASSERT_PANIC (l) User::Panic(KPanicFileName().Right(12),l)
#define ASSERT(x) { ASSERT_FILE ( FILE );
ASSERT_DEBUG(x, ASSERT_PANIC ( LINE ) ); }
#endif
This slightly alarming construction is actually quite simple; in debugbuilds, if condition x is false, the code is halted by a panic identifyingthe exact place in code (in the panic descriptor – which contains the last
12 characters of the filename) and the panic category (which containsthe line of code at which the assertion failed) The disadvantage ofusing this construct is that you are coupling the compiled binary directly
to the source file You cannot later modify your code file, even tomake non-functional changes to comments or white space lines, withoutrecompiling it to update the assertion statements The resulting binarywill differ from the original, regardless of the nature of the changes.Depending on how you deliver your code, this limitation may prohibityou from using this macro
Let’s move on from how to use the Symbian OS assertion syntax toconsider when you should use assertions and, perhaps more importantly,when you should not
Firstly, don’t put code with side effects into assertion statements Bythis, I mean code which is evaluated before a condition can be verified.For example:
ASSERT_DEBUG(FunctionReturningTrue(), Panic(EUnexpectedReturnValue)); ASSERT_DEBUG(++index<=KMaxValue, Panic(EInvalidIndex));
The reason for this is clear; the code may well behave as you expect indebug mode, but in release builds the assertion statements are removed
by the preprocessor, and with them potentially vital steps in your gramming logic Rather than use the abbreviated cases above, you shouldperform the evaluations first and then pass the returned values into the
Trang 14pro-−−−ASSERT−DEBUG 259assertion You should follow this rule for both ASSERT_DEBUG and ASSERT_ALWAYSstatements, despite the fact that the latter are com-piled into release code, because, while you may initially decide theassertion applies in release builds, this may change during the develop-ment or maintenance process You could be storing up a future bug forthe sake of avoiding an extra line of code.
You must also make a clear distinction between programming errors(”bugs”) and exceptional conditions Examples of bugs might be contra-dictory assumptions, unexpected design errors or genuine implementationerrors, such as writing off the end of an array or trying to write to a filebefore opening it These are persistent, unrecoverable errors which should
be detected and corrected in your code at the earliest opportunity Anexceptional condition is different in that it may legitimately arise, although
it is rare (hence the term ”exceptional”) and is not consistent with typical
or expected execution It is not possible to stop exceptions occurring,
so your code should implement a graceful recovery strategy A goodexample of an exceptional condition that may occur on Symbian OS is anout-of-memory failure, because it is designed to run constantly on deviceswith limited resources for long periods of time without a system reset
To distinguish between bugs and exceptions, you should consider thefollowing question Can a scenario arise legitimately, and if it can, isthere anything you should or could do to handle it? If your answer is
”yes”, you’re looking at an exceptional condition – on Symbian OS, this
is exhibited as a leave (leaving is discussed in Chapter 2) If the answer
is ”no”, you should consider the situation to be caused by a bug whichshould be tracked down and fixed The rest of this chapter will focus onthe use of assertions to highlight such programming errors
When code encounters a bug, it should be flagged up at the point
at which it occurs so it can be fixed, rather than handled or ignored(which can at best complicate the issue and, at worst, make the bugmore difficult to find or introduce additional defects as you ”code aroundit”) You could consider assertions as an annoying colleague, leaningover your shoulder pointing out defects for you as your code runs Theydon’t prevent problems, but make them obvious as they arise so you can
fix them
If you add assertion statements liberally as you write code, theyeffectively document assumptions you make about your program logicand may, in addition, flag up unexpected problems As you considerwhich assertions to apply, you are actually asking yourself what implicitassumptions apply to the code and how you can test them By thinkingabout each piece of code you write in this way, you may well discoverother conditions to test or eliminate that would not have been immediatelyobvious Frequent application of assertions as you code can thus helpyou to pre-empt bugs, as well as catch those already in existence
Trang 15260 BUG DETECTION USING ASSERTIONS
And there’s more! Another benefit of assertions is the confidence thatyour code is behaving correctly, and that you are not ignoring defectswhich may later manifest themselves where they are hard to track down,for example intermittently or through behavior seemingly unrelated to thecode that contains the error Say you write some library code containing
no assertions and then create some code to test it, which runs and returns
no errors; are you confident that everything behaves as you expect andthe test code checks every boundary condition? Are you sure? Certain?Adding assertions to test fundamental assumptions in your code willshow you immediately if it is swallowing or masking defects – and itshould give you more confidence that the test results are valid Sure, youmight hit some unpleasant surprises as the test code runs for the firsttime, but once you’ve ironed out any failures, you can be more confidentabout its overall quality What’s more, the addition of assertions alsoprotects your code against any regressions that may be introduced duringmaintenance and refactoring but which would not otherwise be picked
up by your test code What more could you ask?
The cases so far could be considered as ”self defense”, in that I’vediscussed using assertions to catch bugs inyour code Let’s move on toconsider defensive programming in general Defensive programming isnot about retorting ”It works OK on my machine” after being informedthat your code doesn’t work as expected It’s based on defending yourcode against irresponsible use or downright abuse by code that calls
it Defensive code protects functions against invalid input, by inspectingdata passed in and rejecting corrupt or otherwise flawed parameters, such
as strings that are too long or out-of-range numerical values
You’ll need to consider how to handle bad parameters depending onhow your code is called; for example, you may want to assert that thedata is good, terminating with a panic if it is not Alternatively, you maydecide to continue the flow of execution, so instead of assertions, you’llcheck each incoming parameter (e.g using if statements) and return
to the caller if invalid data is detected – either with an error value or aleave code Another method would be to check incoming data and, if
a parameter is invalid, substitute it with a default parameter or continuewith the closest legal value What you don’t want to do is ignore invalidinput and carry on regardless, since this could lead to problems later
on, such as data corruption Whatever method you use to handle illegalinput, it should be consistent throughout your code
Your clients should be testing with debug versions of your librariesand thus you could use ASSERT_DEBUG statements to alert them ofinvalid input, or other misuse, so they can correct it
Trang 16−−−ASSERT−ALWAYS 261
ASSERT_DEBUG assertions can be added early in the ment process to highlight programming errors and can be left in to validate the code as it is maintained and refactored – acting as a means to ”design by contract”.
You still need to be defensive by checking for illegal usage in releasebuilds too, but the case for using ASSERT_ALWAYS isn’t clear cut.Remember, your assertions will terminate the flow of execution andpanic the library, displaying a nasty ”program closed” dialog to the user,which is generally best avoided where possible Additionally, you shouldconsider the impact on the speed and size of your code if you applyassertion statements liberally in release builds
If you decide not to use ASSERT_ALWAYS to check incoming values,you should use another defensive technique to guard against illegal input,such as a set of if statements to check values and return error codes orleave when data is unusable You could use these in combination with
a set of ASSERT_DEBUG statements to alert the client programmer toinvalid use in debug builds, but often it is preferable to keep the flow
of execution the same in both debug and release builds In such cases,
I suggest you don’t use debug assertions to check input, but instead use
if statement checking in both modes, and document each expectedreturn value for your functions Client programmers should understandtheir responsibility to interpret the return value and act accordingly I’llillustrate this with an example later in this chapter
To determine whether you should use ASSERT_ALWAYS or another,less terminal, defense, I recommend that you consider whether the callingcode may be able to take a different action if you do return an error Invalidinput is a bug from the perspective of your code, but may be caused by
an exceptional condition in the calling code which can be handled
A simplistic example would be a call to your code to open and write to
a file, where the caller passes in the full file name and path, as well as thedata to be written to the file If the file does not exist, it is probably moreappropriate to return this information to the caller through a returnederror code or leave value than to assert in a release build Client codecan then anticipate this and deal with it, without the need for your library
to panic and alarm the user accordingly
Trang 17262 BUG DETECTION USING ASSERTIONS
Here’s an example of this scenario which illustrates how to defendagainst illegal parameters without assertions This code returns an error toallow the caller to recover if they pass in an invalid parameter Of course,
if the calling code is written in such a way that each parameter should becorrect and that only a bug could result in them being invalid, it can assertthat the return value from a call to WriteToFile() is KErrNone Onthe other hand, if it’s an exceptional circumstance that the file is missing
or the data is non-existent, it can handle it gracefully
TInt CTestClass::WriteToFile(const TDesC& aFilename,
const TDesC8& aData)
{ // Only executes if the file can be opened
// Writes aData to file, closes file etc
up the problem so it can be fixed A good example of this is in theSymbian OS array classes (RArray and RPointerArray), which have ASSERT_ALWAYSguards to prevent a caller passing an invalid index tomethods that access the array The class provides functions to determinethe size of the array, so if a caller attempts to write off the end of thearray, it can only be doing so because of a bug
Likewise, in the code above, if the context of the function means thatthe second parameter, aData, should never be an empty string, youcan replace the first if statement check with an ASSERT_ALWAYSstatement But this assumes knowledge of how clients expect to call it
Trang 18to do so, depending on the circumstances in which it is used Whateverthe decision, ignoring the problem of illegal input is not an option!
Use defensive coding techniques to protect your functions against invalid input from calling code ASSERT_ALWAYS should be used to protect against illegal input that can only ever have arisen through a bug in the caller.
16.3 Summary
This chapter discussed assertion statements on Symbian OS in terms ofhow they work, what they do and how you should use them Theirprimary purpose is for you to verify program logic as you write code and
to detect bugs at the point they occur so you can find and fix them Forthis reason, assertion statements must terminate the flow of executionupon failure, typically with a panic
The chapter recommended using ASSERT_DEBUG or ASSERT ments liberally in your code to check its internal state You should expect
state-to test your code thoroughly and fix defects before release, so youshouldn’t need to use ASSERT_ALWAYS to check the internals of yourcode A user, or another software developer using your code, expectsyou to have debugged your code; they don’t want to do it for you Fur-thermore, you should think carefully before adding assertion statements
to release code because of the added code size and extra executionoverhead associated with them
You should make the distinction between bugs and exceptions (failuresoccurring under exceptional circumstances) While you can use assertions
to catch bugs, you should handle exceptions gracefully, since they mayoccur legitimately in your code, albeit as an atypical path of execution.This point extends to your client’s code – you should program defensivelyand consider whether invalid input has arisen because of a bug orexception in the caller If it can only be due to a bug, it is acceptable touse ASSERT_ALWAYS statements in your code to indicate to the clientthat they have a programming error which needs fixing However, sincerelease build assertion statements have a cost in terms of size, speed and
Trang 19264 BUG DETECTION USING ASSERTIONS
ugly code termination, I advise you to consider carefully before usingthem against invalid input data which may have arisen from an exceptionthat the caller can handle more gracefully
This chapter illustrated some aspects of defensive programming besidesthe use of assertion statements, but added this note of caution You shouldconsider carefully how much defensive code to use, and whether it variesbetween debug and release builds It can create additional complexity
in your code, leaving it open to its own set of bugs, and, if you checkparameter data for every possible error, it can also make your code slowand bloated You should take care to use defensive techniques wherethey are most effective, and document them clearly so the client canbuild their own bug catching and exception handling around them.The paradox is that you want problems to be noticeable so they areflagged up during development, but you want them to be inconspicuous
in your production code You should consider your approach to defensivecode and assertions appropriately for each project you work on In allcases, it’s best to keep it consistent and consider error handling as animportant issue to be determined and defined at an architectural level.The next chapter discusses useful debug macros and test classes onSymbian OS for tracking down programming errors such as memory leaksand invalid internal state You can find more information about handlingleaves (Symbian OS exceptions) in Chapter 2