Friday, February 11, 2005

C++: Verifying program sanity

The C language provides a macro, called assert, that is used to verify conditions that must be true at any point of the program. These include preconditions, postconditions and invariants, all of which are explained in introductory programming courses. Whenever an assertion is violated, the program is abruptly stopped because there is most likely a bug in its code.

C++, like C, also provides the assert macro in the cassert standard header. However, there is another way to verify program sanity without using it. How? Using exceptions.

The std::logic_error exception is provided with this situation in mind: to verify that a certain condition holds true at a specific point of the program. If it does not, the program logic (hence the name) is incorrect and must be fixed. Using this method has some advantages, but also some disadvantages. Let's see them.

Using an exception, you can provide a descriptive error message of what failed. The assert macro only prints out the assertion triggered, which causes difficulties to decipher what really made the assertion false. Using an exception, you can add more information, such as the value of an specific parameter (in the case of a precondition) to let the developer know what's wrong.

Furthermore, when you catch one of these exceptions (you'd usually do that from your program's main function with a global try/catch block), you can tell the user that something went wrong, where to report the bug, what information to include, etc. In other words: you can decide how the program will end, instead of simply getting an abort trap.

However, there are some drawbacks in this approach too. The first one is that you'll have to manually create some macros to verify an assertion and throw the correct exception. This is to allow you to quickly disable all of them when building production code; otherwise you'd slow down your program quite a bit (it may not matter at all depending on the nature of your program).

But, maybe, the most important problem you can encounter is that you cannot use this method from within destroyers, as explained here. It may sound strange, but a destroyer can rightfully expect some preconditions to be true.

The method you use is up to you: it can be one of these two or a custom one you write (such as global functions used as logic error handlers). But, whichever it is, add assertions to your code.

6 comments:

Anonymous said...

[Originally posted at 2005-02-11 12:20 pm UTC]

I've seen several times exceptions used as method of coding.
I mean, it should be the last resort before a core dump :P, but not a way to manage things wrong. The way Java or Python uses them, seems to add correctness to the code. But the way C++ allow you to play with the whole thing, dunno it can be a good tool despite debugging your code.
What do you think?

Julio M. Merino Vidal said...

[Originally posted at 2005-02-11 12:45 pm UTC]

Well, I'm not sure I understand you (and I don't know anything at all about Python, which makes things a bit worse). However, the few times I've coded in Java, exception handling was much better (I liked specially how it forced you to explicitly manage all the possible exceptions).

In C++ things are weirder, but you can try to mimic Java's style and still get good code. For example, you could restrict which exceptions a function can throw, but I'm not sure yet that's a good idea (mainly because any "unexpected" exception causes a crash).

After all, coding with exceptions is difficult. Personally I tend to like them in some situations... but I've been using std::logic_error recently in a project as described in the post and might end up changing that.

Anyway, could you please elaborate a bit more on your initial comment? (specially the "method of coding" and "manage things wrong" parts?) Thanks.

Anonymous said...

[Originally posted at 2005-02-12 12:24 pm UTC]

First... I'm spanish and my english kinda sux, but I wanna be on topic with your weblog, so I'll try to :D
I've seen cases when the coder just catches the exceptions instead of trying to do sanity checks in the code. I think that's not "the proper way to do things (tm)". And exception should be treated as 'exception', not as a common way to handle usual errors.

Julio M. Merino Vidal said...

[Originally posted at 2005-02-12 03:57 pm UTC]

Oh, true. I've used that scheme sometimes, but then the code becomes difficult to follow and ugly. So I tend to avoid it if possible (some times it is not, because then you have to do other "weird" things to get everything working safely). Anyway, in the case of the original topic, a triggered assertion is really an exception (in fact, a serious error, because it potentially means a bug in the code), so using them seems reasonable.

BTW, do I know you? (It's a pity Livejournal does not allow unregistered users to sign their comments.)

Anonymous said...

[Originally postead at 2005-02-13 03:21 pm UTC]

OK, now I see your point.

Yeah, it's a pitty livejournal has this limitation (well, I bet it works both as anti-spam measure and to recruit new members :D). I sent you a pthreads module for buildtool some time ago. I discovered your blog following links randomly and now it's in my feeds hehehe

Julio M. Merino Vidal said...

[Originally posted at 2005-02-13 04:46 pm UTC]

I think it's most likely to be the "recruit new members" part (after all, spam can still be posted by using the anonymous option). Anyway, you can always add a little note at the end of the post saying who you are, like '-- '.

BTW, I remember who you are ;)