Search code examples
design-patternsc++11code-duplication

Remove duplication in C++11 try catch pattern


This is one of those awkward questions where it is hard to think of anything sensible to put on the title. Maybe I can retitle retrospectively...

I'm dealing with the following code:

    static FinalClass* final_class( PyObject* o ) {/*...*/}

    template< F0 f >
    static PyObject* handler( PyObject* o, PyObject* )
    {
        try
        {
            Object result{  (final_class(o) ->* f)()  };
            return new_reference_to( result.ptr() );
        }
        catch( Exception & )
        {
            DBG_LINE( "! ! !  Exception Python calling new-style-class handler ! ! !" );
            return 0;
        }
    }

    template< F1 f >
    static PyObject* handler( PyObject* o, PyObject* _a )
    {
        try
        {
            Object result{  (final_class(o) ->* f)(Tuple{_a})  };
            return new_reference_to( result.ptr() );
        }
        catch( Exception & )
        {
            DBG_LINE( "! ! !  Exception Python calling new-style-class handler ! ! !" );
            return 0;
        }
    }

    template< F2 f >
    static PyObject* handler( PyObject* o, PyObject* _a, PyObject* _k )
    {
        try
        {
            Object result{  (final_class(o) ->* f)(Tuple{_a}, Dict{_k})  };
            return new_reference_to( result.ptr() );
        }
        catch( Exception & )
        {
            DBG_LINE( "! ! !  Exception Python calling new-style-class handler ! ! !" );
            return 0;
        }
    }

As can be seen, the same pattern occurs in each of the three cases.

Also in the third case, the function signature has an extra parameter, although experimentation suggests I can add a dummy parameter to the first two cases if need be.

I can't see any possible way of abstracting the mechanism short of using #define-s.

Is there anything to be done?


Solution

  • The only thing that comes to mind is something like this:

    template<typename functor_type, typename default_value_functor>
    auto execute_and_catch(functor_type &&functor,
                           default_value_functor &&default_value)
        -> decltype(functor())
    {
         try
         {
             return functor();
         }
         catch( Exception & )
         {
                DBG_LINE( "! ! !  Exception Python calling new-style-class handler ! ! !" );
                return default_value();
         }
    }
    

    Then, one example of how you'd use that:

    template< F0 f >
    static PyObject* handler( PyObject* o, PyObject* )
    {
        execute_and_catch([&]
            {
                Object result{  (final_class(o) ->* f)()  };
                return new_reference_to( result.ptr() );
            },
            []
            {
                return 0;
            });
    }
    

    And the other instances would convert similarly.

    This'll still lead to a lot of template bloat, of course; but at least the source code will be tight, and compact.