As a newcomer to the list I'd like to know what the state of affairs is at present and, more importantly, what state sources are in and, even more importantly, what work most needs doing at present. Cheers, Marcelo _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig@python.org administrivia to: c++-sig-request@python.org _______________
Marcelo Cantos writes:
As a newcomer to the list I'd like to know what the state of affairs is at present and, more importantly, what state sources are in and, even more importantly, what work most needs doing at present.
A great question. First off, I should say that there are more than one person/group working on various aspects of binding Python to C++. I can only speak for myself, and to some extent, Paul Dubois. Back in the 1.4 days, we had achieved the state where Python's configure script had been hacked to include a --with-cxx option, so that you could specify a C++ compiler. If one was specified, it was used to compile main. This was so that global ctors would get run, and is a practical necessity. With 1.5, and it's new, simpler build-by-linking-to-libPython strategy, this is not the issue it once was. Users can fairly easily copy the 3 line main to their home dir, and just compile it with C++. So I am thinking that perhaps with 1.5, we don't necessarily need the hacks to configure.in. I do think there will be some issues here for Guido to ponder if he does decide to integrate the produce of this sig into the mainline release at some point. It certainly won't do to have a Python which links or dynloads C++ extensions, but which wasn't linked with a C++ compiler. But that's for Guido to sort out later. For now, I think we can get by without hacking configure.in, and just let people compile the 3 line main themselves. Now, onto method invocation. While I was at LLNL, I was able to construct a system that allowed you to get a python handle to a C++ object, and invoke C++ methods "directly" from the Python interpreter. For example, given: class foo /* details omitted */ public: PyObject *meth1( PyArgs *args ); }; you could do something like: f = new_foo() f.meth1( <whatever> ) and control flow would show up in foo:meth1() forthwith. Now, in that code (which is available via the LLNL Python distribution), foo had to be derived from PyObject, and PyObject had been hacked to be a class with a virtual destructor, so that the method invocation machinery could do RTTI downcasts. There are problems with this approach. Some people had expressed the desire to compose Python classes which "wrapped" preexisting classes. So, you might want to have something like this: // The orriginal article. class foo { public: void meth1( int x ); }; // A python wrapper class which calls the real thing, but pads it // going in and out, for the purpose of marshalling arguments and // return types to/from Python. class Pyfoo : public PyObject { foo *pfoo; public: PyObject *meth1( PyArgs *args ) { int x = <extract an int from args>; pfoo->meth1( x ); PyINCREF( PyNone ); return PyNone; } }; which is okay as far as it goes. Constructing Pyfoo from foo could be a conceivably automatable process. The problem is that if foo (the orriginal class), had ancestors, then constructing the Python wrapper class got hairy, because of the need for the wrapper to inherit from PyObject. If you wanted to build a wrapper for something which used inheritance, you might like to have a family of wrappers which bear the same relationship to each other as the original class you are wrapping bear to themselves. But now you would wind up with the PyObject getting stuffed into the inheritance hierarchy of the shadow class at multiple points, and that got real ugly. I could never quite get the down casting code to do every case perfectly. Plus, each of the shadow classes would have a pointer to its "slice" of the shadowed class, and ownership gets tricky, etc. All in all this scheme seemed to present thorny issues for this particularly straightforward prescription for building shadow class hierarchies. I had come up with a plan for a reformulation just in the closing days of my term at LLNL, but didn't get it done before leaving, and have had a range of other tasks here at LANL which delayed me in doing this until I had forgotten the details of the plan. However, I have recently been revitalizing the C++ extension stuff for use in Python 1.5 for work here, and so am starting to fuss with it some more. My current thinking is to ditch the required inheritance from PyObject, as well as the hack to make PyObject a class with a vtbl. I am worried that this will have bad effects which just hadn't shown up yet. Instead, I am thinking it may make more sense to just build a custom trampoline for each wrapped class (using templates), so that the type of the wrapped (registered) class can be recovered with a static cast instead of a dynamic cast. Or maybe if we are lucky, we might be able to avoid any casts... I should say that I am not actuallly 100% sure this will work. I will have to code it up to become positive, but I am hopeful. If it does work, it should help simplify the business of building shadow class wrappers. In any event, however the call is made, it is wrapped in a try/catch clause, so that C++ exceptions can be converted to Python exceptions. This is pretty nice. Then there is the portion of the project that relates to wrapping the Python C API in suitably constituted C++ classes. The idea here is that classes will be produced which correspond to the various functional categories of the Python C API. A Dict class, for example. Then, instead of calling the C API, you could instantiate a Python Dict object, and make calls to it, which would vector to the appropriate Python C API functions, but would have conventional C++ object semantics, maybe even look STLish, etc. And in this wrapper layer, any errors in the Python C API would be converted directly to C++ exceptions, which could be caught and handled using conventional C++ exception handling techniques. So, in this model, C++ use of the Python API would be much cleaner because you wouldn't have to do all this error code handling like you have to do when you call the Python C API from C. You would just use standard C++ exceptio9n handling, which cleans up the usage model dramatically. Paul Dubois has been working on this part of the wrapping, and that work is continuing to progress. As for what needs doing... Well, first of all, you give us feedback on how well this agenda meets your needs. Maybe there's things you need that aren't covered here. In terms of actually writing code, perhaps you could volunteer to help Paul wrap portions of the Python C API that he hasn't gotten to yet, or help write test software, or something like that. I am hoping to put together a 1.5 compatible release of what we have so far, sometime in the near future, where "near" is ill defined. -- Geoffrey Furnish email: furnish@lanl.gov LANL XTM Radiation Transport/POOMA phone: 505-665-4529 fax: 505-665-5538 "Software complexity is an artifact of implementation." -Dan Quinlan _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig@python.org administrivia to: c++-sig-request@python.org _______________
participants (2)
-
Geoffrey M. Furnish -
Marcelo Cantos