Symbian way of implementing exceptions vs Standard C++ way

One of the most interesting and controversial characteristics of Symbian, is the way in which exceptions are implemented. Why am I saying this? Because opinions are split. Is it easier and faster to use leave-mechanism or is just a useless overhead, also in terms of program size, execution speed but also in getting familiar and learning them?

Browsing through Symbian forums and developers’ communities I read (almost) only positive and pro opinions. I also had the chance to go through codeproject programmers community and I have found an article claiming that Symbian OS is mostly designed in a faulty manner and that Symbian C++ is a very obscure and cumbersome clone of the Standard C++ language. I thought of picking several quotes from this article in order to highlight the way exceptions are implemented in Symbian.

There is a paragraph called “Making the key decision based on false facts and statements”, and exceptions’ implementation is given as being the best example. Here it is stated that the “false” premises that led to the implementation of this mechanism was the fact that: “C++ makes the compiled code to grow with 40% in size”. I would not assume that this was the driving reason of implementing the “leaves-mechanism” along with the “two-phase construction”, since Symbian OS developers worked on this well before Bjarne Stroustrup wrote about C++ exceptions.

But before assuming any fake presumption let’s have a look on how the exception mechanism is implemented in standard C++.

In fact … what is an exception?

For an engineer coming from an embedded background, like myself, an important distinction has to be made: the difference between interrupts and exceptions. If you were reading some microprocessor or microcontroller specifications you would see that the corresponding microprocessor has an interrupt request line where it can receive different types of interrupts. Usually interrupts having lower channel numbers are named exceptions, those have higher priority than rest of the external interrupts. Basically by external interrupts specifications are referring to any external signal that can come from a peripheral in order to make the CPU aware that some data needs to be processed. Those are referred as being synchronous in the way that their occurrence is known at compile-time. The programmer knows when some data needs to be sent out or received via SPI, or when a DMA transfer is requested.

Exceptions are said to be asynchronous because those are not known at compile-time, they are unpredictable. Some examples of exceptions that may occur are: writing into a file which is read only, a heap overflow when attempting to dynamically create data, division by zero (even if this can be caught at compile time), attempting to delete a file or a directory which does not exist, attempting to write outside an array’s boundaries and many others. Basically (or at least in the context of C/C++ mechanism of handling exceptions) there are four ways to treat such events:

    1. ignore them (a real programmer should not do this, or in any case this is not a reasonable way to write code)
    2. associate an error code to every possible (imaginable) type of exception and check for it (this is preferred by most of junior programmers and should be totally avoided especially when using nested calls)
    3. use non-local jumps to re-route the thread of execution (this is the C approach of handling interrupts)
    4. use exception mechanism (this is how C++ does it)

I do not think considering first two ways of treating exceptions since those should be avoided and are regarded as a bad programming practice. The exception handling mechanism implemented in C maybe worth an eye, also because it was used in Symbian OS until quite recently, but this won’t make the subject of this post. Here I thought of discussing only about C++ approach and how is it different than the one in Symbian.

The exception handling mechanism is activated by declaring a try block, as you may see below:

try {
// code to be tested against exceptions
}

Immediately after this block an exception handler has to follow. This is declared via catch keyword, it is like the interrupt handler, if there are some embedded programmers among readers.

In C++ there is the possibility of intentionally raising an exception using:

throw exception_id;

This makes the compiler to search back in the calling tree for a handler that can catch an integer. Basically this is the exception handling trio in C++: try, catch and throw.

In Symbian, as I roughly wrote some lines about, those things are handled like this:

    – a function supposed to perform an operation which is not guaranteed to succeed is marked with a trailing L, this can be considered an equivalent of the try block from C++
    – TRAP and TRAPD macros act like an exception handler, they are similar to catch from C++
    – a throw statement from C++ has a substitute in Symbian a call to one of the functions User::Leave() and User::LeaveIfError()

Invoking the try block triggers an automatic call to the destructors of every statically constructed object. In case an exception occurs, every automatic variable declared inside the try block is properly deleted via a notorious mechanism called stack unwinding, which must sound quite familiar to Symbian programmers. The main issue that arises from an exception call is about dynamically created objects, for which the destructor is not called automatically. We’ll see shortly how is handled in C++ as well as in Symbian C++.

An important thing to stress is the way the exception code, or information, is transmitted from throw to catch.
How can the handler recognize the exception code since this is lost after the exception has been generated?

Valid point!

We can take as an example the way in which function parameters are transmitted from the called function to the caller code. If the parameters are passed by value, basically the function acts only over a copy of the parameters, after function exits those copies are destroyed. If the parameters are passed by value of by reference the function is forced to act directly on the original data, or objects. Exception handling slightly differs from function call mechanism, in the way that regardless of how the exception information is passed (value, reference or pointer): a copy of it is made in order to be used by the handler. This is why it is strongly recommended to catch the exception information by reference, since catching by value will generate a second copy.

Few lines above I said some words about stack unwinding and the fact that when an exception occurs inside a try block the destructors for the stack-based are called. Well, this is not the case in Symbian. The way exceptions, sorry … leaves, are implemented, does not allow this. Objects created on the stack are “leave-safe”, they do not contain destructors and but only data which can be destroyed in the event of a leave. A naming rule in Symbian says that T classes should always be created on the stack and must not have a destructor, they are said to be leave-safe.

In order to handle such objects created on the heap and referred by local variables, which can be left orphaned in the event of a leave, Symbian uses the Cleanup Stack.
The main point about the Cleanup Stack is that it must be called by programmer accordingly in order to avoid memory leaks or objects being orphaned. It is not like in standard C++ where this is implemented automatically in the exception handling mechanism. This is one of the deficiencies that is spotted in aforementioned article about Symbian design faults:

If the cleanup stack is implemented in OS as the exception cleanup mechanism, then inserting CleanupStack::Push and CleanupStack::Pop is the job of the compiler, not the developer. Humans are just not reliable in such a task

Probably this is a reasonable to think to bear in mind, probably here the Symbian designers made a mistake in allowing programmers to completely and freely insert exception where they think this is useful.

and farther …

Modern compilers are wise enough to insert cleanup mechanism only to places where it is actually required.

I would compare those words with the ones under this C++ FAQ website:

Exception handling seems to make my life more difficult; clearly I’m not the problem, am I??

Absolutely you might be the problem!

Standard C++ has a similar way of dealing with dynamically allocated objects. The programmer has the choice of using a smart pointer, rather than allocating memory into a raw pointer to object. Data saved in this smart pointer, namely auto_ptr, will be destroyed when the smart pointer dies. This is member of a template class defined in header.

I won’t go deeper into the auto_ptr because I also lack some consistent knowledge about it and I will stop here the comparison between the two languages (we may call them like this): Standard C++ and Symbian C++. Exception handling mechanism is far from being completely described here, in this post, but I will come back with a second part. This will be about how exceptions are handled within a constructor and a destructor and a more detailed investigation of Symbian’s Cleanup Stack.

Advertisements

One Response to Symbian way of implementing exceptions vs Standard C++ way

  1. anuna says:

    Wonderful and useful job!
    You are one in a million!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: