- •Contents
- •1.1 Fundamental Types
- •1.2 T Classes
- •1.3 C Classes
- •1.4 R Classes
- •1.5 M Classes
- •1.6 Static Classes
- •1.7 Buyer Beware
- •1.8 Summary
- •2 Leaves: Symbian OS Exceptions
- •2.1 Leaving Functions
- •2.3 Constructors and Destructors
- •2.4 Working with Leaving Functions
- •2.5 Trapping a Leave Using TRAP and TRAPD
- •2.6 LeaveScan
- •2.7 Summary
- •3 The Cleanup Stack
- •3.1 Using the Cleanup Stack
- •3.2 How Does the Cleanup Stack Work?
- •3.4 Using TCleanupItem for Customized Cleanup
- •3.5 Portability
- •3.6 An Incidental Note on the Use of Casts
- •3.7 Summary
- •5 Descriptors: Symbian OS Strings
- •5.3 Pointer Descriptors
- •5.4 Stack-Based Buffer Descriptors
- •5.5 Heap-Based Buffer Descriptors
- •5.6 Literal Descriptors
- •5.7 Summary
- •6 Good Descriptor Style
- •6.1 Descriptors as Parameters and Return Types
- •6.2 Common Descriptor Methods
- •6.3 The Use of HBufC Heap Descriptors
- •6.4 Externalizing and Internalizing Descriptors
- •6.5 The Overuse of TFileName
- •6.6 Useful Classes for Descriptor Manipulation
- •6.7 Summary
- •7 Dynamic Arrays and Buffers
- •7.1 CArrayX Classes
- •7.3 Why Use RArray Instead of CArrayX?
- •7.4 Dynamic Descriptor Arrays
- •7.5 Fixed-Length Arrays
- •7.6 Dynamic Buffers
- •7.7 Summary
- •8.1 Multitasking Basics
- •8.2 Event-Driven Multitasking
- •8.3 Working with Active Objects
- •8.4 Example Code
- •8.5 Threads Without an Active Scheduler
- •8.6 Application Code and Active Objects
- •8.7 Summary
- •9 Active Objects under the Hood
- •9.1 Active Object Basics
- •9.2 Responsibilities of an Active Object
- •9.3 Responsibilities of an Asynchronous Service Provider
- •9.4 Responsibilities of the Active Scheduler
- •9.5 Starting the Active Scheduler
- •9.6 Nesting the Active Scheduler
- •9.7 Extending the Active Scheduler
- •9.8 Cancellation
- •9.9 Request Completion
- •9.10 State Machines
- •9.11 Long-Running Tasks
- •9.14 Common Mistakes
- •9.15 Summary
- •10 Symbian OS Threads and Processes
- •10.2 Thread Priorities
- •10.3 Stopping a Running Thread
- •10.5 Exception Handling
- •10.6 Processes
- •10.7 Summary
- •11.2 How Do the Client and Server Fit Together?
- •11.3 How Do the Client and Server Communicate?
- •11.5 How Do Synchronous and Asynchronous Requests Differ?
- •11.6 How Is a Server Started?
- •11.7 How Many Connections Can a Client Have?
- •11.8 What Happens When a Client Disconnects?
- •11.9 What Happens If a Client Dies?
- •11.10 What Happens If a Server Dies?
- •11.15 How Many Outstanding Requests Can a Client Make to a Server?
- •11.16 Can Server Functionality Be Extended?
- •11.17 Example Code
- •11.18 Summary
- •12.2 Client Boilerplate Code
- •12.3 Starting the Server and Connecting to It from the Client
- •12.4 Server Startup Code
- •12.5 Server Classes
- •12.6 Server Shutdown
- •12.7 Accessing the Server
- •12.8 Summary
- •13 Binary Types
- •13.1 Symbian OS EXEs
- •13.2 Symbian OS DLLs
- •13.3 Writable Static Data
- •13.4 Thread-Local Storage
- •13.5 The DLL Loader
- •13.6 UIDs
- •13.8 Summary
- •14 ECOM
- •14.1 ECOM Architecture
- •14.2 Features of an ECOM Interface
- •14.3 Factory Methods
- •14.4 Implementing an ECOM Interface
- •14.5 Resource Files
- •14.6 Example Client Code
- •14.7 Summary
- •15 Panics
- •15.2 Good Panic Style
- •15.3 Symbian OS Panic Categories
- •15.4 Panicking Another Thread
- •15.5 Faults, Leaves and Panics
- •15.6 Summary
- •16 Bug Detection Using Assertions
- •16.3 Summary
- •17 Debug Macros and Test Classes
- •17.1 Heap-Checking Macros
- •17.2 Object Invariance Macros
- •17.3 Console Tests Using RTest
- •17.4 Summary
- •18 Compatibility
- •18.1 Forward and Backward Compatibility
- •18.2 Source Compatibility
- •18.3 Binary Compatibility
- •18.4 Preventing Compatibility Breaks
- •18.5 What Can I Change Without Breaking Binary Compatibility?
- •18.6 Best Practice: Planning for Future Changes
- •18.7 Compatibility and the Symbian OS Class Types
- •18.8 Summary
- •19 Thin Templates
- •20.1 Class Layout
- •20.3 Parameters and Return Values
- •20.4 Member Data and Functional Abstraction
- •20.5 Choosing Class, Method and Parameter Names
- •20.7 Summary
- •21 Good Code Style
- •21.1 Reduce the Size of Program Code
- •21.2 Use Heap Memory Carefully
- •21.3 Use Stack Memory Carefully
- •21.5 Optimize Late
- •21.6 Summary
- •Glossary
- •Bibliography and Online Resources
- •Index
26 |
LEAVES: SYMBIAN OS EXCEPTIONS |
TRAP, even if none of the callers can handle errors and they all call User::LeaveIfError() as shown in the previous code sample.
However, if you know who all your callers are (for example, if the function is internal to your component) and you know that the callers will all TRAP the call, it may be worthwhile implementing the version that returns an error. This limits the use of a TRAP to one place in the code.
Use of TRAP is an expensive way of managing a leave in terms of executable size and execution speed. You should attempt to minimize the number of TRAPs you use in your code where possible. However, every program must have at least one TRAP to catch any leaves that occur.
2.6 LeaveScan
LeaveScan is a useful tool which you should run regularly against your source code. It checks that all functions which have the potential to leave are named according to the Symbian OS convention, with a trailing L. LeaveScan can be used on your source to indicate areas of code where you may have forgotten to use the convention. By revealing where leaves may occur but are not acknowledged by the function name, it highlights potential bugs and gives you an opportunity to fix the problem and ensure that your code handles any leaves correctly.
LeaveScan works by examining each line of source code and checking that functions which do not terminate in L cannot leave. However, there are a few functions (more accurately, operators) in Symbian OS that may leave but cannot have an L suffix (such as operator<< and operator>> for RWriteStream and RReadStream respectively). The naming convention cannot be applied appropriately to operators and, unfortunately, LeaveScan does not have the sophisticated logic needed to recognize operators that may leave. When you use operators that you know have the potential to leave, you’ll have to remember to check this code by sight yourself.
LeaveScan also checks functions which do have a trailing L to see if they really can leave. If functions are encountered which do not leave, LeaveScan raises a warning. However, this scenario can be perfectly valid, for example, when implementing an abstract function such as CActive::RunL(), some implementations may leave but others may not.
SUMMARY |
27 |
LeaveScan will highlight functions which may leave but are incorrectly named without a suffixed ”L”. The potential to leave occurs when a function:
•calls other functions which themselves leave (and thus have function names ending in L) but does not surround the function call with a TRAP harness
•calls a system function which initiates a leave, such as
User::LeaveIfError() or User::Leave()
•allocates an object on the heap using the Symbian OS overload of operator new.
2.7Summary
This chapter discussed leaves, which are the lightweight equivalent of C++ exceptions on Symbian OS. A leave is used to propagate an error which occurs because of exceptional conditions (such as being out of memory or disk space) to higher-level code which can handle it. A Symbian OS leave is equivalent to a C++ throw and a TRAP harness is used to catch the leave. In fact, a TRAP harness is effectively a combination of try and catch.
Having compared leaves and TRAPs with standard C++, it’s worth making a comparison with the standard library too. TRAP and leave are analogous to the setjmp() and longjmp() methods, respectively – setjmp() stores information about the location to be ”jumped to” in a jump buffer, which is used by longjmp() to direct the code to jump to that point.
On Symbian OS, if a function can leave, that is, fail under exceptional conditions, it indicates this by suffixing its function name with L. Of all the Symbian OS naming conventions, this is one you should comply with because, if you don’t, it’s hard to know whether you can call a function without potentially orphaning any local heap-based variables. You can check that you have adhered to the naming convention by running the LeaveScan tool over your source code.
A function can leave if it calls one of the system functions which cause a leave (such as User::Leave()), calls another leaving function (such as NewL()) or allocates memory using the Symbian OS leaving overload of operator new. Some functions should not leave, namely constructors (I’ll discuss the reasons behind this in detail in Chapter 4,
28 |
LEAVES: SYMBIAN OS EXCEPTIONS |
and explain how two-phase construction can be used to prevent it) and destructors, which could potentially leak memory by leaving before completing object cleanup.
This chapter described best practice for writing and calling leaving code, particularly when a function may have a return value for initialized pointer data. It also discussed the best use of the TRAP and TRAPD macros, which can have a significant overhead in terms of code size and runtime speed.
The next chapter discusses the use of the cleanup stack to protect heap-based local variables against orphaning in the event of a leave.