From brad.venner at mindspring.com Fri Feb 8 21:13:31 1980 From: brad.venner at mindspring.com (Brad Venner) Date: Fri, 8 Feb 1980 15:13:31 -0500 Subject: [C++-SIG] design question - python extensions in C++ Message-ID: <000101a90625$b6ffc1c0$bbe1aec7@bathsheba.mindspring.com> Greetings, I would like to get some feedback regarding some design decisions on some work I have done on C++ extensions. The design philosopy in CXX_Extensions by P. Dubois is to create a unique type object for each C++ class. This is basically the approach used in CPython, where each python type has an associated type object. These type objects are created through the use of a templated class in which the class being defined is given as an argument to the template class. In my use of CXX, I found myself writing very similar code for each class I derived. For example, each class needed a getattr function that was identical to all the other getattr functions in every class. This can partially be addressed by creating an intermediate class that provides a default implementation of some of these common functions. In several posts in December, 1999, John Skaller proposed and outlined in detail an approach in which there is a single python type object that parallels a single C++ abstract base class. Since fools rush in where angels fear to tread, I have implemented some of the ideas outlined in J. Skaller's posts. The basic idea of the code is as follows: class CppPyObject: public PyObject { public: virtual int print() {throw TypeError("unimplemented_method");} } class CppPyType { // code to initialize static _typeobject struct PyTypeObject * type_object(); public: static bool check(PyObject * pyob) { return pyob && pyob->ob_type==type_object(); } static int print(PyObject* pyob) { try { if(check(pyob)) { static_cast(pyob)->print(); return 0; } // error handling & return } } CppPyType& cpp_type() { // singleton pattern static ObjectType type; return type; } class MyObject: public CppPyObject { private: MyObject() { ob_refcnt = 1; ob_type=cpp_type().type_object(); } public: PyObject* create(PyObject*, PyObject*); virtual int print() {cout << "It's alive";} } This approach seems to work, and reduces the amount of work required to implement the virtual functions, since Python-specific stuff can be moved into the default function, including error checking. As discussed by Skaller, methods can be added by overloading the virtual getattr function in the concrete class. Taking Skaller's hint, I used stl::map<> to implement the method tables. There are several design decisions now that I would like to get feedback on. One problem I'm running into now is how to safely convert PyObject*'s to the appropriate and specific C++ implementation. In the CXX approach, there would be a static function MyObject::check. Again, this is similar to the CPython approach, which checks to see if the address of the type object since one-to-one correspondence with the type object. In the Skaller approach, the type-checking routine has to be (can be?) implemented within the C++ iterface class CppPyObject. The design I am attempting is to use class-specific identifiers (static strings) in creation-type classes and the use a virtual "check" function to perform type checking using string comparison. For example, PyObject* function(PyObject *self, PyObject* args) { Tuple a(args); // check length PyObject* arg1 = a[0].ptr(); ObjectProtocol * arg1 = static_cast(arg1); if(arg1->check("MyObject")) { MyObject * myob = static_cast(arg1); // do useful stuff } else throw TypeError("Function requires MyObject argument"); } Does anyone have a better idea or approach that I should consider. A second problem is how to check for to see if a PyObject* implements a particular Python/C++ interface. It would be nice to write C++ functions that did not require conversion to a specific creation-type but instead could check to see if a creation-type implemented a particular interface, and then cast the PyObject* to a pointer to the interface. Python checks for it's interface "types" (number, sequence, mapping) by checking for a null-pointer in the type object. A C++ equivalent is needed but I don't really see how to do it. Anyway, I appreciate any feedback on these or other issues. My e-mail address at work is venner.brad at epa.gov, at home is vennerbc at mindspring.com.