
Hello, I've been using exceptions for several years now and while this programming style is very rewarding in general, one particular problem was bugging me all along: how do you make sure the catch site has all the information necessary to process the exception? This problem is trivial only in the case when the throw site has all the information that the catch site needs; but in general that is not the case. A good example that illustrates the problem I'm talking about is a file read function, which needs to be given the file name, so that if a read error occurs, the exception object would have the file name stored in it. The typical solution I've seen, and the one I've been using in the past, is to somehow pass all relevant information to the function that's throwing the exception, so that if it throws it can encode it in the exception object. Clearly, this approach is not ideal; in the file read example, the read function doesn't need a file name, all it needs to attempt the read and to detect an error is a file handle. Besides, some files, such as the standard out, don't have names anyway... I came up with a solution which seems to solve the problem, and I wanted to see if there is sufficient interest in it to be added to boost. Essentially, the idea is to decouple all context information, such as file names, etc., from the "what went wrong" information. The "what went wrong" is indicated exclusively by the type of the exception being thrown (e.g. read_error). Any additional information should be independent of that type, because in different contexts different information may be relevant, for the same type of error. Consider the following throw statement: throw read_error(); With the system I developed, this same error would be reported like this: throw failed<read_error>(); Here, 'failed' is a function template, which returns an unnamed temporary of unspecified type, which derives from the type argument passed to 'failed', and another class called 'info'. Class info is essentially a map of boost::any objects, associated by their type_info. So, an object of class info can store objects of any type, but no more than one instance per type. In the example above, you can catch the exception as read_error & (or any of the read_error base types), or as info &. At the catch site, you simply catch errors (that is, you dispatch on 'what went wrong'), and then probe the exception object for any context information available: catch( read_error & x ) { if( info * xi = dynamic_cast<info *>(&x) ) if( file_name * fn = xi->get<file_name>() ) ...... } How did the file name end up in the info object, if the original exception was thrown simply by 'throw failed<read_error>()'? Since you can catch the exception as info &, you can intercept *any* exception thrown by the 'failed' function template -- not to handle it, but to add relevant context information to it, regardless of what went wrong, like this: void open_and_read_file( char const * n ) { boost::shared_ptr<FILE> f = io::fopen(n,"rb"); try { read_file(f,....); .... } catch( info & xi ) { xi.add( file_name(n) ); throw; } } Of course, in the try block, there may be many different function calls; when we catch info &, we don't care which one of those calls failed: we catch any exception thrown by the 'failed' function template, add the file name (since it is relevant to any error that occured in this context), and then re-throw the original object of unspecified type, as returned by the 'failed' function template. In fact, class info is abstract, which protects against slicing which would occur if the user tried to throw xi (in the example above). I can provide more information about my implementation if needed; it allows context information to be encoded as demonstrated by the example above, and also directly in the throw-expression -- for stuff that happens to be known at the throw site anyway, such as errno, etc. --Emil