Hi,

   I have run into a small problem with my understanding of Python reference 
counts with Python/C++ hybrid objects.  I have an abstract interface called A 
and an associated factory interface called AFactory.  I am looking to implement 
various A+AFactory combinations in both C++ and Python.  I am using boosts 
intrusive smart pointers to make life a little more interesting.
   I have attached two files that illustrate my problem (they are a little 
long, sorry about that but I couldn't come up with a better example).  On 
linux, compile the cpp file with:

$ g++ foo.cpp -g -fPIC -shared -I/usr/include/python2.7 -lpython2.7 
-lboost_python -o module.so

   Run the py file with a single argument ('crash' to produce a segfault, 
anything else to have it work).

   What am I doing?  The abstract factory interface looks roughly like this 
(with most of the pointer stuff removed):

class AFactory {
  public:
    virtual A::Ptr newA() = 0;
};

   A single function that returns an intrusive pointer to the A interface.  I 
have an AFactoryWrap class that will forward the call to a python 
implementation.  I derive it in Python as AFactoryDerived (where ADerived is 
the python implementation of the A interface):

tmp = ADerived()
class AFactoryDerived( module.AFactory ):
    def __init__( self ):
        module.AFactory.__init__( self )

    def newA( self ):
        if( sys.argv[1] == 'crash' ):
            return ADerived() # segfaults
        else:
            return tmp        # works

   I then trigger the whole thing from a simple C++ function (that I also call 
from python, just to add to the fun):

void f2( const AFactory::Ptr &factory ) {
    A::Ptr a = factory->newA();
    a->f();
}

   Get a new instance of A from the factory.  Call a virtual function, 
implemented in Python, on it that prints "In f" on stdout.

   It works if the instance of ADerived is persistent (global variable) and 
segfaults if it is temporary (local to the newA call).  That makes me suspect 
that the problem is with Python reference counts and that I somehow need to 
maintain a reference from the c++ side so it doesn't get garbage collected.  
The boost::python::handle<> class would seem like the way to go, except I can't 
seem to find any decent examples of how to use it.

   Am I on the right track?  I would assume that the handle<> should be added 
to the Wrapper constructors, and the ptrRelease function enhanced to count C++ 
and Python references, but the syntax of creating a handle and obtaining python 
reference counts is escaping me.

   Thanks,

   -- Per.
#include <boost/checked_delete.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/python.hpp>

#include <iomanip>
#include <iostream>

namespace boost {
template< typename T >
void intrusive_ptr_add_ref( T *p ) {
    p->ptrAddRef();
}

template< typename T >
void intrusive_ptr_release( T* p ) {
    p->ptrRelease();
}
}

class PtrBase {
  public:
    PtrBase() : mReferences( 0 ) {}
    virtual ~PtrBase() {}

    inline void ptrAddRef() {
        ++mReferences;
    }

    virtual void ptrRelease() {
        if (--mReferences == 0) {
            boost::checked_delete(this);
        }
    }

    int references() const { return mReferences; }

  private:
    int mReferences;
};

class A : public PtrBase, public boost::python::wrapper<A> {
  public:
    typedef boost::intrusive_ptr<A> Ptr;

    virtual void f() = 0;
};

class AWrap : public A {
  public:
    typedef boost::intrusive_ptr<AWrap> Ptr;

    virtual void f() {
        this->get_override("f")();
    }
};

class AFactory : public PtrBase, public boost::python::wrapper<AFactory> {
  public:
    typedef boost::intrusive_ptr<AFactory> Ptr;

    virtual A::Ptr newA() = 0;
};

class AFactoryWrap : public AFactory {
  public:
    typedef boost::intrusive_ptr<AFactoryWrap> Ptr;

    virtual A::Ptr newA() {
        return this->get_override("newA")();
    }
};

void f1( const A::Ptr &a ) {
    a->f();
}

void f2( const AFactory::Ptr &factory ) {
    A::Ptr a = factory->newA();
    a->f();
}

BOOST_PYTHON_MODULE(module) {
    boost::python::class_< AWrap,
                           boost::noncopyable,
                           AWrap::Ptr >
        ( "A" )
        .def( "f", boost::python::pure_virtual(&A::f) )
        ;
    boost::python::implicitly_convertible<AWrap::Ptr,
                                          A::Ptr>();

    boost::python::class_< AFactoryWrap,
                           boost::noncopyable,
                           AFactoryWrap::Ptr >
        ( "AFactory" )
        .def( "newA", boost::python::pure_virtual(&AFactory::newA) )
        ;
    boost::python::implicitly_convertible<AFactoryWrap::Ptr,
                                          AFactory::Ptr>();

    def( "f1", f1 )
        ;

    def( "f2", f2 )
        ;
};

Attachment: foo.py
Description: foo.py

_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to