Для этого вам в основном нужно написать %feature("director:except")
, который может обработать исключение Python и перезапустить его как C ++. Вот небольшой, но полный пример:
Предположим, у нас есть следующий заголовочный файл, который мы хотим обернуть:
#include <iostream>
#include <exception>
class MyException : public std::exception {
};
class AnotherException : public std::exception {
};
class Callback {
public:
virtual ~Callback() { std::cout << "~Callback()" << std:: endl; }
virtual void run() { std::cout << "Callback::run()" << std::endl; }
};
inline void call(Callback *callback) { if (callback) callback->run(); }
И этот код Python, который его использует:
import example
class PyCallback(example.Callback):
def __init__(self):
example.Callback.__init__(self)
def run(self):
print("PyCallback.run()")
raise example.MyException()
callback = PyCallback()
example.call(callback)
Мы можем определить следующий файл интерфейса SWIG:
%module(directors="1") example
%{
#include "example.h"
%}
%include "std_string.i"
%include "std_except.i"
%include "pyabc.i"
// Python requires that anything we raise inherits from this
%pythonabc(MyException, Exception);
%feature("director:except") {
PyObject *etype = $error;
if (etype != NULL) {
PyObject *obj, *trace;
PyErr_Fetch(&etype, &obj, &trace);
Py_DecRef(etype);
Py_DecRef(trace);
// Not too sure if I need to call Py_DecRef for obj
void *ptr;
int res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_MyException, 0);
if (SWIG_IsOK(res) && ptr) {
MyException *e = reinterpret_cast< MyException * >(ptr);
// Throw by pointer (Yucky!)
throw e;
}
res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_AnotherException, 0);
if (SWIG_IsOK(res) && ptr) {
AnotherException *e = reinterpret_cast< AnotherException * >(ptr);
throw e;
}
throw Swig::DirectorMethodException();
}
}
%feature("director") Callback;
%include "example.h"
Который обрабатывает ошибку из вызова Director, проверяет, был ли это один из наших MyException
экземпляров, а затем повторно выдает указатель, если он был. Если у вас есть несколько типов исключений, вам, вероятно, потребуется использовать PyErr_ExceptionMatches
, чтобы определить, какой это тип.
Мы можем также бросить по значению или ссылке, используя:
// Throw by value (after a copy!)
MyException temp = *e;
if (SWIG_IsNewObj(res))
delete e;
throw temp;
вместо этого, но учтите, что если вы бросите в Python подкласс из MyException
, это приведет к нарушению проблемы среза объектов .
Я не совсем уверен, что код на 100% правильный, в частности, я считаю, что подсчет ссылок правильный, но я могу ошибаться.
Примечание. Чтобы этот пример работал (%pythonabc
не сработал бы в противном случае), мне пришлось вызвать SWIG с -py3
. Это, в свою очередь, означало, что мне пришлось перейти на SWIG 2.0, потому что моя установленная копия Python 3.2 удалила некоторые устаревшие функции из C-API, который вызывал SWIG 1.3.40.