cexcept: rationale 0.6.1 (2000-Apr-08-Sat) Adam M. Costello Rationale for some of the design decisions behind version 0.6.* of the cexcept.h interface. See README for copyright information. Why aren't multiple Catch clauses allowed? The purpose of multiple catch clauses in C++ or Java is to allow you to catch multiple exception types. This is natural in a language that supports polymorphism, where you can substitute objects of different types into the same expression and cause different code to be executed. In such a language you can throw any type of object, and the appropriate catch clause will be selected automatically. C does not support polymorphism (except for unions and void pointers). Therefore it is natural in C to have just one exception type, and therefore one Catch clause is sufficient. The application programmer is free to use any type for the exception type, and may use a union to simulate polymorphism (see cexcept-example2.c for an example of this). But for many applications an int, enum, or small struct will be sufficient. But couldn't tricks be played to allow multiple exception types? Yes, but cexcept.h is intended for C programs and C programmers. If the concept of polymorphism were incorporated into the interface, the interface would become less intuitive to many of the people it is designed for. But if an application programmer uses unions to simulate multiple exception types, the resulting code will be perfectly understandable to other C programmers, even if they have no experience with polymorphism. Why can't I say Catch (int e) like in C++ or Java? Again, the interface tries to be intuitive to C programmers. In C, declarations go at the beginnings of blocks. The cexcept syntax is actually more flexible in a way, because any lvalue can go in the parentheses, for example: Catch (p->e) Why can't I jump into or out of a Try clause? There is some hidden setup and cleanup that needs to be done regardless of whether an exception is caught. Jumping into a Try clause would avoid doing the setup, and jumping out of it would avoid doing the cleanup. Why isn't a Finally clause supported? There's no way to implement it. In Java, the main difference between putting code in a finally clause versus putting the code after the try/catch statement is that the finally clause will get executed even if a return or throw occurs within the try clause or catch clause. But in C, a return statement returns immediately. In Java one could make do without a finally clause by deferring any return from the try clause (by storing the return value in a variable), and moving some or all of the catch code outside the try/catch statement, like so: Exception e = null; try { ... } catch (Exception tmp) { e = tmp; } // Do cleanup here. if (e instanceof Foo) { ... } else if (e instanceof Bar) { ... } ... else if (e != null) throw e; A similar technique can be used with cexcept: my_exception_type e = some null value that will not be thrown; Try { ... } Catch (e) { } /* Do cleanup here. */ if (e is not that null value) { ... } Why can't I use Throw without an expression to re-throw an exception? The use of throw without an expression is needed in C++ because it's the only way to re-throw an exception of unknown type: catch (...) { // do some stuff throw; } This syntax is not provided in Java because the fact that all exceptions are instances of Exception eliminates the problem: catch (Exception e) { // do some stuff throw e; } Similarly, with cexcept, the type of exceptions is always known, so an expressionless Throw is not needed. Besides, the syntax would be very difficult, if not impossible, to implement. Why is the Catch expression evaluated even if no exception is caught? We need to warn potential throwers where to store the exception value in the event of an exception. In order to avoid evaluating the expression, we would have to let the thrower store the value in a temporary location, which would then have to be copied into the user-supplied object. Why must the Catch expression have the exact same type that was passed to define_exception_type()? I can assign a double value to an int, so why can't I catch a double exception in an int variable? The type of the object passed to Catch cannot be communicated to the thrower, so the thrower must assume it is storing into an object of the type passed to define_exception_type(). If we had the thrower store into a temporary variable, and the catcher perform a second copy, then the requirement could be relaxed, but the semantics would still not be the same as a direct assignment of the Throw expression into the Catch expression, because two conversions might be performed. Why are the macros spelled Try, Catch, Throw rather than...? We considered many other possibilities: try catch throw TRY CATCH THROW ctry ccatch cthrow c_try c_catch c_throw cex_try cex_catch cex_throw CEX_TRY CEX_CATCH CEX_THROW cexcept_try cexcept_catch cexcept_throw try_until_exception catch_exception throw_exception We wanted names that would be easy to read, easy to type, intuitive, and unlikely to conflict with existing names. Ultimately we settled on Try, Catch, and Throw as the best compromise among all these criteria.