Я пишу SWIG-обертку вокруг пользовательской библиотеки С++, которая определяет свои собственные типы исключений С++. Типы исключений библиотеки более богаты и более конкретны, чем стандартные исключения. (Например, один класс представляет ошибки синтаксического анализа и имеет набор номеров строк.) Как передать эти исключения обратно на Python, сохраняя тип исключения?
Как распространять исключения С++ на Python в библиотеке оберток SWIG?
Ответ 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
%except(python) {
try {
$function
}
catch (RangeError) {
PyErr_SetString(PyExc_IndexError,"index out-of-bounds");
return NULL;
}
}
Ответ 4
Помогает ли описать исключение при сбоях? Он упоминает определение разных обработчиков исключений.
Ответ 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");
}