Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 597
Elias Bachaalany Avatar asked May 23 '26 12:05

Elias Bachaalany


1 Answers

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.

like image 174
aknuds1 Avatar answered May 26 '26 01:05

aknuds1