Python C API,发送一个Python函数指针到C并执行它



我想在python中创建一个函数,将它的函数指针传递给c并在那里执行。

我的python文件:

import ctypes
import example
def tester_print():
print("Hello")
my_function_ptr = ctypes.CFUNCTYPE(None)(tester_print)
example.pass_func(my_function_ptr)

下面是我用c写的函数:

typedef void (*MyFunctionType)(void);
PyObject* pass_func(PyObject *self, PyObject* args)
{
PyObject* callable_object;
if (!PyArg_ParseTuple(args, "O", &callable_object))
return NULL;
if (!PyCallable_Check(callable_object)) 
{
PyErr_SetString(PyExc_TypeError, "The object is not a callable function.");
return NULL;
}
PyObject* function_pointer = PyCapsule_New(callable_object, "my_function_capsule", NULL);
if (function_pointer == NULL) return NULL;
MyFunctionType my_function = (MyFunctionType) PyCapsule_GetPointer(function_pointer, "my_function_capsule");
if (my_function == NULL) return NULL;
my_function(); // Or (*my_function)() Both same result.
// PyCapsule_Free(function_pointer);
Py_RETURN_NONE;
}

这样做会导致my_function()调用出现seg错误。我该怎么做呢?

如果您只是试图将Python函数传递给C扩展,请直接传递它(不要使用ctypes)并使用PyObject_Call来调用它:

example.pass_func(tester_print)

PyObject_CallNoArgs(callable_object);

如果您出于某种原因需要一个真正的C函数指针,通常的方法是编写一个C包装器,将可调用对象作为参数:

void callable_wrapper(PyObject *func) {
PyObject_CallNoArgs(func);
// plus whatever other code you need (e.g. reference counting, return value handling)
}

接受回调函数的大多数合理的C api也提供了一种向可调用对象("user data")添加任意参数的方法;例如,使用pthreads:

result = pthread_create(&tid, &attr, callable_wrapper, callable_object);

确保正确处理引用计数:在将可调用对象传递给C API之前,将其自增引用,并在不再需要时减少引用(例如,如果回调只被调用一次,callable_wrapper可以在返回之前DECREF)。

当使用线程时,你还需要确保在调用任何Python代码时保持GIL;有关更多细节和代码示例,请参阅https://docs.python.org/3/c-api/init.html#non-python-created-threads。


你当前的代码正在做的是接收一个指向ctypes CFUNCTYPE对象的指针作为callable_object,将该指针放在胶囊中,再次将其取出,并调用它,就好像它是一个C函数指针一样。这不起作用,因为它实际上试图调用CFUNCTYPE对象,就好像它是一个C函数一样(胶囊的东西最终是无用的)。当你使用Python C API时,几乎不需要Python中的ctypes,因为C API可以直接与Python对象交互。