【问题标题】:Python/Cython/C and callbacks, calling a Python function from C using CythonPython/Cython/C 和回调,使用 Cython 从 C 调用 Python 函数
【发布时间】:2012-07-26 21:09:23
【问题描述】:

我有以下问题。 我们必须将回调函数传递给 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 函数,需要某个包装器

【问题讨论】:

  • 请阅读关于代码格式化的常见问题...
  • 如果你想同时包装不同的函数(所以一个全局变量是不够的),没有办法绕过运行时生成的代码。见stackoverflow.com/a/51054667/5769463

标签: python c function callback cython


【解决方案1】:

在这个例子中,extracted from a Python wrapperCubature integration C library,一个 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

在调用 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 函数。

【讨论】:

  • 这应该被接受为答案,它工作正常。全局实例使实例保持活力。
  • 它应该是c_function(&amp;cfunction_cb,...) 而不是c_function(&lt;cfunction&gt;cfunction_cb,...),因为带有&lt;cfunction&gt; 的版本会将任何函数转换为cfunction-functor,并且只会在运行时看到问题。对于带有&amp; 的版本,编译器将捕获可能的类型不匹配。
【解决方案2】:

我认为最简单的方法是将 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*。 )

【讨论】:

  • 我也在考虑这个方向。但是代码根本无法编译:(。你能详细说明为什么我们不必将函数指针转换为 long 吗?
  • @IvanOseledets:函数指针值不保证适合long,反之亦然。它可能在一台机器上工作,但在另一台机器上失败。这段代码无法编译可能是由于语法错误,我手边没有 Cython 安装。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多