RE: [C++-sig] Re: Wrapper for exception translation
...
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. 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"?
From the documentation to PyErr_NewException: ... The __module__ attribute of the new class is set to the first
Yes. part (up to the last dot) of the name argument, and the class name is set to the last part (after the last dot).
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.
- 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.
What do you mean by "signals the exception"? Calling PyErr_SetString/PyErr_SetObject.
- register the exception translator with the library
This I understand to mean something like:
register_exception_translator<error>(&translate);
My facility allows to do this with only one function call "exception_<Exception type> (name)" within the module init function. It will allocate a python exception
Do you mean it will create a Python subclass of Exception? Or is it something else? I guess the docs for PyErr_NewException are a little unclear on this point, but I think it's what I said.
Yes, that's what I mean.
and store it within an instance of the translated_exception_handler<> class. The name for that exception is taken from the name parameter and the current scope according to the naming convention documented in the manual page for PyErr_NewException.
Hmm, I didn't know about PyErr_NewException, but it looks useful.
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<>(). Normally t would be destructed by the end of exception_<>() thereby leaving a registration to a non-existing translator - very bad karma. 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.
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. I was under the impression that it wouldn't have one otherwise. Ok then, I'll make a redesign ... Thnx for your feedback.
-- Dave Abrahams Boost Consulting www.boost-consulting.com
Cheers, Gottfried
Gottfried.Ganssauge@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
participants (2)
-
David Abrahams -
Gottfried.Ganssauge@HAUFE.DE