[C++-sig] Re: Wrapper for exception translation

David Abrahams dave at boost-consulting.com
Thu Jul 10 19:13:30 CEST 2003


Gottfried.Ganssauge at HAUFE.DE writes:

> ...
>> 
>> > Currently, in order to wrap user defined exceptions you need to
>> >
>> > - allocate a python exception using PyErr_NewException
>> 
>> Well, no.  I don't see any call to PyErr_NewException in
>> libs/python/test/exception_translator.cpp
>
> Right, you don't need to if you are content with RuntimeError
> exceptions.

or any other pre-packaged exception type provided by Python.

> My objective was another one: I wanted to create module exceptions
> that are derived from the python "Exception" class.
>> 
>> >   This function needs to be parametrized using a specific naming
>> >   convention
>> 
>> It's not clear to me what you're referring to.  Are you sure you mean
>> "naming convention"?
> Yes.
> From the documentation to PyErr_NewException:
>
> 	... The __module__ attribute of the new class is set to the first part
> 	(up to the last dot) of the name argument, and the class name is set to
> 	the last part (after the last dot).

...yeah, that's a naming convention, but I don't see how that affects
the parameterization of any function.

>> >   you also need to find a place where this exception instance lives
>> >   and is accessible to the exception translator
>> 
>> I don't know what you mean here.  For one thing, first "this" is a
>> function and now "this" is an exception instance.
> "this function" meant PyErr_NewException which creates a new exception
> instance.
>
> The exception instance (from now on called "e") created by
> PyErr_NewException needs to live somewhere because in order to
> signal a python exception you must hand e to
> PyErr_SetString/PyErr_SetObject.

OK, I understand that.  For the record, Python exceptions are
"raised", not "signalled".

>> > - define an exception translator which signals the exception
>> > defined above
>> 
>> What do you mean by "define an exception translator"?
> A translation function in the sense of register_exception_translator.

OK.

>> What do you mean by "signals the exception"?
>
> Calling PyErr_SetString/PyErr_SetObject.

OK.

>> > A reference to the translated_exception_handler is stored within
>> > the class dictionary of the newly allocated python exception in
>> > order to keep the translator alive.  The
>> > translated_exception_handler provides the exception translator to
>> > be registered with the library.
>> 
>> OK, OK, wait.  I was beginning to like where this was going, (I like
>> what you're accomplishing) but this all sounds *way* too complex for
>> that purpose.  Especially the "creation of an anonymous class" that
>> you do in the code here looks like a messy hack likely to break at
>> some point.  Why is such a complicated mechanism needed?
>
> You're probably right.
>
> What I wanted to achieve was the following:
> Create an instance of a translated_exception_handler<> t which will be
> registered with the library by calling
> register_exception_translator<>().

Sorry to be difficult, but I'm going to contradict you.  You didn't
want any of that.  What you wanted was to create a new Python
exception type in the module which would correspond to a particular
C++ exception type and would be raised at the boundary with Python
whenever a wrapped function threw the C++ exception.  Right?

> Normally t would be destructed by the end of exception_<>() thereby
> leaving a registration to a non-existing translator - very bad
> karma.

I don't understand any of this.  All you have to pass to
register_exception_translator is a function pointer, and functions
live forever.  So where's the lifetime issue?

> Because I didn't like to have an additional container for all
> translated_exception_handler<> instances, I chose to store t within
> e's class dictionary.  At that moment it seemed to be the easiest
> way to create a wrapper class using class_<> instead of making a
> true python class by hand (and it even works!).  You are right, it
> is way too complex, I will change it to the mechanism I used in
> opaque_pointer_converter.

I don't know what that is, but before you consider doing something
which is again too complex, please consider:

    template <class E>
    struct translate_exception
    {
       static char const* msg;
       static object python_exception_type;

       static void handler(E const&)
       {
            PyErr_SetString(python_exception_type.ptr(), msg.c_str());
       };

       translate_exception(char const* python_exception_name, char const* msg_)
       {
            // need fancier name manipulation here
            python_exception_type
                = object(
                    handle<>(PyErr_NewException(name)));
            msg = msg_;
            register_exception_translator<E>(handler);
       }
    };


Simple.

>> > It uses a generic method to obtain an exception description
>> > by calling a function get_exception_description<>() which has a
>> > default implementation in the detail namespace.  Finally the python
>> > exception itself is made available in the module's namespace under
>> > the name given.
>> >
>> > get_exception_description<>() currently returns a const char * which
>> > is passed to PyErr_SetString(). It might be worthwile to make it
>> > return an object which then could be passed to
>> > PyErr_SetObject(). This modification would make it possible to pass
>> > an instance of a class which wraps the exception caught, which again
>> > would make the exception instance itself available to python
>> > programs.
>> >
>> > Another enhancement could be the generation of derived python
>> > exceptions, an area in which I have no experience yet. This could be
>> > by an additional template parameter to the exception_<>() function
>> > which defaults to the allocation with PyErr_NewException().
>> >
>> > What do you think?
>> 
>> I think I understand this much:
>> 
>>   your code provides the ability to easily define new Python
>>   exception types corresponding to particular C++ exception types.
>> 
>> Bravo to that; I'd like to get this capability into the library.
>> 
>> I do think the implementation is needlessly convoluted.  Just by
>> inspection it looks to me like you make heavy use of a dictionary 
>> 
>>         // class dictionary for the new exception class
>>         dict d;
>> 
>> which later simply goes out of scope.  What's the point of that?
>> Have I missed something?
> It doesn't simply go out of scope. Before going out of scope it is passed to
> PyErr_NewException as the new exception's class dictionary. 

Ah, yes, I see it now.  I did miss that, sorry.

> Ok then,
>
> I'll make a redesign ...
> Thnx for your feedback.

Cool!  Looking forward to it.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com





More information about the Cplusplus-sig mailing list