围绕 Python 函数制作 Cython 包装器 [英] Making a Cython wrapper around Python function

查看:30
本文介绍了围绕 Python 函数制作 Cython 包装器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 C 函数,它的签名如下所示:

typedef double (*func_t)(double*, int)int some_f(func_t myFunc);

我想传递一个 Python 函数(不一定是明确的)作为 some_f 的参数.不幸的是,我不能改变 some_f 的声明,就是这样:我不应该改变 C 代码.

我尝试做的一件显而易见的事情是创建一个基本的包装函数,如下所示:

cdef double wraping_f(double *d, int i/*?, object f */):/*做东西*/返回 <double>f(d_t)

但是,我无法想出一种方法将它实际放入"wrapping_f 的主体中.

这个问题有一个非常糟糕的解决方案:我可以使用全局对象变量,但是这迫使我复制粘贴多个本质上相同的包装函数的实例,这些实例将使用不同的全局函数(我计划使用多个Python 函数同时运行).

解决方案

由于历史原因,我保留了我的另一个答案 - 它表明,没有 jit 编译就无法做你想做的事,并帮助我理解它有多棒@DavidW 在这个答案中的建议是.

为了简单起见,我使用了一个稍微简单的函数签名,相信您会根据需要对其进行相应更改.

这是一个闭包的蓝图,它让 ctypes 在幕后进行 jit 编译:

%%cython#needs Cython >由于逐字 C 代码而运行 0.28cdef extern from *: #fill some_t with life"""typedef int (*func_t)(int);静态 int some_f(func_t fun){返回乐趣(42);}"""ctypedef int (*func_t)(int)int some_f(func_t myFunc)#适用于任何最近的 Cython 版本:导入 ctypescdef 类关闭:cdef 对象 python_funcdef 对象 jitted_wrapperdefinner_fun(self, int arg):返回 self.python_fun(arg)def __cinit__(self, python_fun):self.python_fun=python_funftype = ctypes.CFUNCTYPE(ctypes.c_int,ctypes.c_int) #定义签名self.jitted_wrapper=ftype(self.inner_fun) #jit 包装器cdef func_t get_fun_ptr(self):返回 (<func_t *><size_t>ctypes.addressof(self.jitted_wrapper))[0]def use_closure(关闭关闭):打印(some_f(closure.get_fun_ptr()))

现在使用它:

<预><代码>>>>cl1, cl2=闭包(lambda x:2*x), 闭包(lambda x:3*x)>>>use_closure(cl1)84>>>use_closure(cl2)126

I have a C function which signature looks like this:

typedef double (*func_t)(double*, int)
int some_f(func_t myFunc);

I would like to pass a Python function (not necessarily explicitly) as an argument for some_f. Unfortunately, I can't afford to alter declaration of some_f, that's it: I shouldn't change C code.

One obvious thing I tried to do is to create a basic wrapping function like this:

cdef double wraping_f(double *d, int i /*?, object f */):
     /*do stuff*/
     return <double>f(d_t)

However, I can't come up with a way to actually "put" it inside wrapping_f's body.

There is a very bad solution to this problem: I could use a global object variable, however this forces me copy-n-paste multiple instances of essentially same wrapper function that will use different global functions (I am planning to use multiple Python functions simultaneously).

解决方案

I keep my other answer for historical reasons - it shows, that there is no way to do what you want without jit-compilation and helped me to understand how great @DavidW's advise in this answer was.

For the sake of simplicity, I use a slightly simpler signature of functions and trust you to change it accordingly to your needs.

Here is a blueprint for a closure, which lets ctypes do the jit-compilation behind the scenes:

%%cython
#needs Cython > 0.28 to run because of verbatim C-code 
cdef extern from *:   #fill some_t with life
    """
    typedef int (*func_t)(int);
    static int some_f(func_t fun){
        return fun(42);
    }
    """
    ctypedef int (*func_t)(int)
    int some_f(func_t myFunc)

#works with any recent Cython version:
import ctypes
cdef class Closure:
    cdef object python_fun
    cdef object jitted_wrapper

    def inner_fun(self, int arg):
        return self.python_fun(arg)

    def __cinit__(self, python_fun):
        self.python_fun=python_fun
        ftype = ctypes.CFUNCTYPE(ctypes.c_int,ctypes.c_int) #define signature
        self.jitted_wrapper=ftype(self.inner_fun)           #jit the wrapper

    cdef func_t get_fun_ptr(self):
        return (<func_t *><size_t>ctypes.addressof(self.jitted_wrapper))[0]

def use_closure(Closure closure):
    print(some_f(closure.get_fun_ptr()))

And now using it:

>>> cl1, cl2=Closure(lambda x:2*x), Closure(lambda x:3*x)
>>> use_closure(cl1)
84
>>> use_closure(cl2)
126

这篇关于围绕 Python 函数制作 Cython 包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆