Remove duplication in C++11 try catch pattern

99 views Asked by At

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?

2

There are 2 answers

0
Sam Varshavchik On BEST ANSWER

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.

1
Remy Lebeau On

Maybe you can do something with variadic templates, provided that Tuple has a constructor that can take a PyObject* as input, eg:

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

PyObject* h = handler(&someF0method, o);

PyObject* h = handler(&someF1method, o, a);

PyObject* h = handler(&someF2method, o, a, b);