Elias Bachaalany
Elias Bachaalany

Reputation: 1200

How to return a function pointer from CPython to Python then call a C function with ctypes?

I have CPython function:

void my_cfunc(int arg)
{
  printf("Hello from C; arg=%d\n", arg);
}

PyObject *get_fptr(PyObject * /*self*/, PyObject * /*args*/)
{
    return PyCObject_FromVoidPtr(my_cfunc, NULL);
}

Then later, in Python I have:

import mymodule

ptr = mymodule.get_fptr() # will return a PyCObject wrapping the C function pointer

Then later:

from ctypes import *

SOMEFUNC_T = CFUNCTYPE(c_void, c_int)
somefunc = SOMEFUNC_T(ptr) # <-- bad!

Now, if I change get_fptr to return as: PyLong_FromSize_t(size_t(my_cfunc)) then 'somefunc' will be valid.

But I don't want to cast a function pointer to a size_t.

Please advise

Upvotes: 3

Views: 1368

Answers (1)

aknuds1
aknuds1

Reputation: 67997

First of all, I don't understand why you'd want to return a C function pointer from a Python extension only to call it from Python (via ctypes), while the logical thing would be to call the C function via the Python extension (unless I'm missing something).

Second, it does not look like ctypes supports PyCObject at all. Your call to CFUNCTYPE(None, c_int) [I replaced c_void with None] with a PyCObject argument fails because CFUNCTYPE expects an integer, and does not know what to do with a PyCObject.

Why not write a Python wrapper to my_cfunc instead, which you'll call from Python without the ctypes hassle? For example:

PyObject *call_fptr(PyObject *self, PyObject *args)
{
    int arg;
    if (!PyArg_ParseTuple(args, "i", &arg))
        return NULL;

    my_cfunc(arg);
    Py_RETURN_NONE;
}

As a bonus, if ctypes is your thing, the Python wrapper to my_func can be used to instantiate a ctypes foreign function (unlike the PyCObject)!

from ctypes import *
import foo

SOMEFUNC_T = CFUNCTYPE(None, c_int)
cfunc = SOMEFUNC_T(foo.call_fptr)
cfunc(1)

Edit: Specifying that the C function should take a variable number of arguments makes your question more difficult... How would you wrap your variable argument C function with ctypes anyway, considering that CFUNCTYPE requires a known set of arguments?

The problem in this case really boils down to converting a Python tuple to a variable argument list in C, which is apparently anything but trivial. In fact SWIG has dedicated a section of its documentation to this problem, saying for instance this: Most other wrapper generation tools have wisely chosen to avoid this issue.

It does give the advise, though, that one can dynamically construct variable argument lists in a portable manner with the help of libffi.

My suggestion, ultimately, is to wrap your variable arguments function with SWIG and save yourself the pain.

Upvotes: 1

Related Questions