The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Exception Strategies?

I've always found coming up with an Exception Strategy in C++ to be somewhat difficult.  To me the issues are what do the exceptions signify, etc.

I've seen people use Asserts for errors that should be caught during debug and exceptions for everything else.  I've never really liked this approach, but what approaches has worked for you guys in the past and why?

Sorry if that seems like an awfully open ended question :)
curious.
Sunday, August 05, 2007
 
 
There is a very clear distinction between asserts and exceptions. Asserts represent pre-conditions, post-conditions or invariants. These are a set of rules, or contract, who's violation is a bug (see Design by Contract).

As their name implies, Exceptions are used to handle exceptional conditions. This are adverse situations that can happen at run-time. For example, out of memory conditions, network failure, etc. A network failure cannot be considered a bug in the software, but it is acceptable that the software fails in a reasonable way when it happens.

I think the biggest problems people have with exceptions are:
1. Where to catch them - exceptions should be caught at the lowest level that has sufficient context to handle them.
2. What to do with them - fix the problem and try again? gracefully terminate? something else?
Dan Shappir Send private email
Sunday, August 05, 2007
 
 
I have two reasons why I don't like exceptions.

1. The program just stops with an error, you get no context

2. Taking out error handling in your final product isn't exactly the best approach to take.

I understand that you can write your own assert macro that throws up a dialogue box, et al, but that seems like a kludge IMO and it still doesn't get around issue #2.

Has anyone handled these types of errors without using ssserts, and if so, what were the general strategies that you employed?
curious.
Sunday, August 05, 2007
 
 
> I have two reasons why I don't like exceptions.

I think you mean "assertions".
JW
Sunday, August 05, 2007
 
 
curious,

read what Dan wrote. Asserts should be used to catch program bugs. Thus you leave the bug handling out of the final product, not the error handling -- assuming a thorough testing to ensure as best as possible that no more bugs are left that could fire an assert.

Leaving the asserts out, you basically have two strategies:

1) Don't test for the conditions at all. Since they are bugs, they are of the "This should never happen" type...

2) Handle them with the error handling infrastructure you already use for your normal error handling.
Secure
Monday, August 06, 2007
 
 
Thank you JW, that's a case of my fingers going faster than my brain :)


Secure, I'm not sure I follow you.  If you're handling the errors through some other mechanism besides asserts, which is what you're advocating, why use asserts in the first place?  You can use said mechanism to alert the developers during testing to the problem, and I imagine it'd be more informative than simply having windows throw up a dialogue box regarding an assertion failure.


I've never liked this approach, which is why I was asking others what alternative approaches they've used.
curious.
Monday, August 06, 2007
 
 
curious,

your approach is perfectly fine. However, since asserts test for bugs, you (in theory) don't need these tests in the final product. Using asserts, define the right macro and they are gone.

It is a question of how paranoid you are and of how robust you want to have your code. And using e.g. exceptions instead of asserts, you may find that even the smallest getters and setters suddenly can throw exceptions, because you want to test the integrity of the given object in them, with additional impacts on performance, testing for complex conditions "that should never happen" in the final release.
Secure
Monday, August 06, 2007
 
 
Assertions in C/C++ are best utilized for integration testing - the tying together of disparate bits of code.  If one uses an agile approach, the need for assertions should largely be replaced through unit tests and continuous integration.  A lot of C/C++ libraries come with assertions, though I would like to see this practice replaced with shipping with appropriate test stubs (which give vastly more ability to excercise code).

My view on exceptions is that they should be used when it is appropriate for the code to curl up and die - gracefully die, but dead nonetheless.  Although there may be some exceptions to my rule, it is rare that it is possible to make things right (i.e. recover) after an exception has occurred.
Wayne M.
Monday, August 06, 2007
 
 
"...assertions should largely be replaced through unit tests..."

Interesting.  My take is pretty much the opposite.  I'd prefer to move the logic contained in the unit tests into assertions, and just use the test harness to initialize testing scenarios and then pump data into them.

This allows the assertions to act as resident documentation to make various assumptions explicit and to make the semantics of various blocks of code clearer. Assertions also have more privileged access to the class internals than unit tests should have, and so you don't have to start making a lot of stuff public just to be able to properly test it.  Assertions also allow a finer grained kind of testing; for example, you can test for individual loop variants and invariants, which you can't do with unit tests easily.
Bill Send private email
Monday, August 06, 2007
 
 
An assertion is used when you try to get the number of bytes free on the internet - you're applying GetBytesFree() to an invalid target.

An exception is used when you try to get the number of bytes free on a hard drive and a read error means you can't get an answer - but you expect this to happen in the field, and thus have to be able to deal with it.

Also, artificial choices between unit tests and assertions are kinda silly. Neither is a substitute for the other, and both help ensure that your code coverage is greater than if you restrict yourself to using only one tool. We all know about the church of the holy unit test, but really - it doesn't matetr how shiny your new hammer is, there are still problems that aren't nails.

Monday, August 06, 2007
 
 
assert()s are great places to test for obvious errors you're likely to make.  Stuff that should kill the app when it's doing something obviously stupid.  Just make sure you ping all your code during testing!

For example: assert(this);

Will tell you if you're trying to invoke a method on a null object pointer.  Comes in handy.  Takes the debug time for one of these bugs down to nothing.

They also work as good documentation for preconditions.
E.g.:
void foo (char * p) {
  assert (p && *p);
}

  Says that p has to be a string of nonzero length (and can't be null).  Even basic testing code fails immediately and people know more about how to use your APIs.  asserts() on parameters as the first few lines of code are things people pick up on while skimming your code.

  Use more complex ones to document & enforce consistency constraints on your parameters & data structures.  If you want to document it a bit, use && "Message":

assert ((dst != 0) && "Must use valid destination address")

Logically it'll work fine, and anyone who hits the message will likely get the entire condition in the error message.  Or at least when they bother looking up the line that failed.


C++ with Exceptions is wonderful due to MI,  stack rollback, & the destructor chain.

Two great things about them:

 1. You can define different layers of Exception Firewalls.  Tag (e.g. multiply inherit from exception base classes) exceptions with a semantic.  For example, a per-operation exception means the op failed but the app is fine.  Report it to the user, or beep, and keep going.  Fatal ones mean the app's going to die soon.  Minor ones you can straight up ignore.  For example, background tasks that may fail can do so quietly, invoking the same code as your primary app -- just ignore the exceptions it throws.

  2. In the stack unwind you can do some nice cleanup. Smart pointers auto-delete any temporary objects, the copy-mutate-swap technique keeps your containers consistent (admittedly of limited use on the big containers, but you can add atomic semantics to your own),  and you can take care of any mutexes or other bound context you need.  Hell even simple stuff like indent level in your debug-print stream can be cleaned up well.
  Just use local objects with d'tors that do the right thing, and it's fairly automatic.

Use both techniques for different things.
Lally Singh Send private email
Tuesday, August 07, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz