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

David Abrahams dave at boost-consulting.com
Fri Jan 10 15:35:19 CET 2003

Dirk Gerrits <dirk at gerrits.homeip.net> writes:

> 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. 

copying an instance of class object just does what it does in Python;
it makes a copy of the reference.

> 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());
> }

There's no way this would work.  It just makes a new C++ object which
refers to the same Python object.

> 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!

That's the least of the problems.

> 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.

No coincidence; you can't get there that way.

> 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.

It's an important use case, but you can't get around the ownership
issue.  Either the C++ object owns the Python object or it's the other
way around.  Conceivably we could hook up Python's cycle GC to this
and allow them to keep references to one-another, but if you start
passing around raw Cloneable*s instead of proper smart pointers, it's
almost certainly going to get messed up anyway.

The only thing I think Boost.Python can reasonably provide is a way to
do it with Clone() returning boost::shared_ptr... and we already
provide that.

                       David Abrahams
   dave at boost-consulting.com * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

More information about the Cplusplus-sig mailing list