
"Emil Dotchevski" wrote:
} catch(boost::exception& e) { e.dump_everything(); // what happened? }
Yes, but what does dump_everything() look like?
The reason why this is important is that I can't imagine dump_everything being able to format a proper user message. The best it can do is dump stuff in a debug log, for example. Which begs the question, how do you format a proper message for the user? I think that the only way to do this is for the code that formats the message to *know*, for each class of exceptions, what info could be available. You can't parameterize this with a "visitor" interface. Or can you? That was my intention when I asked you for an example: what's in dump_everything?
Would it be possible to use typeid(..).name() together with lexical_cast<> on the values? The formatting is almost irrelevant at this point, raw data are. The end user woudn't see it, only the author.
typeswitch: if (dynamic_cast<This>(e)) .... else if (dynamic_cast<That>(e)) ....
What's wrong with
catch( This & ) { } catch( That & ) { }
This looses the valuable simplicity of catch (boost::exception&). And what if you want to add the same 10 data items for every exception. [ snip visitor-like approach vs individual catch statements ]
My personal opinion is that a list of ordered catch statements is the simplest way to get what you need. And it is directly supported by C++.
Problems: * Coupling. The code containing catch is likely already complex and now it needs to include all handled exceptions, even if they are from lower layer. With the other approach you may create visitor in lower layer and provide it as part of its interface. The visitor would not need to know about higher layer but would work correctly, the upper layer would not depend on lower layer details (definitions of the exceptions). * Isolation of error handling. With sequence of catch you put error handling code into vicinity of normal-path code. The code is spread across many functions. With visitor you can put error handling code into one central location and can completely isolate it from normal-path code. Only visitor definition needs to be shared. * Not dynamic. Behaviour of a hardcoded catch sequence cannot be changed easily. A visitor fares better. Not really earth shaking feature but may come handy. Example: the visitor itself may be payload of the thrown exception. On each catch level it will be invoked and when it decided the error has been solved it will stop propagating the exception up. Say std::bad_alloc that "auto-stops" when enough of memory has been released. Better example: regression test suite. You created and use debug visitor that stops propagating of exceptions up and you may safely throw and test anything from lower layer w/o worry what disaster will it cause somewhere up. * It is shorter: } catch (boost::exception& e) { my_visitor v; v.process(e); } vs. } catch (exception1& e) { handle(e); } catch (exception2& e) { handle(e); } catch (exception3& e) { handle(e); } .... * Safety: the correct ordering of the checking is (should be) automated with a visitor. Finding bug in hand-written catch sequence is very hard. * If you add new exception or change exception hierarchy you need to update only the visitor class, not every catch. /Pavel