围绕Python函数制作Cython包装器 [英] Making a Cython wrapper around Python function
问题描述
我有一个C函数,其签名看起来像这样:
I have a C function which signature looks like this:
typedef double (*func_t)(double*, int)
int some_f(func_t myFunc);
我想将Python函数(不一定显式)作为some_f的参数传递。不幸的是,我负担不起更改some_f的声明,就是这样:我不应该更改C代码。
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)
但是,我无法提出一种将其实际放置在wrapping_f体内的方法。
However, I can't come up with a way to actually "put" it inside wrapping_f's body.
这个问题有一个非常糟糕的解决方案:我可以使用一个全局对象变量,但是这迫使我将本质上相同的包装函数的多个实例复制-粘贴到粘贴实例中,这些实例将使用不同的全局函数(我打算同时使用多个Python函数)。
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).
推荐答案
出于历史原因,我保留了其他答案-这表明,不进行jit编译就无法做您想要的事情,并且可以帮助我了解@DavidW的出色表现在这个答案是。
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.
以下是闭包的蓝图,它允许 ctypes
在后台进行jit编译:
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()))
现在使用它:
>>> cl1, cl2=Closure(lambda x:2*x), Closure(lambda x:3*x)
>>> use_closure(cl1)
84
>>> use_closure(cl2)
126
这篇关于围绕Python函数制作Cython包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!