From time to time I notice some code that I have had for many years, and it makes me nervous. I don't have a specific problem, but I also don't remember enough why I adopted this model, and some of its aspects seem to fit the anti-pattern. This recently happened to me with WRT, as some of my codes use exceptions.
Anxious case includes cases when I get an exception "by reference", treating it in the same way as I will handle a function parameter. One reason for this is that I can have an inheritance hierarchy of exception classes and specify a more general or more accurate catch type depending on the application. For example, I could determine ...
class widget_error {}; class widget_error_all_wibbly : public widget_error {}; class widget_error_all_wobbly : public widget_error {}; void wibbly_widget () { throw widget_error_all_wibbly (); } void wobbly_widget () { throw widget_error_all_wobbly (); } void call_unknown_widget (void (*p_widget) ()) { try { p_widget (); } catch (const widget_error &p_exception) {
Now this bothers me because I noticed that the class instance was constructed (as part of the throw) inside the function, but was referenced (via the "catch-clause" parameter of the p_Exception parameter) after this function exited. Usually this is an anti-pattern - a link or pointer to a local variable or temporary one created inside the function, but passed when the function exits, is usually a dangling reference / pointer, since the local variable / temporary is destroyed and the memory is freed when the function exits.
Some quick tests suggest that the throw above is probably OK - the instance created in the throw clause does not crash when the function completes, but it crash when the final catch-clause completes - if the catch block does not throw an exception, in which case the next the catch block does this job.
My remaining nervousness is that a test run in one or two compilers is not proof of what the standard says, and since my experience is that what I consider common sense is often different from what the language guarantees.
So - is this exception handling pattern (catching them using a reference type) safe? Or should I do something else, for example ...
- Catching (and explicitly deleting) pointers to instances allocated by the heap, instead of references to something that looks (when thrown) is very similar to temporary?
- Using a smart pointer class?
- Using a catch-by-value catch clause and accepting that I cannot catch any class of exceptions from a hierarchy with a single catch clause?
- Something I didn't think about?
Steve314
source share