I have a pattern that repeats for several member functions that looks like this:
int myClass::abstract_one(int sig1)
{
try {
return _original->abstract_one(sig1);
} catch (std::exception& err) {
handleException(err);
} catch (...) {
handleException();
}
}
bool myClass::abstract_two(int sig2)
{
try {
return _original->abstract_two(sig2);
} catch (std::exception& err) {
handleException(err);
} catch (...) {
handleException();
}
}
[...]
int myClass::abstract_n(bool sig3a, short sig3b)
{
try {
return _original->abstract_n(sig3a, sig3b);
} catch (std::exception& err) {
handleException(err);
} catch (...) {
handleException();
}
}
Where abstract one through n are methods of a pure virtual abstract interface for which myClass
and _original
are concrete implementations.
I don't like that this pattern repeats in the code and would like to find a way to eliminate the repeating try
/ catch
pattern and code as a single abstraction, but I can't think of a good way to do this in C++ without macros. I would think that there is a way with templates to do this better.
Please suggest a clean way to refactor this code to abstract out the repeated pattern.
My answer is: do nothing at all. The first code example as shown is fine. So what is there's repetition? It is plain and clear, and does what it looks like it does. It can be understood with no extra mental burdens past the code seen and general knowledge of C++.
Consider your motive for asking this question.
Where I'm coming from is past projects where code had to be examined by others - not PhD Comp Sci experts, but federal inspectors, mechanical engineers, self-taught programmers, scientists, etc. Smart people, all of them (or most of them), but only an off-the-deep-end chrome dome PhD, or a younger programmer out to impress everyone with their hi IQ, would appreciate the clever "solutions" to this question. For the places I've been, nothing beats plain clear code that does what it says, with no mental burden of having to keep in mind the meaning of dozens of classes, macros, etc. and recognizing "design pattern" understood properly only be experienced software engineers.
Increasingly, I find C++ (and Java and C#) code becoming more sophisticated in a coding sense, but needing to be understood by non-experts in C++.
Of course YMMV, depending on the intended audience and source of potential future maintenence programmers. Sometimes clever coding is necessary to achieve certain goals. Is your case an example?
I don't have an answer except to suggest that you might be better off avoiding exception handling altogether and relying instead on Smart Pointers and Boost Scope Exit to do all your resource clean up. That way you won't have to catch exceptions unless you can do something about them, which is rarely the case. Then you can do all exception handling in one centralized place higher up the call chain for error reporting and such.
As a variant on Alexander Gessler's solution you can omit some of the braces that make this implementation a little long. It does exactly the same thing, just with a little less { } verbage.
Use boost::function and boost::bind. Works with any function signature so long as the return type matches;
I asked a very similar conceptual question, see Is re-throwing an exception legal in a nested 'try'?.
Basically, you can move the various exception handlers to a separate function by catching all exceptions, calling the handler and rethrowing the active exception.
It scales well with more different exception kinds to differenciate. You can pack the first
try .. catch(...)
into macros if you like to:One option, if there are a limited number of function arities, would be to use a function template:
This assumes that
handleException
is some non-member function, but it is easy to modify it if it is a member function. You need to decide whatcall_and_handle
returns if an exception is handled; I have it returning an initializedReturnT
as a placeholder.This reduces your member functions to:
You would need a separate function template for calling functions that have one parameter, two parameters, etc.
If you have functions that have an unwieldy number of parameters and you were really desperate, you could use a macro (I wouldn't really recommend this, though):
Which can be used as:
As an aside, if you
catch (...)
and do not rethrow the caught exception, you should in most cases terminate the program.