如何从CPython返回函数指针到Python,并用ctypes调用C函数?
我有一个CPython的函数:
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);
}
然后在Python中我有:
import mymodule
ptr = mymodule.get_fptr() # will return a PyCObject wrapping the C function pointer
接下来:
from ctypes import *
SOMEFUNC_T = CFUNCTYPE(c_void, c_int)
somefunc = SOMEFUNC_T(ptr) # <-- bad!
现在,如果我把get_fptr改成返回:PyLong_FromSize_t(size_t(my_cfunc)),那么'somefunc'就会有效了。
但是我不想把一个函数指针转换成size_t类型。
请给点建议。
1 个回答
首先,我不太明白你为什么想从Python扩展中返回一个C函数指针,然后再通过ctypes在Python中调用它。其实更合理的做法是直接通过Python扩展来调用C函数,除非我漏掉了什么。
其次,ctypes似乎根本不支持PyCObject。你调用CFUNCTYPE(None, c_int)时(我把c_void换成了None),如果传入一个PyCObject参数就会失败,因为CFUNCTYPE期望的是一个整数,而不知道该如何处理PyCObject。
为什么不直接为my_cfunc写一个Python包装器呢?这样你就可以在Python中调用它,而不用麻烦ctypes了。例如:
PyObject *call_fptr(PyObject *self, PyObject *args)
{
int arg;
if (!PyArg_ParseTuple(args, "i", &arg))
return NULL;
my_cfunc(arg);
Py_RETURN_NONE;
}
另外,如果你喜欢使用ctypes,my_func的Python包装器可以用来实例化一个ctypes外部函数(这点和PyCObject不同)!
from ctypes import *
import foo
SOMEFUNC_T = CFUNCTYPE(None, c_int)
cfunc = SOMEFUNC_T(foo.call_fptr)
cfunc(1)
编辑: 你提到C函数应该接受可变数量的参数,这让你的问题变得更复杂了……你打算怎么用ctypes包装这个可变参数的C函数呢?因为CFUNCTYPE需要一组已知的参数。
这个问题其实归结起来就是把Python的元组转换成C的可变参数列表,这显然不是一件简单的事。实际上,SWIG在它的文档中专门有一节讨论这个问题,里面提到:大多数其他的包装生成工具明智地选择避免这个问题。
不过,它确实给出了建议,可以借助libffi以一种可移植的方式动态构建可变参数列表。
最后,我的建议是用SWIG来包装你的可变参数函数,这样可以省去很多麻烦。