是否可以使用 Cython 将指向编译时未知函数的 C 函数指针传递给 C 函数?

Is it possible to pass a C function pointer to a function that is unknown at compile time to a C function using Cython?

我正在尝试从 Python 调用一个以函数指针作为参数的 C 函数。我需要在运行时动态确定该功能。使用ctypes,很简单。

C 代码可能如下所示:

// my_c_funcs.h
double mul(double, double);
double add(double, double);
double do_something(double (*)(double, double), double, double);
// my_c_funcs.h
int mul(int a, int b) {
    return a*b;
}
int add(int a, int b) {
    return a + b;
}
int do_something(int (*f)(int, int), int a, int b) {
    return f(a, b);
}

将该代码编译到名为“libMyCFuncs.so”的共享库后,我可以使用 ctypes:

传递在 Python 运行时确定的函数
# foo.py
import ctypes

lib = ctypes.cdll.LoadLibrary("./libMyCfuncs.so")

def foo(func_name, a, b):
    func = getattr(lib, func_name)
    return lib.do_something(func, a, b)

我知道我应该定义 return 类型,但为简洁起见,我将其省略并仅使用 ints。

上面的代码给出了预期的结果,例如调用 foo.foo('add', 2, 4) 会产生 6。但是,我更喜欢使用 Cython,因为我大量使用二维或更高维数组,恕我直言,在 Cython 中传递数组要容易得多。假设 Cython 代码位于 "foo.pyx":

# foo.pyx
cdef extern from "my_c_funcs.h":
    int mul(int, int)
    int add(int, int)
    int do_something(int (*)(int, int), int, int)

def foo(func_name, int a, int b):
    # ???

调用getattr甚至eval显然是行不通的。那么我如何在 Cython 中实现这一点呢?

你必须提供一个来自 cython 的包装器,可以在你的共享对象中调用。 Cython 本质上具有三种“模式”

  1. def : 正常 python 函数
  2. cpdef: python 具有可能 c/cpp 内部变量的可调用函数
  3. cdef:纯c/cpp函数

作为您的代码示例,一个简单的绑定是


cdef extern from "c_funcs.h":
    double mul(double, double)
    double add (double, double)
    double do_something(double(*)(double, double), double, double)

cdef extern from "c_funcs.c":
    pass

# this is callable from python
cpdef double py_do_something_mul(str name, double x,  double y):
    return do_something(mul, x, y)

如果你想要动态调度之类的东西,你也必须为此提供一些包装。它不适用于默认的 python 指令,但有序的或 unorered_map 可以用于此。