Based on Boost 1.88.0, boost::python Export Custom Exception and linked discussions I'm using the following to expose a custom C++ std::exception to python.
class cException : public std::exception
{
private:
const std::string m_message;
const int m_code;
public:
cException(const std::string &message, const int &code = -1);
virtual ~cException() throw();
const std::string &message() const;
const int &code() const;
};
template <class ExceptionCustomType>
class exception_ : public boost::python::class_<ExceptionCustomType>
{
private:
std::string m_exc_name;
public:
template <class ExceptionBaseType>
exception_(char const* excName, ExceptionBaseType excBaseType) : boost::python::class_<ExceptionCustomType>(excName, boost::python::no_init), m_exc_name(excName)
{
PyObject* excType = PyErr_NewException((std::string(boost::python::extract<std::string>(boost::python::scope().attr("__name__"))) + std::string(".") + this->m_exc_name).c_str(), excBaseType, 0);
boost::python::scope().attr(this->m_exc_name.c_str()) = boost::python::handle<>(boost::python::borrowed(excType));
boost::python::register_exception_translator<ExceptionCustomType>(
[ptr=excType](const ExceptionCustomType& e) {
boost::python::object excObj(boost::python::handle<>(boost::python::borrowed(ptr)));
excObj.attr("__repr__") = boost::python::str(e.message());
excObj.attr("code") = boost::python::object(e.code());
PyErr_SetString(ptr, e.message().c_str());
PyErr_SetObject(ptr, boost::python::object(e).ptr());
});
}
};
BOOST_PYTHON_MODULE(MyModule)
{
exception_<cException>("MyCustomException", PyExc_BaseException)
;
}
In Python then this works like this:
import MyModule
try:
MyModule.throw()
except MyModule.MyCustomException as e:
print("REPR = ", e.__repr__)
print("CODE = ", e.code)
But I don't like it that the attributes "__repr__" and "code" are set in the register_exception_translator. I would prefer to set them in the python exception definition itself. E.g.
BOOST_PYTHON_MODULE(MODULENAME)
{
exception_<cException>("MyCustomException", PyExc_BaseException)
.add_property("__repr__", &cException::message)
.add_property("code", &cException::code)
.def("some_additional_fancy_method", &cException::some_additional_fancy_method)
;
}
I have failed to implement this so far. Does anyone who is more familiar with boost::python know whether this can be realized at all or how?