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

8

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

Cython中:

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

cdef int callbackme(int x):
    print <int> x
    return <int>x

在C语言中:
int c_callme(int x, int (*f)(int))
{
    printf("---%d\n",x);
    printf("--%d\n",f(x));
    return x;
}

问题如下:我们希望以最Pythonic的方式概括这段代码,使其能够接受Python函数作为回调参数(当然,需要一些额外的层),以及来自另一个模块的C/Cython函数。我想,对于来自单独模块的C/Cython函数,必须获取这些函数的地址(转换为long int?),而对于Python函数则需要某些包装器。

请阅读有关代码格式的常见问题解答(FAQ)... - ThiefMaster
如果您想同时包装不同的函数(因此一个全局变量不足够),那么避免运行时生成代码是不可能的。请参见https://dev59.com/Gazka4cB1Zd3GeqP8nJf#51054667。 - ead
2个回答

15
在这个例子中,从Python包装器中提取Cubature积分C库,一个Python函数被传递给了一个具有cfunction原型的C函数。你可以创建一个具有相同原型的函数,称为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

调用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 )

在这个例子中,还展示了如何通过C向您的Python函数传递参数。

1
全局实例可以使实例保持存活和健康。 - dashesy
1
应该使用 c_function(&cfunction_cb,...) 而不是 c_function(<cfunction>cfunction_cb,...),因为带有 <cfunction> 的版本会将任何函数强制转换为 cfunction-functor,并且只有在运行时才能看到问题。对于带有 & 的版本,编译器将捕获可能的类型不匹配。 - ead

-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)

这样,您可以将这种类型的对象以及其他可调用函数传递给必须调用回调函数的函数。

但是,如果您必须从C中调用回调函数,则可以传递一个额外的参数,即Python对象的可选地址,可能转换为`void*`。

(请不要将函数指针强制转换为`long`,否则可能会出现未定义的行为。我不确定是否可以安全地将函数指针转换为任何其他类型,甚至是`void *`。)


1
我也曾考虑过那个方向。但是代码根本无法编译 :( 。你能详细解释一下为什么我们不需要将函数指针转换为 long 吗? - Ivan Oseledets
@IvanOseledets:函数指针值不能保证适合于long,反之亦然。它可能在一台机器上运行正常,但在另一台机器上失败。这段代码无法编译的原因可能是语法错误,我手头没有Cython安装包。 - Fred Foo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接