Search code examples
c++refactoringcode-readability

How to organize dozens of similar try-catch blocks?


I have external interface, let's say with 20 similar methods. Each can throw anything.

So, I have 20 methods looks like:

void Call3Wrapper(int& important_parameter)
{
try 
{
   external_namespace::IExternal::Call3(important_parameter);
}
catch(external_namespace::Exception& ex)
{
   LogIt(ex, "Call3");
   throw mynamespace::MyException(ex);
}
catch(std::exception& ex)
{
   LogIt(ex, "Call3");
   throw mynamespace::MyException(ex);
}
catch(...)
{
   LogIt("Unresolved exception", "Call3");
   throw mynamespace::MyException("Unresolved exception");
}
}

And same for each Call1-Call20 (calls with different parameters).

Such as:

  • void Call7Wrapper(std::string& important_parameter)
  • void Call12Wrapper(bool& important_parameter, double& important_parameter2)

...and etc.

I have nice function LogIt for all those cases, and our own type MyException, that handles all those cases also.

And all those 20 (40, 60) function's bodies looks ugly, because 90% of code is exactly similar try-catch checks. And a developer will die when he will need to fix anything same in all of them...

Does there exists a method / practice how to organize it less ugly way? Something like lock-unlock-idiom in constructors&destructors, but for try-catch?

I definitely don't want to use #define CALLS_CATCH_BLOCK(call_name) ...


Solution

  • Maybe you can templatize the wrapper and call it via function objects:

    template<typename CALLABLE_T>
    void CallWrapper(CALLABLE_T callable, const char* name)
    {
    try 
    {
       callable();
    }
    catch(external_namespace::Exception& ex)
    {
       LogIt(ex, name);
       throw mynamespace::MyException(ex);
    }
    catch(std::exception& ex)
    {
       LogIt(ex, name);
       throw mynamespace::MyException(ex);
    }
    catch(...)
    {
       LogIt("Unresolved exception", name);
       throw mynamespace::MyException("Unresolved exception");
    }
    }
    

    Then you can use std::bind to create callable object with parameters, for example:

    CallWrapper(std::bind(&external_namespace::IExternal::Call3,
                          std::ref(important_parameter)),
                "Call3");
    

    (or boost::bind/boost::ref for older compilers)

    Note that the compiler will still generate an instantiation of the template for each different CALLABLE_T type, but you do not need to repeat the code.

    Also note that this way you can also call and handle member functions of classes etc.


    If this is to be used by an API interface, you might still need to create the separate methods, but call the templated wrapper internally instead of repeating the exception handling code.