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 [email protected] http://mail.python.org/mailman/listinfo/cplusplus-sig
