Unexceptional C++

Started by
12 comments, last by Squirm 18 years, 8 months ago
I am faced with the unusual situation of wanting to write portable cross platform (and I mean really cross platform) code. By choice, I would prefer C++, but sadly, although all of these platforms support C++ to some extent, they do not all provide exception handling. Some of these are the kind of platform where leaking memory is not an option. Does anyone have any bright ideas on how I might go about writing safe code in this case?
Advertisement
Exception handling isn't really part of the platform, its part of the C++ language. Does the compiler you have for that platform not support exceptions?

"I can't believe I'm defending logic to a turing machine." - Kent Woolworth [Other Space]

You could try to look at how the CleanupStack is done in Symbian OS. That design originates from a time when exception handling was not available.

I must say that implementing something similar reliably is not for everybody, so unless you absolutely must, try to find a way to use normal exceptions!
Quote:Original post by Rattrap
Exception handling isn't really part of the platform, its part of the C++ language. Does the compiler you have for that platform not support exceptions?


One example of a -platform- that doesn't support exception handling is Qualcomm's BREW.

I remember we tried to make our own exception handler that would jump out using goto, but it didn't work out too well. I would say your best and safest bet is to do it the old fashioned way: any functions that can fail should return a "succeeded" boolean. And the caller has to check if the call failed.

As far as leaking memory.. just don't do it :) . Usually you can find out if the app has leaked memory when it finishes, so it's not hard to verify that your code is leak-free. The hard part is finding leaks, and one possible way of doing that would be to overload new/delete/malloc/whatever so that it records information about the allocation. Often if you only know the *size* of the leaked memory, that's enough to track it down.
Quote:Original post by pinacolada
As far as leaking memory.. just don't do it :) . Usually you can find out if the app has leaked memory when it finishes, so it's not hard to verify that your code is leak-free. The hard part is finding leaks, and one possible way of doing that would be to overload new/delete/malloc/whatever so that it records information about the allocation. Often if you only know the *size* of the leaked memory, that's enough to track it down.
If you're writing for an embedded system, try not to dynamically allocate data since that can cause memory fragmentation. If you really must, you're probably best with a full blown GC system than a half baked semi GC system to manage your memory. At least that's my experience in trying to debug a system that would die only after a week of running without rebooting.
There's an Embedded C++ spec (EC++) that's basically C++ without exceptions, templates, or RTTI.

You're probably best off with C if extreme portability if your goal.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Various answers:

Quote:You could try to look at how the CleanupStack is done in Symbian OS . . .

Symbian is one of the platforms, I'm more familiar with its cleanup stack than I ever really wanted to be, and it indeed doesn't support exceptions - they have been disabled in the compiler. Writing my own cleanup stack and using two stage construction is my fall back if I can't come up with something better.

Quote:As far as leaking memory.. just don't do it :) . Usually you can find out if the app has leaked memory . . .

Checking for leaks and fixing them doesn't solve my problem. It isn't succesful execution that I need to make leak free, it's failures. For example, some devices may have only 4K of ram - out of memory errors aren't just possible, they will happen regularly and your application is expected to not only cope, but to continue running, on a device which may not be rebooted for a couple of years in normal use.

thing::thing(){    mMember = new whatever;    mOther = new something;}

leaks if the second memory allocation fails. This is the kind of issue I was referring to when I said "safe". It can be made safe with exceptions. It is annoying to make safe otherwise. (Clearly this is a trivial example - it doesn't have to be a constructor, nor 2 allocations, to hit a similiar problem).

Quote:I would say your best and safest bet is to do it the old fashioned way: any functions that can fail should return a "succeeded" boolean. And the caller has to check if the call failed.

I'd rather write a cleanup stack :/ What happens if a function is called, allocates memory, then performs 6 operations each of which might fail? Each failure has to be checked, caught, the allocated memory freed, and then the fail returned to the next level. Yes, you can fix it, it's just a pain in the neck compared to 'try auto_ptr throw catch'

Quote:You're probably best off with C if extreme portability if your goal.

What does pure C provide me that C++ doesn't? All the platforms support most of C++, just some of them lack exceptions, so I don't think there's anything to gain?

Quote:you're probably best with a full blown GC system than a half baked semi GC system to manage your memory

What's a GC system?
Truely safe code is, in my opinion, much easier to write in the absence of exceptions anyway. Sure you have to be more explicit with your error checking, but you don't get anything for free.

In your example where you allocate some memory, perform 6 operations, each of which may fail, using exceptions doesn't give you much. After all, you don't nessecarily know all the side-effects of those six operations, and so if any can fail you need to undo any half-completed global operations anyway. And if you only have a catch at the bottom of the function, you won't know how far you've come.

Take a look at this (from here):

try {  AccessDatabase accessDb = new AccessDatabase();  accessDb.GenerateDatabase();} catch (Exception e) {  // Inspect caught exception}public void GenerateDatabase(){  CreatePhysicalDatabase();  CreateTables();  CreateIndexes();}


As Raymond points out, if CreateIndexes throws an exception which is caught by the caller, the caller has no way of know what state the database-creation was at. Does it need to drop the indexes? The tables? Or just the database?

Anyway, that's just my opinion and I don't really want to start an argument (I use exception-based error handling all the time in my normal work, where that level of safety is not really required).
Hopefully, every function either succeeds, or fails, it never partially succeeds. This is usually not the case in the real world, but the closer to it you can get the better, and you can get closer easier using exceptions. I don't like the code snippet you just posted because it is 2 stage construction - the database object exists without being in a valid state. If things like generate indexes are to be somewhere other than the constructor, then a factory function and private constructor would be nice, and placing accessDb in an auto_ptr would be nice, and infact if you do both of these things all your catch has to handle is what you do next - cleanup will be automatic via the databases destructor when the auto_ptr is unwound off the stack.

It's kind of the point. Your code doesn't need to know anything about the internal workings of the database object. It may be written to take advantage of indexes, but it should not have any concept of them being valid or not - that is internal workings of the class and should stay internal, along with its cleanup. RAII.
Quote:Original post by Squirm
It may be written to take advantage of indexes, but it should not have any concept of them being valid or not - that is internal workings of the class and should stay internal, along with its cleanup.


But at some point you have the three steps: create database, create tables, create indexes. Whether you do them all in one function or in separate functions, if one of those steps has a problem, you need to be able to get the system back to a stable state.

The example I gave is kind of contrived, but it shows the general point. If you use exceptions, your error recovery code is far more complex because you lose information on exactly where the error was generated. All you get is some vague notion that it happened inside the try {} block.

This topic is closed to new replies.

Advertisement