views:

427

answers:

11

I have seen problems when using C++ code that, unexpectedly to the caller, throws an exception. It's not always possible or practical to read every line of a module that you are using to see if it throws exceptions and if so, what type of exception.

Are there established idioms or "best practices" that exist for dealing with this problem?

I've thought of the following:

(1) In our doxygen documentation, we could add a comment in every function that is expected to throw an exception and it's type(s).
Pluses: Simple.
Minuses: Subject to user error.

(2) We could have an app-wide try/catch(...) for safety.
Pluses: We won't have any more uncaught exceptions.
Minuses: The exception is caught far away from the throw. It's hard to figure out what to do or what went wrong.

(3) Use Exception Specifications
Pluses: This is the language-sanctioned way of dealing with this problem.
Minuses: Refactoring of problem libraries needed for this to be effective. Not enforced at compile-time, so violations turn into run-time problems, which is what I'm trying to avoid!

Any experiences with these methods, or any additional methods that I'm unaware of?

+6  A: 

Frankly, just about any C++ function can throw an exception. You should not worry too much about documenting this, but instead make your code exception safe, by using idioms such as RAII.

anon
Though I do follow RAII, you can never create a code that is exception proof. There are always risks of bugs in third party libraries, OS API, or even changes in project details that are not accounted for.
Leahn Novash
I meant safe, of course - edited answer.
anon
If you could eliminate all exceptions... You would be rich... and soon!
Axxmasterr
I like exceptions! A million times better than checking return values!
anon
eh, at most 2.3 times better. Exceptions are a pain. Just because they're less painful than return values doesn't make them a *good* solution. :p
jalf
I does not agree, of course you have to care. You should catch exceptions as early as possible. when exceptions are thrown by yourself, they are part of your interface so they should be documented.
fnieto
+2  A: 

I use both 1 and 2.

About 1: You cannot avoid or prevent user error. You can beat the developer that did not write the doxygen properly to a pulp, if you know him. But you cannot avoid or prevent user error, so drop the paranoia. If the user err'ed, he did it, not you.

About 2: C# has built-in a way to catch unhandled exceptions. So it is not a 'bad thing', though I agree it smells. Sometimes, it is just best to crash than to run inconsistently, but I made a practice to log any unhandled exceptions and THEN crash. That allows people to send me the log so I can check the stack trace and track down the problem. This way, after each correction, less and less crashes happen.

Leahn Novash
I think you mean C++, not C#.
anon
@Neil: No, I really meant C#, although the question is about C++. I never really worked with C++, but I exemplified C# because it is a newer language which reflects the newest concepts so it may mean something that it is acceptable to do it in a newer language.
Leahn Novash
@Leahn Well, it may suprise you to know that the old-timer C++ also has a way to catch unhandled exceptions :-)
anon
Or it may surprise you that C# actualy reflects a lot of truly ancient concepts in some ways. The way you deal with exception handling or resource cleanup in C# is pretty much what C++ programmers did 15 years ago (and would no longer *dream* of doing) ;)
jalf
@Jalf: I am accepting suggestions. If they no longer dream of doing it that way, how do they do it? I mean, I find it to be a pretty good idea to catch all unhandled exceptions and log them before crashing the program. Trying to recover IS a bad idea, but logging for later debugging isn't. At least I think so. But you seem to imply that it is, so how do you deal with unhandled exceptions?
Leahn Novash
A: 

You should always expect exceptions and handle them. Unfortunately there's no automated way for the computer to do due diligence for you.

Jay
+6  A: 

Short answer to the title question - the idiom to indicate a function can throw is not to document it "this function doesn't throw". That is, everything can throw by default.

C++ is not Java, and doesn't have compiler-checked exceptions. There is nothing in C++ which will allow the compiler to tell you that your code claims it won't throw, but calls something which might. So you can't completely avoid this being a runtime problem. Static analysis tools might help, not sure.

If you only care about MSVC, you could consider using an empty exception specification or __declspec(nothrow) on functions which don't throw, and throw(...) on functions which do. This will not result in inefficient code, because MSVC doesn't emit the code to check that functions declared nothrow actually don't throw. GCC can do the same with -fno-enforce-eh-specs, check your compiler documentation. Everything will then be automatically documented too.

Option 2, the app-wide try-catch isn't really "for safety", it's just because you think you can do something more useful with the exception (like print something out and exit cleanly) than just let the C++ runtime call terminate. If you're writing code on the assumption that something won't throw, and it actually does, then you may have gone undefined before either one of them actually happens, for example if a destructor makes a false assumption of consistent state.

I'd normally do a variant of (1): for each function document what exception guarantee it offers - nothrow, strong, weak, or none. The last is a bug. The first is prized but rare, and with good coding is only strictly necessary for swap functions. Yes, it's subject to user error, but any means of C++ coding with exceptions is subject to user error. Then on top of that, also do (2) and/or (3) if it helps you enforce (1).

Symbian has a pre-standard dialect of C++, with a mechanism called "leave" which is like exceptions in some respects. The convention in Symbian is that any function which might leave must be named with an L at the end: CreateL, ConnectL, etc. On average this reduces user error, because you can see more easily whether you're calling something which might leave. As you might expect, the same people hate it who hate apps Hungarian notation, and if almost all functions leave it ceases to be useful. And as you might expect, if you do write a function which leaves without an L in the name, it can be good long while in the debugger before you figure out the problem, because your assumptions point you away from the actual bug.

Steve Jessop
+1  A: 

C++ defines a throw specification. See this question. It has been my experience in the past that microsoft compilers ignore the C++ throw spec. The whole notion of "checked exceptions" is controversial, especially in the Java realm. Many developers consider it a failed experiment.

Todd Stout
+1  A: 

Use doxygen documentation to describe your methods. When you use them, you will need to check this documentation to see what their parameters are, and what exceptions they throw.

Write unit tests to exercise your code in the cases exceptions are thrown.

fnieto
+2  A: 

Documentation seems to be the only reasonable method that I know of.

Regarding exception specifications, here's an old (2002) article by Herb Sutter on the subject http://www.ddj.com/cpp/184401544 It discusses why this feature of the language doesn't give us compile-time safety and ends up with the conclusion:

So here’s what seems to be the best advice we as a community have learned as of today:

Moral #1: Never write an exception specification.

Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.

TheUndeadFish
A: 

No.
The only common thing is to indicate you dont throw anything.
And then you should also manually make sure that no exception can actually escape your method/function. Note: If exceptions do escape the method the application will be terminated.

#include <stdexcept>

class MyException: public std::exception
{
    public:
    ~MyException() throw() {}
     char* what() const throw()
     {
        try
        {
            // Do Stuff that may throw
            return "HI";
        }
        // Exceptions must not escape this method
        // So catch everything.
        catch(...)
        {
           return "Bad Stuff HAppening Cant Make Exception Message.";
        }
     }
};
Martin York
+5  A: 

The idiomatic way to solve the problem is not to indicate that your code can throw exceptions, but to implement exception safety in your objects. The standard defines several exception guarantees objects should implement:

  • No-throw guarantee: The function will never throw an exception
  • Strong exception safety guarantee: If an exception is thrown, the object will be left in its initial state.
  • Basic exception safety guarantee: If an exception is thrown, the object be left in a valid state.

And of course, the standard documents the level of exception safety for every standard library class.

That's really the way to deal with exceptions in C++. Rather than marking which code can or can not throw exceptions, use RAII to ensure your objects get cleaned up, and put some thought into implementing the appropriate level of exception safety in your RAII objects, so they're able to survive without special handling if an exception is thrown.

Exceptions only really cause problems if they allow your objects to be left in an invalid state. That should never happen. Your objects should always implement at least the basic guarantee. (and implementing a container class which provides the proper level of exception safety is an enlightening C++ exercise ;))

As for documentation, when you're able to determine for certain which exceptions a function may throw, by all means feel free to document it. But in general, when nothing else is specified, it is assumed that a function may throw. The empty throw specfication is sometimes used to document when a function never throws. If it's not there, assume that the function may throw.

jalf
"Exceptions only really cause problems if they allow your objects to be left in an invalid state." > No, they are also a problem when the caller does not know what a method throws and does not handle every exception properly.
fnieto
Depends on the kind of problem we're talking about. I'm talking about the most fundamental problem: Your program is left in an invalid or indeterminate state. That won't happen if your objects are individually exception-safe. And that means you can simply handle the exception whereever it makes sense, without having to worry about correctness.
jalf
A: 

There is a way to specify which exceptions a function can throw in C++. An example taken from the Stroustrup book is

void f(int a) throw (x2, x3);

which specifies that f can only throw exceptions of type x2 or x3, or derived types. Using this syntax you can easily look at the functions declaration to see what exceptions it is legally allowed to throw, and code accordingly.

bstamour
No, you can't.The syntax allows you to _document_ what you _think_ it will throw, but this is not enforced by the compiler. See http://stackoverflow.com/questions/88573/should-i-use-an-exception-specifier-in-cI feel this is actually worse than not having such throw decoration at all. Now readers of the code will think that is a guarantee, when it essentially is only a comment.:(
akauppi
+1  A: 
  1. Document what level of exception safety a function guarantees. As Steve Jessop pointed in his answer, there is number of levels. Ideally if they are documented for interfaces all, or at least necessary minimum: a) never throws or b) may throw

    Read about Abrahams exception safety guarantees explained by Herb Sutter.

  2. I strongly doubt it would be practical in a big codebase. Regarding far away from the throw and it's hard to figure out what to do concerns, a good rule is to catch only in places where you want deal with exception, otherwise let it bubble up. It is not a good idea to catch and extinguish exceptions as soon as they appear, just because...they are exceptions, so you feel you have to do something with it. In complex systems, it's good to have a logging mechanism, so it's easier to trace a problem.

  3. Don't do it. Read Herb Sutter's A Pragmatic Look at Exception Specifications and related articles.

mloskot