我无法通过dlsym传播异常。我使用dlsym加载一个cythonized python文件。我在下面做了一个最小的工作示例,所以你可以自己尝试一下:
我有一个pyx文件,c_fun.pyx,我使用Cython编译成C文件。然后我使用dlsym加载另一个程序中的so文件,比如use_fun.c ++。您可以使用./compile.sh来编译文件。在执行./test时,程序崩溃并出现分段错误。
#c_fun.pyx:
cdef public double _myfunction(double x) except*:
a=1/0 # This does not propagate an exception. Comment to make the example work
return x**2-x # This works.
#use_fun.c++
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv)
{
void* handle = dlopen("./c_fun.so", RTLD_NOW | RTLD_GLOBAL);
if(handle==NULL) {
printf("%s \nat line number: %i\n",dlerror(),__LINE__); return;
}
double (*f)(double) = (double (*)(double))dlsym(handle, "_myfunction");
if(f==NULL) {
printf("%s\n",dlerror()); return;
}
double res = 0;
try {
res = (*f)((double)99);
} catch(char *err) {
printf("Got exception: %s.\n", err);
}
printf("res = %f\n", res);
}
#setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("c_fun", ["c_fun.pyx"], libraries = ['python2.7'], extra_compile_args= ['-fexceptions'])]
setup(
name = 'Hello world app', cmdclass = {'build_ext': build_ext}, ext_modules = ext_modules
)
# compile.sh
python setup.py build_ext --inplace
echo "gcc use_fun.c++ -g -O0 -o test -ldl"
g++ use_fun.c++ -g -O0 -o test -ldl
最初,我尝试在我的函数末尾没有“except *”,没有“-fexceptions”编译器标志。但添加这些并不会改变行为。 gdb甚至不允许我回溯问题,并说:“无法找到新线程:泛型错误 “。我已经在互联网上查看了与dlsym结合使用的异常处理,但发现的信息很少。
故事的其余部分:为了使它更复杂,实际上这个use_fun.c ++文件是我导入的Python模块。所以:我使用Python加载模块use_fun,在那个C ++模块中,调用了_myfunction。但也是,我无法正确处理异常。但是,在这种情况下,我成功地使用gdb进入C代码并看到PyErr_Format被成功调用。但是错误不是在C ++代码中触发也不会被捕获。
请注意,为了能够公开文件_myfunction,我在pyx文件中指定了'public'关键字。没有它,名称修改将导致dlsym函数调用失败。 我试着在这两个链接上寻找文档:
http://docs.cython.org/src/userguide/external_C_code.html#using-cython-declarations-from-c
http://docs.cython.org/src/userguide/language_basics.html#error-return-values
编辑:我找到了解决方案。我会保留上面的原始问题,以便它可以帮助其他人。基本上有2个问题
1)当然,由于C没有例外,因此在函数上设置try / catch是错误的!什么工作,是使用PyErr_Occurred()检查Python中是否发生错误。
2)由于cython会生成一个模块,因此必须先进行初始化,然后才能正确使用它。这意味着:调用Py_Initialize / Py_Finalize,并调用init_cfun方法。
解决方案如下所示:
#include "Python.h"
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv)
{
Py_Initialize();
void* handle = dlopen("./c_fun.so", RTLD_NOW | RTLD_GLOBAL);
if(handle==NULL) {
printf("%s \nat line number: %i\n",dlerror(),__LINE__); return -1;
}
void (*init_func)();
*(void**)(&init_func) = dlsym(handle, "initc_fun");
init_func();
double (*f)(double) = (double (*)(double))dlsym(handle, "_myfunction");
if(f==NULL) {
printf("%s\n",dlerror()); return -1;
}
double res = 0;
res = (*f)((double)99);
if(PyErr_Occurred()) {
PyErr_Print();
}
printf("res = %f\n", res);
Py_Finalize();
return 0;
}