[C++-SIG] design question - python extensions in C++

Brad Venner brad.venner at mindspring.com
Fri Feb 8 21:13:31 CET 1980


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<CppPyObject*>(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<ObjectProtocol*>(arg1);
  if(arg1->check("MyObject")) { 
    MyObject * myob = static_cast<MyObject*>(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.








More information about the Cplusplus-sig mailing list