Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a generally accepted idiom for indicating C++ code can throw exceptions?

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?

like image 329
David Coufal Avatar asked Aug 11 '09 16:08

David Coufal


1 Answers

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 and destructors. 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.

like image 98
Steve Jessop Avatar answered Sep 29 '22 13:09

Steve Jessop