2

dlsym を介して例外を伝播できません。dlsym を使用して、cythonized python ファイルをロードします。以下に最小限の作業例を作成したので、自分で試すことができます。

Cython を使用して C ファイルにコンパイルする pyx ファイル c_fun.pyx があります。次に、dlsym を使用して、use_fun.c++ などの別のプログラムで so ファイルをロードしています。./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 関数呼び出しが失敗します。次の 2 つのリンクに関するドキュメントを探してみました。

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