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 ) ; };
foo.py
Description: foo.py
_______________________________________________ Cplusplus-sig mailing list Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig