Exceptions in Frameworks

Exceptions are a topic near and dear to my heart, mostly because I have some strong opinions about the benefits and disadvantages of exceptions. But this isn’t a blog posting about whether exceptions are good or not. Instead, this is a post about using exceptions when designing a C or C++ framework.
There’s really two different cases to discuss on this topic. One is designing a C framework, and the other is designing a C++ framework.

If you are designing a framework with a straight C interface, you can’t use exceptions at all, for any reason. That’s because the C language doesn’t define exceptions (ignoring floating-point exceptions, which are a slightly different beast). So even if your implementation is in C++ and the C headers are merely a wrapper, you cannot throw exceptions across library boundaries. You can’t guarantee that the caller of your library will be able to do anything useful with the exception because the consumer could be written in any language able to work with C ABIs, like a C application, or a VisualBasic application, etc. In fact, the language consuming your framework may not even have the concept of exceptions to begin with! So when making a C framework, there’s no valid situation where you can throw an exception.

If you are designing a C++ framework, the waters get a bit murky. The C++ specification makes no assurances about the ABI for exceptions. This means that one C++ compiler could define the layout and handling of exceptions differently from another C++ compiler. However, some vendors have tried to alleviate this issue by creating a stable ABI for their exceptions. Unfortunately, the road to hell is paved with good intentions. That stability is a false safety net unless there is only one compiler and one ABI for that particular platform. For instance, if you use Visual Studio to develop your framework, nothing says the consumer won’t be using gcc over MinGW. Or if you use XCode (Clang or gcc) on the Mac, the consumer may be using CodeWarrior.

Taking a more concrete example of differences: on Windows, there are (at least) three different common exception specifications you can run into. The VC++ exception implementation (used for try/catch blocks in C++), the gcc exception implementation (used for try/catch blocks in C++), and the structured exception handling implementation (used for SEH exceptions, __try/__catch). All three of these use entirely different mechanisms to provide roughly equivalent functionality. The way the stack is unwound in all three situations are drastically different, as are the runtime memory layouts of the exception objects themselves. If one mechanism throws an exception, either of the other mechanisms will certainly be unable to do anything useful.

So you cannot rely on exceptions being caught across library boundaries even for C++.

Well, I lied a little bit. You can rely on exceptions in C++ if you’re willing to sacrifice code portability. Some machines have a well-defined exception ABI as a convention, such as Itaniums. Assuming that all compilers involved match that convention, then you can safely cross library boundaries. However, the barrier is still quite large in that your code is now tied to that particular platform simply to handle edge case errors!

If your C++ code uses exceptions internally, the one reasonable approach you can take is to flatten your exceptions into error codes using a try/catch block within your public APIs. For instance:

You may dislike co-opting the return value to pass status information, but you can see how this would be modified to return status as a by reference parameter. The important part is that you’re catching exceptions at library boundaries and converting them into a more framework-friendly mechanism.

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website

Who

Aaron Ballman is a software engineer for GrammaTech. He has almost two decades of experience writing cross-platform frameworks in C/C++, compiler & language design, and software engineering best practices and is currently a voting member of the C (WG14) and C++ (WG21) standards committees.

In case you can't figure it out easily enough, the views expressed here are my personal views and not the views of my employer, my past employers, my future employers, or some random person on the street. Please yell only at me if you disagree with what you read.