Подтвердить что ты не робот

Как распространять исключения С++ на Python в библиотеке оберток SWIG?

Я пишу SWIG-обертку вокруг пользовательской библиотеки С++, которая определяет свои собственные типы исключений С++. Типы исключений библиотеки более богаты и более конкретны, чем стандартные исключения. (Например, один класс представляет ошибки синтаксического анализа и имеет набор номеров строк.) Как передать эти исключения обратно на Python, сохраняя тип исключения?

4b9b3361

Ответ 1

Я знаю, что этот вопрос несколько недель назад, но я просто нашел его, поскольку я изучал решение для себя. Поэтому я отвечу на ответ, но я заранее предупрежу, что это не может быть привлекательным решением, поскольку файлы интерфейса swig могут быть более сложными, чем ручной кодирование обертки. Кроме того, насколько я могу судить, документация swig никогда не касается непосредственно определенных пользователем исключений.

Предположим, вы хотите отбросить следующее исключение из вашего модуля кода С++, mylibrary.cpp, а также небольшое сообщение об ошибке, которое должно быть уловлено в вашем коде на языке python:

throw MyException("Highly irregular condition...");  /* C++ code */

MyException - это исключение пользователя, определенное в другом месте.

Прежде чем начать, отметьте, как это исключение должно быть уловлено в вашем python. Для наших целей, скажем, у вас есть код python, например:

import mylibrary
try:
    s = mylibrary.do_something('foobar')
except mylibrary.MyException, e:
    print(e)

Я думаю, это описывает вашу проблему.

Мое решение включает в себя создание четырех дополнений к вашему файлу интерфейса swig (mylibrary.i) следующим образом:

Шаг 1: В директиве заголовка (обычно неименованный блок% {...%}) добавьте объявление для указателя на исключение python, которое мы будем называть pMyException. Шаг 2 ниже определит это:

%{
#define SWIG_FILE_WITH_INIT  /* for eg */
extern char* do_something(char*);   /* or #include "mylibrary.h" etc  */
static PyObject* pMyException;  /* add this! */
%}

Шаг 2: Добавьте директиву инициализации ( "m" является особенно вопиющим, но то, что в данный момент требуется для swig v1.3.40 в этом сконфигурированном файле оболочки) - pMyException было объявлено на шаге 1 выше:

%init %{
    pMyException = PyErr_NewException("_mylibrary.MyException", NULL, NULL);
    Py_INCREF(pMyException);
    PyModule_AddObject(m, "MyException", pMyException);
%}

Шаг 3: Как упоминалось в более раннем сообщении, нам нужна директива исключения - примечание "% except (python)" устарело. Это обернет функцию С+++ "do_something" в блок try-except, который поймает исключение С++ и преобразует его в исключение python, определенное на шаге 2 выше:

%exception do_something {
    try {
        $action
    } catch (MyException &e) {
        PyErr_SetString(pMyException, const_cast<char*>(e.what()));
        SWIG_fail;
    }
}

/* The usual functions to be wrapped are listed here: */
extern char* do_something(char*);

Шаг 4: Поскольку swig создает упаковку python (модуль "shadow" ) вокруг dll dp, нам также необходимо убедиться, что наш код python может "видеть" в .pyd файле. Следующие работали для меня, и было предпочтительнее непосредственно редактировать код обертки swig py:

%pythoncode %{
    MyException = _mylibrary.MyException
%}

Возможно, слишком поздно использовать его для оригинального плаката, но, возможно, кто-то еще найдет предложенные выше предложения. Для небольших заданий вы можете предпочесть чистоту оболочки с расширением С++ для путаницы с интерфейсом интерфейса swig.


Добавлено:

В приведенном выше списке файлов интерфейса я пропустил стандартную директиву модуля, потому что я только хотел описать дополнения, необходимые для выполнения исключений. Но ваша строка модуля должна выглядеть так:

%module mylibrary

Кроме того, ваш setup.py(если вы используете distutils, который я рекомендую по крайней мере для начала работы), должен иметь код, похожий на следующий, иначе шаг 4 завершится неудачно, если _mylibrary не будет распознан:

/* setup.py: */
from distutils.core import setup, Extension

mylibrary_module = Extension('_mylibrary', extra_compile_args = ['/EHsc'],
                    sources=['mylibrary_wrap.cpp', 'mylibrary.cpp'],)

setup(name="mylibrary",
        version="1.0",
        description='Testing user defined exceptions...',
        ext_modules=[mylibrary_module],
        py_modules = ["mylibrary"],)

Обратите внимание на флаг компиляции /EHsc, который мне нужен для Windows для компиляции исключений. Ваша платформа может не требовать этого флага; У google есть данные.

Я проверил код, используя свои собственные имена, которые я преобразовал здесь в "mylibrary" и "MyException", чтобы помочь в обобщении решения. Надеемся, что ошибок в транскрипции немного или нет. Основными моментами являются директивы% init и% pythoncode вместе с

static PyObject* pMyException;

в директиве заголовка.

Надеюсь, что это прояснит решение.

Ответ 2

Я немного добавлю здесь, так как приведенный здесь пример говорит, что "% except (python)" - это осуждается...

Теперь вы можете (как и в случае с swig 1.3.40) сделать полностью общий, script -язычный независимый перевод. Мой пример:

%exception { 
    try {
        $action
    } catch (myException &e) {
        std::string s("myModule error: "), s2(e.what());
        s = s + s2;
        SWIG_exception(SWIG_RuntimeError, s.c_str());
    } catch (myOtherException &e) {
        std::string s("otherModule error: "), s2(e.what());
        s = s + s2;
        SWIG_exception(SWIG_RuntimeError, s.c_str());
    } catch (...) {
        SWIG_exception(SWIG_RuntimeError, "unknown exception");
    }
}

Это приведет к возникновению исключения RuntimeError на любом поддерживаемом языке сценариев, включая Python, не получая специфичные для python вещи в ваших других заголовках.

Вам нужно поставить это перед вызовами, которые хотят эту обработку исключений.

Ответ 3

Из документация swig

%except(python) {
try {
$function
}
catch (RangeError) {
    PyErr_SetString(PyExc_IndexError,"index out-of-bounds");
    return NULL;
}
}

Ответ 5

U также может использовать:

ловит: http://www.swig.org/Doc3.0/SWIGPlus.html#SWIGPlus_catches

Пример:

%catches(std::exception, std::string, int, ...);

который генерирует для каждой функции блок catch try:

  try {
    result = (namespace::Function *)new namespace::Function ((uint16_t const *)arg1);
  }
  catch(std::exception &_e) {
    SWIG_exception_fail(SWIG_SystemError, (&_e)->what());
  }
  catch(std::string &_e) {
    SWIG_Python_Raise(SWIG_From_std_string(static_cast< std::string >(_e)), "std::string", 0); SWIG_fail;
  }
  catch(int &_e) {
    SWIG_Python_Raise(SWIG_From_int(static_cast< int >(_e)), "int", 0); SWIG_fail;
  }
  catch(...) {
    SWIG_exception_fail(SWIG_RuntimeError,"unknown exception");
  }