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 )