Reputation: 73071
Here's a not-too-uncommon problem in C++ programming:
Programmer A writes a C++ library (we'll call it Foo), which includes a nice public API that programmer B's program calls methods on, in order to implement part of his program. When working with the Foo API, programmer B looks at the documentation and the header file, but not very much at the .cpp files, mainly because the whole point of having a public API is to hide implementation details. Programmer B gets his program to work and initially everything looks good.
One day the program is doing its thing, when something unusual happens inside the Foo codebase that causes a Foo method to throw an exception. Programmer B was unaware that that type of exception might be thrown by that Foo method, and so the exception either doesn't get caught (and the program crashes), or the exception gets caught by a very general exception handler (e.g. catch(...)) and doesn't get handled adequately (and so the program does something non-optimal, like putting up a dialog with a cryptic error message).
Since I'd like my C++ software to be robust, my question is, what is a good way to avoid a scenario like the above? Requiring programmer B to read through all of the .cpp files in the Foo codebase (and all .cpp files of any C++ codebase that Foo calls, and so on all the way down) looking for throw statements doesn't seem like an adequate solution, since it violates the spirit of encapsulation, and even if it was done there would be no guarantee that more throw statements might not be added in the future. Requiring programmer A to document all possible thrown exceptions seems like a good idea, but in practice programmer A is very likely to miss some of them, especially if his code in turn calls into yet another library that may or may not document 100% of its possible exceptions.
In Java there is some support for automated exception-handling checks; e.g. you can annotate your Java methods to declare what exceptions they might throw, and if the calling code doesn't explicitly handle all of those exceptions (or itself declare that it might throw them), the compile fails and the programmer is forced to fix his program -- thereby preventing the problem scenario described above. C++, on the other hand, has no such compile-time enforcement, meaning that verifying that a C++ program handles all exceptions properly seems to be left entirely up to the programmer and QA team. But in a non-trivial program, actually performing such a verification seems impractical; is there some technique (automated or non-automated) by which C++ programmers can gain confidence that their programs handle all possibly-thrown exceptions in a considered fashion? (Even just a program that would list all of the exceptions that can be thrown from various calls would be useful, I think -- at least then there'd be a list of known possibilities to check the exception-handling code against)
Upvotes: 0
Views: 990
Reputation: 266
At first take this question seems to present quite a dilemma. Until you discover the following insight.
Where exceptions are concerned, the fact that an exception is thrown, where it is thrown, and where it is caught captures more than 90% of the importance of a thrown exception's information. The type of the exception rarely is that important.
It turns out that we seldom create code that can troubleshoot a problem and come up with a remedial solution. Almost always the only useful thing code can do is report that the command failed and its error message.
If the exceptions used in a library are derived from std::exception and support a useful call to what(), then when you report/log that message, you've moved to capturing 95% of the useful information. If the what() message contains __ FILE __ , __ LINE __ , and __ func __ information, then you are at 98% of the important information provided by most thrown exceptions.
Even if a library isn't carefully documented with each exception type that can be thrown from each particular function call, a decent library will provide the base type used for all the exceptions it with throw (often it will be std::exception or something derived from it). This exception base class will have some way of getting the associated error message (such as the what() call).
But even if you are reduced to using catch (...) as your last line of defense, your error message can still indicate that a command failed and what command it was. And you have the confidence that you are handling every exception that might be thrown.
Upvotes: 2