When a function leaves, it transfers control directly to the
statement following the TRAP
(or TRAPD
) macro under
which it was invoked. This is carried out by setting the stack pointer to the
context of the original TRAP
macro, and jumping to the desired
program location. Therefore,
any objects created as automatic variables, passed by value as arguments, or created as member variables of other objects so created, will be orphaned: their destructor will not be called, and any resources they claim except for storage space on the stack, cannot be recovered.
This key aspect of EPOC exceptions has far-reaching implications:
There should be a clear distinction between objects which can be safely orphaned, and those which cannot.
This is embodied in the naming convention for types. All types
beginning with T
can be safely orphaned, including, for instance,
TInt
, TPoint
, TPtr
and many others. Such
objects can be freely allocated on the stack.
The basic requirement for T
objects is that all
their data is contained internally. Pointers, handles and references to data
owned by the T
object are not allowed (although such references to
data owned by other objects is allowed).
C
objects must never be orphaned: they should never
be allocated on the stack.
R
objects may contain handles to external resources,
but are generally designed so that the R object can be copied without copying
its resources. Copied R
objects may therefore be allocated on the
stack: the stack-allocated copies may safely be orphaned, provided the
resources are safely accessible by some other means.
Objects which cannot be safely orphaned must, if allocated inside the trap harness, be accessible somehow so they can be cleaned up.
The cleanup stack is the EPOC mechanism for handling this last problem.
The problem for heap-allocated resources is shown below. If the
call to DoSomethingL()
leaves, the CExample
object
would be orphaned on the heap: the memory used
for it could not have been recovered until the program terminates.
void doExampleL()
{
// An T-type object: can be declared on the stack
TBuf<10> buf;
// A C-type object: must be allocated on the heap
// Allocate and leave if can not
CExample* myExample = new (ELeave) CExample;
// do something that cannot leave: no protection needed
myExample->iInt = 5;
// PROBLEM: do something that can leave
myExample->DoSomethingL();
// delete
delete myExample;
}