[C++-sig] Inheriting from a python base class/exception translation

Jim Bosch talljimbo at gmail.com
Tue Nov 8 16:55:00 CET 2011


On 11/07/2011 09:19 PM, Nick Rasmussen wrote:
> I'm cleaning up some of our python bindings for public release as
> part of the openexr distribution, and wanted to update the python
> bindings of our exception library, Iex, to use boost::python.
>
> The first thing I did was the trivial binding of the class hierarchy
> out to python, and performed register_exception_translator<>  for each
> type with a translator like:
>
> void
> translateExc(const BaseExc&exc)
> {
>      PyErr_SetObject(pytype,boost::python::object(exc).ptr());
> }
>
> where pytype is the pointer to the type object for BaseExc's
> python binding.  This allows instances of the exception types
> to be raised in c++, translated into python, and caught.
>
> However, the problem I'm having is this: to allow instances of
> the exception types to be raised in python (or in fact to have
> a try/except/else block finalize properly), the python objects
> need to be derived from the python Exception type or one of
> its subclasses.
>

This is a tough problem, and one I think Boost.Python wasn't designed to 
do, since most of it was in place before one could even derive from 
Python builtins using the C-API.  It's definitely something I'd like to 
fix in the future.

In the meantime, I've essentially followed the "parallel pure-Python 
exception hierarchy" method, but you can do some metaprogramming tricks 
to keep from having to actually build the Python hierarchy yourself.  In 
other words, write some Python code that inspects your 
Boost.Python-wrapped C++ exception hierarchy and calls type(...) to make 
corresponding Python types that derive from Exception.  The pure-Python 
exceptions can then be set as class members of the Boost.Python-wrapped 
C++ exceptions, so your translate functions can do something like this:

void translateExc(const BaseExc & exc) {
     bp::object wrappedExc(exc);
     bp::object pyExcType = wrappedExc.attr("_pyExcType")
     // assume the constructor for the Python exception accepts a
     // C++ exception; useful if the C++ exception has data
     bp::object pyExc = pyExcType(wrappedExc);
     PyErr_SetObject(pyExcType.ptr(), pyExc.ptr());
}

To finish it off, you could add a from-Python converter that converts 
the pure-Python exception back to the appropriate C++ exception when 
it's passed to Boost.Python-wrapped functions, but that isn't always 
necessary.

I don't have any code to do the metaprogramming bits on hand, but I'm 
happy to help further if you have questions.

By the way, I do hope someone else has an even better idea - my approach 
is more complicated than it ought to be, but at least it's all overhead 
rather than something that scales with the number of exceptions you have.

Good luck!

Jim


More information about the Cplusplus-sig mailing list