First off, when you have a Python "extension method", implemented in C, and that function receives a Python callable as an argument, here is how you receive the argument, and how you call the callable:
/* this code uses only C features */
static PyObject *
foo(PyObject *self, PyObject *args)
{
PyObject *cb;
// Receive a single argument which can be any Python object
// note: the object's reference count is NOT increased (but it's pinned by
// the argument tuple).
if (!PyArg_ParseTuple(args, "O", &cb)) {
return 0;
}
// determine whether the object is in fact callable
if (!PyCallable_Check(cb)) {
PyErr_SetString(PyExc_TypeError, "foo: a callable is required");
return 0;
}
// call it (no arguments supplied)
// there are a whole bunch of other PyObject_Call* functions for when you want
// to supply arguments
PyObject *rv = PyObject_CallObject(cb, 0);
// if calling it returned 0, must return 0 to propagate the exception
if (!rv) return 0;
// otherwise, discard the object returned and return None
Py_CLEAR(rv);
Py_RETURN_NONE;
}
The problem with using logic like this to wrap bsp_init
is that the pointer to the Python callable is a data pointer. If you passed that pointer directly to bsp_init
, bsp_init
would attempt to invoke data as machine code and it would crash. If bsp_init
passed through a data pointer to the function that it calls, you could work around this with a "glue" procedure:
/* this code also uses only C features */
struct bsp_init_glue_args {
PyObject *cb;
PyObject *rv;
};
static void
bsp_init_glue(void *data)
{
struct bsp_init_glue_args *args = data;
args->rv = PyObject_CallObject(args->cb, 0);
}
static PyObject *
foo(PyObject *self, PyObject *args)
{
bsp_init_glue_args ba;
if (!PyArg_ParseTuple(args, "O", &ba.cb)) {
return 0;
}
if (!PyCallable_Check(ba.cb)) {
PyErr_SetString(PyExc_TypeError, "foo: a callable is required");
return 0;
}
bsp_init(bsp_init_glue, (void *)&ba, ...);
if (ba->rv == 0) return 0;
Py_CLEAR(ba->rv);
Py_RETURN_NONE;
}
Unfortunately, bsp_init
does not have this signature, so you cannot do this. But the alternative interface BSPLib::Classic::Init
takes a std::function<void()>
, which is an object-oriented wrapper around the pattern above, so you can do this instead:
/* this code requires C++11 */
static PyObject *
foo(PyObject *self, PyObject *args)
{
PyObject *cb;
PyObject *rv = 0;
if (!PyArg_ParseTuple(args, "O", &cb)) {
return 0;
}
if (!PyCallable_Check(cb)) {
PyErr_SetString(PyExc_TypeError, "foo: a callable is required");
return 0;
}
std::function<void()> closure = [&]() {
rv = PyObject_CallObject(cb, 0);
};
BSPLib::Classic::Init(closure, ...);
if (rv == 0) return 0;
Py_CLEAR(rv);
Py_RETURN_NONE;
}
The magic here is all in the [&]() { ... }
notation, which is syntactic sugar for defining and creating an instance of a local class that "captures" the variables cb
and rv
so that the code inside the curly braces, which will be compiled as a separate function, can communicate with foo
proper. This is a C++11 feature called "lambdas", which is a jargon term going all the way back to the earliest days of theoretical CS and immortalized by Lisp. Here is a tutorial, but I am not sure how good it is because I already know the concept inside and out.
It is not possible to do this in plain C, but it isn't possible to call BSPLib::Classic::Init
from plain C either (because you can't define a std::function
object at all in plain C ... well, not without reverse engineering the C++ standard library and ABI, anyway) so that's okay.