【发布时间】:2021-07-13 02:33:01
【问题描述】:
我基本上是在尝试包装一个 C 库,并希望使用 Cython 在包装的 C 函数中传递 Python 函数。我正在使用全局变量来存储 Python 函数,在 Cython 包装器中调用它,然后将 Cython 包装器作为 C 函数传递给其他 C 函数。我想在被 Cython 包装器调用时捕获 Python 函数中的错误,因此,我在 Cython 包装器声明的末尾添加了 except? -1。
问题是当我使用 except 关键字传递 Cython 函数时,它无法将其转换为类似 C 的函数。如果我不这样做,我将无法处理 Python 函数中发生的错误(我需要这样做,因为该函数是在循环中调用的)
MRE 如下所示。
# file: mre.pyx
# cython: language_level=3
import numpy as np
cimport numpy as np
_glob_params = None
_glob_func = None
cdef extern from "mre.h":
double eval_func(double (*func)(double, double *, int))
cdef double _func_wrapper(double x, double *params, int n) except? -1:
return _glob_func(x, *_glob_params)
def run_func(func, params=(), Py_ssize_t size=100):
cdef Py_ssize_t i
cdef np.ndarray[np.float64_t, ndim=1] out = np.empty(size, dtype=np.float64)
global _glob_func, _glob_params
_glob_func = func
_glob_params = params
for i in range(size):
out[i] = eval_func(_func_wrapper)
return out
// file : mre.h
#pragma once
#include <stdlib.h>
#include <stdio.h>
double eval_func(double (*func)(double, double *, int)) {
double res = func(1.0, NULL, 0);
if ( res < 0. ) {
fprintf(stderr, "ERROR: func < 0. -> not allowed!\n");
}
return res;
}
错误:
Error compiling Cython file:
------------------------------------------------------------
...
_glob_func = func
_glob_params = params
for i in range(size):
out[i] = eval_func(_func_wrapper)
^
------------------------------------------------------------
mre.pyx:24:27: Cannot assign type 'double (double, double *, int) except? -1.0' to 'double (*)(double, double *, int)'
没有except? -1:
>>> from mre import run_func
>>> run_func(None)
TypeError: 'NoneType' object is not callable
Exception ignored in: 'mre._func_wrapper'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
TypeError: 'NoneType' object is not callable
Exception ignored in: 'mre._func_wrapper'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
...
...
...
...
TypeError: 'NoneType' object is not callable
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
那么,有没有办法在没有except 的情况下处理 Cython 中的异常?我尝试使用ctypes 包装Python 函数,但在实践中非常慢。
【问题讨论】:
-
我觉得你需要把函数地址拿进去:
out[i] = eval_func(&_func_wrapper) -
不,不起作用。我认为函数已经是 C 中的指针。因此,您不需要传递显式引用。
-
所以您想将错误传播到 eval_func?也许,在
cdef extern from "mre.h"中尝试将except? -1添加到 eval_func 函数的声明中。 -
好的。经过试验,我只能抛出一次错误。我通过修改 mre.h 中的 eval_func 以使用 throw std::runtime_error 来抛出此错误。然后,在 mre.pyx 中用“except +”声明 eval_func。我将 mre.pyx 编译为 c++ 而不是 c。 c++ 抛出的新错误在 python 中被捕获,消息为“RuntimeError: ERROR: func not allowed!”。这种情况发生一次。这是你想要的吗?我可以写一个完整的答案。
-
澄清一下,我让它以一种可以在 Python 中处理的方式抛出一次,而不是相同的错误发生 100 次但从未被捕获。