Python/Cython/C和回调,使用Cython从C调用Python函数

Iva*_*ets 5 c python function callback cython

我有以下问题.我们必须将回调函数传递给C代码.如果该函数是同一模块中的Cython函数,则情况非常简单

在Cython中:

def callme(int x):
    c_callme(x, <int (*)(int)>&callbackme) 

cdef int callbackme(int x):
    print <int> x
    return <int>x
Run Code Online (Sandbox Code Playgroud)

在C:

int c_callme(int x, int (*f)(int))
{
    printf("---%d\n",x);
    printf("--%d\n",f(x));
    return x;
}
Run Code Online (Sandbox Code Playgroud)

问题如下:我们希望以最Pythonic的方式推广这个代码,以便它也可以接受python函数作为回调参数(当然,还需要一些额外的层),以及来自另一个模块的C/Cython函数.我想,对于来自单独模块的C/Cython函数,必须获取这些函数的地址(转换为long int?),对于Python函数,需要某个包装器

Sau*_*tro 11

在这个例子中,从Python包装器提取Cubature集成C库,Python函数被传递给C函数,该函数具有给定的原型cfunction.您可以使用相同的原型创建一个函数,称为cfunction_cb(回调),并返回相同的类型,int在此示例中):

cdef object f
ctypedef int (*cfunction) (double a, double b, double c, void *args)

cdef int cfunction_cb(double a, double b, double c, void *args):
    global f
    result_from_function = (<object>f)(a, b, c, *<tuple>args)
    for k in range(fdim):
        fval[k] = fval_buffer[k]
    return 0
Run Code Online (Sandbox Code Playgroud)

调用C函数时,可以使用C原型强制转换回调包装:

def main(pythonf, double a, double b, double c, args): 
    global f
    f = pythonf
    c_function( <cfunction> cfunction_cb,
                double a,
                double b,
                double c,
                <void *> args )
Run Code Online (Sandbox Code Playgroud)

在这个例子中,还展示了如何通过C将参数传递给Python函数.


Fre*_*Foo -1

我认为最简单的方法是将 C 回调包装在

cdef class Callback(object):
    cdef int (*f)(int)        # I hope I got the syntax right...

    def __call__(int x):
        return self.f(x)
Run Code Online (Sandbox Code Playgroud)

因此您可以将此类型的对象以及任何其他可调用对象传递给必须调用回调的函数。

但是,如果您必须从 C 调用回调,那么您应该向它传递一个额外的参数,即 Python 对象的可选地址,可能会转换为“void*”。

(请不要函数指针转换为long,否则您可能会得到未定义的行为。我不确定是否可以安全地将函数指针转换为任何内容,甚至是void*。)