One of the problems faced with creating boost python wrappers is that sooner or later you can run into the situation where you need to support extra exception messages which are being thrown from C++ code but are not understood by python or the python wrapper. So this is a guide to support these extra exceptions.
You will know that you have come across this issue when you see the following being produced in python. In this case the Example function in C++ throws a MyException class which provides an error message which in its current state just isn't really useful to try to trace the problem.
>>> import MyClass_py
>>> obj = MyClass_py.MyClass()
>>> obj.Example()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: unidentifiable C++ exception
>>>
In previous posts about creating python wrappers and supporting operator overloading. I have been using much the same example code. In this case it has been changed to add the extra exception. So the C++ code now looks like this.
class MyException {
public:
MyException(const std::string str) {
m_str = str;
}
~MyException() {
}
std::string GetMessage() const {
return m_str;
}
private:
std::string m_str;
};
class MyClass {
public:
MyClass() { }
~MyClass() { }
std::string Example() {
throw(MyException("Hello World"));
}
};
To get the exception method supported we need to add an exception converter to our python module using a boost python macro register_exception_translator and an extra C/C++ function in our wrapper. Note that the C++ function should not be in the BOOST_PYTHON_MODULE section but the register_exception_translator is. This is how it should look to handle the exception.
Also take note that because the MyExceptionTranslator requires the argument to be a const the functions that you call must also be marked as const or it will not compile. In this case the function for "MyException::GetMessage" must be declared as "std::string MyException::GetMessage() const" to say that it does not modify the contents of the MyException this is because it is meant to be constant.
static void MyExceptionTranslator(const MyException &err) {
PyErr_SetString(PyExc_UserWarning, err.GetMessage().c_str());
}
BOOST_PYTHON_MODULE(MyClass_py)
{
register_exception_translator<MyException>(&MyExceptionTranslator);
class_<MyClass>( "MyClass", init<>() )
.def("Example", static_cast< std::string (MyClass::*)() > (&MyClass::Example) )
;
}
When compiled and run from python we now get the following instead of the useless error message above
>>> import MyClass_py
>>> obj = MyClass_py.MyClass()
>>> obj.Example()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UserWarning: Hello World
I have included a full working example.
#include <string>
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/init.hpp>
#include <boost/python/operators.hpp>
#include <boost/python/call_method.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/implicit.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/overloads.hpp>
#include <boost/ref.hpp>
#include <boost/utility.hpp>
using namespace boost::python;
class MyException {
public:
MyException(const std::string str) {
m_str = str;
}
~MyException() {
}
std::string GetMessage() const {
return m_str;
}
private:
std::string m_str;
};
class MyClass {
public:
MyClass() { }
~MyClass() { }
std::string Example() {
throw(MyException("Hello World"));
}
};
static void MyExceptionTranslator(const MyException &err) {
PyErr_SetString(PyExc_UserWarning, err.GetMessage().c_str());
}
BOOST_PYTHON_MODULE(MyClass_py)
{
register_exception_translator<MyException>(&MyExceptionTranslator);
class_<MyClass>( "MyClass", init<>() )
.def("Example", static_cast< std::string (MyClass::*)() > (&MyClass::Example) )
;
}
The above example can be compiled with
g++ -shared -Wall MyClass_py.cpp -o MyClass_py.so -I/usr/include/python2.6/ -lboost_python -lpython2.6