[C++-sig] Re: PyObject * self member in class wrappers.

Dirk Gerrits dirk at gerrits.homeip.net
Thu Jan 9 20:29:43 CET 2003


David Sankel wrote:
> When a python class is being inherited from a c++
> class, one must write a c++ wrapper around the c++
> class.
> 
> This wrapper must have a pointer to a PyObject called
> self.
> 
> I was wondering, what exactly is this PyObject?

Python provides a 'Python/C API' that allows you to create C/C++ 
extension modules and to embed the Python interpreter in your C/C++ 
programs. Pointers to PyObjects are the Python/C API's way to use Python 
objects from the C/C++ side.

Boost.Python uses advanced C++ techniques to provide a much easier 
interface to the Python/C API. One manifestation of this is in 
python::object which is basically a (very) convenient, high-level 
wrapper around PyObject*. A lower level wrapping around PyObject* is 
python::handle.

> Say I have the following scenario:
> 
> class Cloneable
> {
> public:
>   virtual Cloneable * Clone()=0;
> };
> 
> class PythonCloneable
> {
> public:
>   PyObject * self;
>   PythonCloneable( PyObject * self_ ) : self(self_) {}
> 
>   Cloneable * Clone()
>   {
>      PythonCloneable * theCopy;
>      //... What goes here?
>      return theCopy;
>   }
> };
> 
> I would like Clone to return a copy (not instance) of
> the original.  This way, both the clone and the
> original could be modified independently.  How would I
> manipulate the self member to do this?

Well I'm afraid that I'm not too sure myself. Here's what I thought up:


You can wrap a PyObject* in a python::handle<>, create a python::object 
out of it and then copy that. Calling the copy's ptr() method gives you 
it's PyObject*.

I'm just not totally sure whether you should construct the 
python::handle from self or from python::borrowed(self). I think it is 
the latter, resulting in:

Cloneable * Clone()
{
     object theObject( handle(borrowed(self)) );
     object theCopy(theObject);
     return new PythonClonable(theCopy.ptr());
}


The problem with this however, is that theCopy falls out of scope at the 
end of the function body so the clone will have a dangling self pointer. 
Very bad!

To fix this, I think you could do this:

class PythonCloneable
{
   object self_object;
public:
   PyObject* self;
   PythonCloneable( PyObject * self_ ) : self(self_) {}
   PythonCloneable( handle<> self_ ) : self_object(self_)
   { self = self_object.ptr(); }

   Cloneable* Clone()
   {
     object theObject( handle(borrowed(self)) );
     object theCopy(theObject);
     return new PythonClonable(theCopy);
   }
};

I believe that should work. But I have not tested this. And it rather 
feels like a hack.

I can't help but think that Boost.Python should provide a better way to 
do this. If it is there, then I just didn't think of it. If not, then I 
think it should be added, whatever 'it' may be.

Regards,
Dirk Gerrits






More information about the Cplusplus-sig mailing list