2

我无法通过 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;
}
4

0 回答 0