[C++-sig] Howto expose exception classes using boost.python?
Ryan Gallagher
ryan.gallagher at gmail.com
Tue May 9 20:05:47 CEST 2006
Ryan Gallagher <ryan.gallagher <at> gmail.com> writes:
>
> David Abrahams <dave <at> boost-consulting.com> writes:
>
> > Officially, it does require that exceptions be derived from
> > PyExc_Exception (I don't remember where that's documented), but
> > unofficially, you can throw anything :)
> >
>
> Does this really work in practice though? (With classes exposed through
> Boost.Python at least?)
>
> I was also working on this problem several months back, translating an
> exception class I exposed through Boost.Python using class_<> to raise an
> instance of the python class.
> ...
Here's the example output and code I had. Ignoring the hardcoded module
name this seemed to be a good solution, except that it doesn't quite work.
;-) (Also, I'd prefer to map std::runtime_error to Python's equivalent.)
(What I had recalled as a stack corruption was the infact just the
SystemError exception.)
Here's the python output I had:
Python 2.4.1c1 (#63, Mar 10 2005, 10:36:41) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pit
>>> dir(pit)
['RuntimeError', '__doc__', '__file__', '__name__', 'must_be_even', 'not_even_ex
ception', 'numeric_error']
>>> pit.must_be_even(6)
>>> pit.must_be_even(7)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
<class 'pit.not_even_exception'>: Numeric Error: must_be_even(int) 7 is not even
!
>>> try:
... pit.must_be_even(7)
... except pit.not_even_exception, e:
... print(e)
...
Numeric Error: must_be_even(int) 7 is not even!
>>> try:
... pit.must_be_even(7)
... except pit.numeric_error, e:
... print(e)
...
Traceback (most recent call last):
File "<stdin>", line 4, in ?
SystemError: 'finally' pops bad exception
>>> try:
... pit.must_be_even(7)
... except pit.numeric_error, e:
... print('1 ')
... except pit.not_even_exception, e:
... print('2')
...
2
For this wrapper code: ##########################################
#include <boost/python.hpp>
using namespace boost::python;
#include <stdexcept>
#include <iostream>
#include <string>
#include <boost/function.hpp>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
struct numeric_error : std::runtime_error
{
numeric_error(std::string const& msg)
: std::runtime_error(boost::str(boost::format("Numeric Error: %s") % msg))
{}
};
struct not_even_exception : numeric_error
{
not_even_exception(int i, std::string extra_message)
: numeric_error(boost::str( boost::format("%s %d is not even!")
% extra_message % i))
, m_i(i)
, m_msg(extra_message)
{}
int m_i;
std::string m_msg;
};
void must_be_even(int i)
{
if(i % 2 != 0)
{
throw not_even_exception(i, "must_be_even(int)");
}
}
namespace {
template<typename T>
std::string wrap_output(T const& exn)
{
return exn.what();
}
}
namespace wrap
{
template< typename CPP_ExceptionType
, typename X1 = ::boost::python::detail::not_specified
, typename X2 = ::boost::python::detail::not_specified
, typename X3 = ::boost::python::detail::not_specified
>
class exception
: public ::boost::python::class_<CPP_ExceptionType, X1, X2, X3>
{
public:
typedef ::boost::python::class_<CPP_ExceptionType, X1, X2, X3> base_type;
typedef exception<CPP_ExceptionType, X1, X2, X3> self;
// Construct with the class name, with or without docstring, and default
// __init__() function
exception(char const* name, char const* doc = 0)
: base_type(name, doc), m_exception_name(name)
{
init();
}
// Construct with class name, no docstring, and an uncallable
// __init__ function
exception(char const* name, no_init_t const& no_init_tag)
: base_type(name, no_init_tag), m_exception_name(name)
{
init();
}
// Construct with class name, docstring, and an uncallable
// __init__ function
exception(char const* name, char const* doc, no_init_t const& no_init_tag)
: base_type(name, doc, no_init_tag), m_exception_name(name)
{
init();
}
// Construct with class name and init<> function
template <class DerivedT>
inline exception(char const* name, init_base<DerivedT> const& i)
: base_type(name, i), m_exception_name(name)
{
init();
}
// Construct with class name, docstring and init<> function
template <class DerivedT>
inline exception( char const* name
, char const* doc
, init_base<DerivedT> const& i)
: base_type(name, doc, i), m_exception_name(name)
{
init();
}
private:
std::string get_module_qualified_name() const
{
return boost::str(boost::format("%s.%s") % "pit" % m_exception_name);
}
void init() const
{
using namespace boost;
function<void (typename base_type::wrapped_type const&)>
conversion_func = bind( &exception::to_python_exception
, *this, get_module_qualified_name(), _1);
::boost::python::register_exception_translator<typename
base_type::wrapped_type>(conversion_func);
}
static void to_python_exception( ::boost::python::object const& exn_type
, std::string const& exception_name
, typename base_type::wrapped_type const& exn
)
{
static const ::boost::python::to_python_value<typename
base_type::wrapped_type> convert_argument;
PyErr_SetObject(exn_type.ptr(), convert_argument(exn));
throw_error_already_set();
}
std::string const m_exception_name;
};
}
BOOST_PYTHON_MODULE(pit)
{
wrap::exception<std::runtime_error>("RuntimeError", init<std::string>())
.def("__str__", &std::runtime_error::what)
;
wrap::exception<numeric_error, bases<std::runtime_error> >
("numeric_error", init<std::string>())
.def("__str__", &wrap_output<numeric_error>)
;
wrap::exception<not_even_exception, bases<numeric_error> >
("not_even_exception", init<int, std::string>())
.def("__str__", &wrap_output<not_even_exception>)
;
def( "must_be_even"
, &must_be_even
, "raises not_even_exception if argument is not even.");
}
More information about the Cplusplus-sig
mailing list