问题描述
我有一个 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)126I 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 包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!