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.