Pycxx Pyinvoke

There are three calls towards the bottom of cxx_extensions.cxx that invoke a Python function.

//--------------------------------------------------------------------------------
//
//    Method call handlers for
//        PythonExtensionBase
//        ExtensionModuleBase
//
//--------------------------------------------------------------------------------
// Note: Python calls noargs as varargs buts args==NULL
extern "C" PyObject *method_noargs_call_handler( PyObject *_self_and_name_tuple, PyObject * )
{
    try
    {
        Tuple self_and_name_tuple( _self_and_name_tuple );

        PyObject *self_in_cobject = self_and_name_tuple[0].ptr();
        void *self_as_void = PyCapsule_GetPointer( self_in_cobject, NULL );
        if( self_as_void == NULL )
            return NULL;

        ExtensionModuleBase *self = static_cast<ExtensionModuleBase *>( self_as_void );

        Object result( self->invoke_method_noargs( PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ) ) );

        return new_reference_to( result.ptr() );
    }
    catch( Exception & )
    {
        return 0;
    }
}

extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args )
{
    try
    {
        Tuple self_and_name_tuple( _self_and_name_tuple );

        PyObject *self_in_cobject = self_and_name_tuple[0].ptr();
        void *self_as_void = PyCapsule_GetPointer( self_in_cobject, NULL );
        if( self_as_void == NULL )
            return NULL;

        ExtensionModuleBase *self = static_cast<ExtensionModuleBase *>( self_as_void );
        Tuple args( _args );
        Object result
                (
                self->invoke_method_varargs
                    (
                    PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ),
                    args
                    )
                );

        return new_reference_to( result.ptr() );
    }
    catch( Exception & )
    {
        return 0;
    }
}

extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords )
{
    try
    {
        Tuple self_and_name_tuple( _self_and_name_tuple );

        PyObject *self_in_cobject = self_and_name_tuple[0].ptr();
        void *self_as_void = PyCapsule_GetPointer( self_in_cobject, NULL );
        if( self_as_void == NULL )
            return NULL;

        ExtensionModuleBase *self = static_cast<ExtensionModuleBase *>( self_as_void );

        Tuple args( _args );

        if( _keywords == NULL )
        {
            Dict keywords;    // pass an empty dict

            Object result
                (
                self->invoke_method_keyword
                    (
                    PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ),
                    args,
                    keywords
                    )
                );

            return new_reference_to( result.ptr() );
        }
        else
        {
            Dict keywords( _keywords ); // make dict

            Object result
                    (
                    self->invoke_method_keyword
                        (
                        PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ),
                        args,
                        keywords
                        )
                    );

            return new_reference_to( result.ptr() );
        }
    }
    catch( Exception & )
    {
        return 0;
    }
}

The bottom one can be tidied up straight away:

//        if( _keywords == NULL )
//        {
//            Dict keywords;    // pass an empty dict

            Object result
                (
                self->invoke_method_keyword
                    (
                    PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ),
                    args,
                     _keywords ? Dict{ _keywords } : Dict { } // was: keywords
                    )
                );

            return new_reference_to( result.ptr() );
//        }
//        else
//        {
//            Dict keywords( _keywords ); // make dict
//
//            Object result
//                    (
//                    self->invoke_method_keyword
//                        (
//                        PyCapsule_GetPointer( self_and_name_tuple[1].ptr(), NULL ),
//                        args,
//                        keywords
//                        )
//                    );
//
//            return new_reference_to( result.ptr() );
//        }

http://www.informit.com/articles/article.aspx?p=1852519

Furthermore, a lot of code is duplicated between these three functions. This duplication can be removed:

extern "C" PyObject* omni_call_handler(
                            uint16_t flag,
                            PyObject* _self_and_name_tuple,
                            PyObject* _args,
                            PyObject* _keywords
                            )
{
    try
    {
        Tuple self_and_name_tuple( _self_and_name_tuple );

        PyObject* self_in_c_object = self_and_name_tuple[0].ptr();
        PyObject* name_in_c_object = self_and_name_tuple[1].ptr();

        void* self_as_void = PyCapsule_GetPointer( self_in_c_object, nullptr );
        void* name_as_void = PyCapsule_GetPointer( name_in_c_object, nullptr );

        if( self_as_void == nullptr  ||  name_as_void == nullptr )
            return nullptr;

        ExtensionModuleBase* self = static_cast<ExtensionModuleBase *>( self_as_void );

        switch( flag )
        {
            case METH_NOARGS:
            {
                Object result(
                              self->invoke_method_noargs(
                                                         name_as_void ) );
                return new_reference_to( result.ptr() );
            }

            case METH_VARARGS:
            {
                Tuple args( _args );
                Object result(
                              self->invoke_method_varargs(
                                                          name_as_void,
                                                          args
                                                          ) );
                return new_reference_to( result.ptr() );
            }

            case METH_KEYWORDS:
            {
                Tuple args( _args );
                Object result(
                              self->invoke_method_keyword(
                                                          name_as_void,
                                                          args,
                                                          _keywords ? Dict{ _keywords } : Dict { } // was: keywords
                                                          ) );
                return new_reference_to( result.ptr() );
            }
        }

        return 0;
    }

    catch( Exception & )
    {
        return 0;
    }
}

// Note: Python calls noargs as varargs buts args==NULL
extern "C" PyObject *method_noargs_call_handler( PyObject *_self_and_name_tuple, PyObject * )
{
    return omni_call_handler( METH_NOARGS, _self_and_name_tuple, nullptr, nullptr );
}
extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args )
{
    return omni_call_handler( METH_VARARGS, _self_and_name_tuple, _args, nullptr );
}
extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords )
{
    return omni_call_handler( METH_KEYWORDS, _self_and_name_tuple, _args, _keywords );
}

While it would be possible to compacted even further and avoid multiple returns( http://stackoverflow.com/questions/26265966/initialise-variable-in-various-switch-cases ), that would introduce nontrivial syntax for very little benefit.


There appears to be a different mechanism for achieving the same thing in ExtensionType.hxx:

#define PYCXX_NOARGS_METHOD_NAME( NAME ) _callNoArgsMethod__##NAME
#define PYCXX_VARARGS_METHOD_NAME( NAME ) _callVarArgsMethod__##NAME
#define PYCXX_KEYWORDS_METHOD_NAME( NAME ) _callKeywordsMethod__##NAME

#define PYCXX_NOARGS_METHOD_DECL( CLS, NAME ) \
    static PyObject *PYCXX_NOARGS_METHOD_NAME( NAME )( PyObject *_self, PyObject *, PyObject * ) \
    { \
        try \
        { \
            Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
            CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
            Py::Object r( (self->NAME)() ); \
            return Py::new_reference_to( r.ptr() ); \
        } \
        catch( Py::Exception & ) \
        { \
            return 0; \
        } \
    }

#define PYCXX_VARARGS_METHOD_DECL( CLS, NAME ) \
    static PyObject *PYCXX_VARARGS_METHOD_NAME( NAME )( PyObject *_self, PyObject *_a, PyObject * ) \
    { \
        try \
        { \
            Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
            CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
            Py::Tuple a( _a ); \
            Py::Object r( (self->NAME)( a ) ); \
            return Py::new_reference_to( r.ptr() ); \
        } \
        catch( Py::Exception & ) \
        { \
            return 0; \
        } \
    }

#define PYCXX_KEYWORDS_METHOD_DECL( CLS, NAME ) \
    static PyObject *PYCXX_KEYWORDS_METHOD_NAME( NAME )( PyObject *_self, PyObject *_a, PyObject *_k ) \
    { \
        try \
        { \
            Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
            CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
            Py::Tuple a( _a ); \
            Py::Dict k; \
            if( _k != NULL ) \
                k = _k; \
            Py::Object r( (self->NAME)( a, k ) ); \
            return Py::new_reference_to( r.ptr() ); \
        } \
        catch( Py::Exception & ) \
        { \
            return 0; \
        } \
    }
#define PYCXX_ADD_NOARGS_METHOD( PYNAME, NAME, docs ) \
    add_method( #PYNAME, (PyCFunction)PYCXX_NOARGS_METHOD_NAME( NAME ), METH_NOARGS, docs )

#define PYCXX_ADD_VARARGS_METHOD( PYNAME, NAME, docs ) \
    add_method( #PYNAME, (PyCFunction)PYCXX_VARARGS_METHOD_NAME( NAME ), METH_VARARGS, docs )

#define PYCXX_ADD_KEYWORDS_METHOD( PYNAME, NAME, docs ) \
    add_method( #PYNAME, (PyCFunction)PYCXX_KEYWORDS_METHOD_NAME( NAME ), METH_VARARGS | METH_KEYWORDS, docs )

Again, this threefold duplication can be eliminated:

#define PYCXX_NOARGS_METHOD_NAME( NAME ) _callNoArgsMethod__##NAME
#define PYCXX_VARARGS_METHOD_NAME( NAME ) _callVarArgsMethod__##NAME
#define PYCXX_KEYWORDS_METHOD_NAME( NAME ) _callKeywordsMethod__##NAME

// - - -

#define PYCXX_DECL_METHOD( CLS, NAME, METHOD_NAME, ... ) \
    static PyObject *METHOD_NAME( PyObject *_self, PyObject *_a, PyObject *_k ) \
    { \
        try \
        { \
            Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
            CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
            Py::Object r(  (self -> NAME)( __VA_ARGS__ )  ); \
            return Py::new_reference_to( r.ptr() ); \
        } \
        catch( Py::Exception & ) \
        { \
            return 0; \
        } \
    }

#define PYCXX_NOARGS_METHOD_DECL(   _class, name ) PYCXX_DECL_METHOD( _class, name, PYCXX_NOARGS_METHOD_NAME( name ) )
#define PYCXX_VARARGS_METHOD_DECL(  _class, name ) PYCXX_DECL_METHOD( _class, name, PYCXX_VARARGS_METHOD_NAME( name ), _a?Py::Tuple{_a}:Py::Tuple{} )
#define PYCXX_KEYWORDS_METHOD_DECL( _class, name ) PYCXX_DECL_METHOD( _class, name, PYCXX_KEYWORDS_METHOD_NAME( name ), _a?Py::Tuple{_a}:Py::Tuple{}, _k?Py::Dict{_k}:Py::Dict{} )

// - - -

#define PYCXX_ADD_METHOD( py_name, name, docs, method_name, flags ) \
    add_method( \
               #py_name, \
               (PyCFunction)method_name, \
               flags, \
               docs \
               )

#define PYCXX_ADD_NOARGS_METHOD(   py_name, name, docs )  PYCXX_ADD_METHOD( py_name, name, docs, PYCXX_NOARGS_METHOD_NAME( name ),   METH_NOARGS )
#define PYCXX_ADD_VARARGS_METHOD(  py_name, name, docs )  PYCXX_ADD_METHOD( py_name, name, docs, PYCXX_VARARGS_METHOD_NAME( name ),  METH_VARARGS )
#define PYCXX_ADD_KEYWORDS_METHOD( py_name, name, docs )  PYCXX_ADD_METHOD( py_name, name, docs, PYCXX_KEYWORDS_METHOD_NAME( name ), METH_VARARGS | METH_KEYWORDS )
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License