Rule 1: Functions that leave, and trap harnesses

Functions that leave

Instead of returning an error code, functions in Symbian OS should leave whenever an out-of-resource error occurs. A leave is a call to User::Leave(), and it causes program execution to return immediately to the trap harness within which the function was executed.

All functions that can leave should have the suffix This enables programmers to know that the function might leave. For example:

void MyFunctionL()
    {
    iMember = new( ELeave ) CMember;
    iValue = AnotherFunctionL();
    User::LeaveIfError( iSession.Connect() );
    }

Each line in MyFunctionL could cause a leave. Any of these lines makes MyFunctionL a leaving function.

Note, however, that it is very rarely necessary for application code to use a TRAP, since the application framework provides traps in the right places, and code to handle them. Traps should not be used in normal coding. In general, the way to deal with leaves is to simply allow them to propagate through the function by adding a letter suffix to the function name.

The new (ELeave) operator

The possibility of the new operator failing arises so often in Symbian OS that the new operator has been overridden to take a parameter, ELeave. When called with this parameter, the overridden new operator will leave if it fails to allocate the required memory. ELeave is defined as follows in e32std.h:

enum TLeave {ELeave};

This is implemented globally, so any class can use the new (ELeave) version of the operator. For example:

CSomeObject* myObject = new CSomeObject;
if ( !myObject ) User::Leave( KErrNoMemory );
// Can be replaced in by:
CSomeObject* myObject = new( ELeave ) CSomeObject;

The NewL() and NewLC() conventions

By convention, Symbian OS classes often implement the methods NewL()and NewLC(). These are declared as static methods in the class definition, which allows them to be called before an instance of the class exists. They are invoked using the class scope. For example:

CSomeObject* myObject = CSomeObject::NewL();

NewL() creates a new instance of the class on the heap and leaves if an out-of-memory error occurs. For simple objects this simply involves a call to new (ELeave). However, for compound objects it can incorporate two-phase construction (see Section Rule 3: Two-phase construction).

NewLC() creates a new instance of the class on the heap and pushes it onto the cleanup stack (see Section Rule 2: Using the cleanup stack), leaving if an out-of-memory error occurs. (In general, the C suffix at the end of a method means that it pushes a created object onto the heap before returning.)

When creating C-class objects, programs should use NewL() if a member variable will point to the object, and NewLC() if an automatic variable will point to it. However, it is not always advisable to implement NewL() and NewLC() for every class. If NewL() or NewLC() are called from only one place in the application, implementing them will actually use more lines of code than it saves. It is a good idea to assess the need for NewL() and NewLC() for each individual class.

Using a trap harness: TRAP and TRAPD

In exceptional circumstances, a developer is permitted to handle a leave by using a trap harness. However, the use of TRAP and TRAPD is only for special situations; for all general coding they should not be used. Usually the best course of action is to allow the leave to propagate back to the Active Scheduler for default handling. If there is any doubt about whether a trap harness is really needed, there is probably a cheaper or cleaner way to achieve the same functionality.

Symbian OS provides two trap harness macros, TRAP and TRAPD, which are both very similar. Whenever a leave occurs within code executed inside the harness, program control returns immediately to the trap harness macro. The macro then returns an error code, which can be used by the calling function.

To execute a function within a trap harness, use TRAPD as follows:

TRAPD( error, doExampleL() );
if ( error != KerrNone )
    {
    // Do some error code
    }

TRAP differs from TRAPD only in that the program code must declare the leave code variable. TRAPD is more convenient to use, as error is declared inside the macro. Using TRAP, the above example would become:

TInt error;
TRAP( error, doExampleL() );
if ( error != KerrNone )
    {
    // Do some error code
    }

Any functions called by doExampleL() are also executed within the trap harness, as are any functions called by them, and so on. A leave occurring in any function nested within doExampleL() will return to this trap harness. Other TRAP harnesses can also be nested within the first, so that errors are checked at different levels within the application.