[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