ACCU Home page ACCU Conference Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pinNotes on Exceptions

Overload Journal #28 - Oct 1998 + Programming Topics   Author: Detlef Vollmann

A general remark first: The Harpist seems to assume that at least the main lines of the implementation are known when the interface is defined. My personal working domain is different: when the interface of a general class is defined, little is known about who will implement it and how it will be implemented. The interface must stay stable through several different incarnations of the implementation.

Of course, from this different background follows a different approach to interface specification, which includes exception specifications.

Note 1: Catching exceptions

What is essentially the difference between

catch(std::exception &e)
{
  // do some cleanup
  throw;
}

and

catch(...)
{
  // do some cleanup
  throw;
}

One basic rule of exception handling is: "Never handle an exception you don't know!" So, if you catch a general base class (such as std::exception), the only thing you should normally do is some cleanup and rethrow the exception. You can do exactly this with catch(...).

There is one special case: If you have a parameter, you have an object that you can copy to store the exception for later use. This is a technique that is typically required for destructors that get an exception during stack unwinding. This technique is also required for smuggling an exception through a layer of C-functions, like typical GUI frameworks.

But alas, std:exception does not provide a virtual clone() method, so you can't copy your exception anyway. Indeed the copy-constructor cannot be used due to object slicing. This is one of the reasons why every programmer invents her own exception base class, which in turn should be derived from std::exception.

Now, if you really need to copy an exception you normally don't know what type it is. This could be at the border between a C- and a C++-subsystem. So you have good reason to define your own exception base class as the only one, as the Harpist proposes. But this is generally the only justification to do so.

But why is it not always useful? Because it's just too much effort.

You must define your own exception base class, with a virtual clone(), and then you must duplicate each possible exception of any library class you use into your own exception hierarchy, and then you must remap all the original exceptions into your own ones. I really believe that this turns out as a maintenance nightmare. Not only do you have all exceptions twice in your system, with different names and in different class hierarchies, but you also have to do the duplication again. You'll have to rewrite all your remapping if you change the library you use, as the new library will definitely throw different exceptions from the original one. This type of implementation change happens more often than you would wish. Also, someone else who uses your classes as a library would consequently have to do the same, i.e. he has his own exception hierarchy again.

Now each exception exists three times...

I admit that the concept of an own exception class hierarchy and consequently providing each function with a respective exception specification is nearer to the ideal of implementation hiding. But, implementation hiding was not set up for its own sake, but to provide better maintainability. In my experience, better maintainability is achieved by less implementation hiding in the case of exception specifications.

Note 2: Exception Specifications for Destructors and Containers

It is true that objects whose destructors have no exception specifications (they can throw anything) cannot be held by value in an STL container. However, I would be extremely careful when mixing exceptions and STL containers with the current standard library implementations. The main point of my previous letter about exception specifications and destructors was that you should never hold base classes by value in containers. You should hold them via (smart) pointers. Then you have to destroy your objects yourself, and then you can take care of exceptions from destructors, which you really should do.

Note 3: Class Libraries

Perhaps the Harpist and I are not so far apart as it may seem. He gives very good reasons why the standard library usually provides no exception specifications. His reasoning applies to most of the classes I have had to design this way: they are either templates or framework classes with template methods ("template methods" in the sense of the "Design Patterns" book by Gamma, Helm, Johnson and Vlissides). The original Customer class for which the whole discussion started is just such a class (or at least should be).

While Ulrich Eisenecker would probably propose to design them as templates (see http://home.t-online.de/home/Ulrich.Eisenecker/meta.htm), I prefer the framework concept with template methods, as this gives better flexibility without recompiling. Only the final application that uses your hotel classes should decide, whether the data for the Customer (and everything else) should be taken from a CORBA server, an Excel spreadsheet, or a mainframe RDBMS (to mention just a few possibilities). So, only the designer of the final application knows of the possible exceptions and has to provide means to handle them. The designer of the Customer (and any other general hotel class) can do nothing more than to provide no exception specifications -- even for the destructors.

Overload Journal #28 - Oct 1998 + Programming Topics