[C++-sig] problems downcasting from C++ base class intrusive pointers to Python derived instances.
Lucio Moser
lucio at image-engine.com
Tue Feb 13 20:04:05 CET 2007
I created a simpleTest python module that provides two C++ classes,
named "Base" and "Derived". I also provide some methods that receive
and/or return intrusive_ptr for the "Base" class. If I instantiate
"Derived" class in Python and use it on the functions then it's ok. If I
create a class in Python that is derived from "Derived" class and use it
on the library functions then I lose the Python object and get only the
most derived C++ instance.
If I compile the same simpleTest library, using shared_ptr only then
everything works fine, like shown below.
Thank you for your help.
python test code using shared_ptr:
==================================
Python 2.5 (r25:51908, Nov 17 2006, 14:19:39)
[GCC 4.0.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from simpleTest import *
>>>
>>> class Derived2(Derived):
... pass
...
>>> a = Derived2()
>>> a
<__main__.Derived2 object at 0xb7f3743c>
>>> store(a)
>>> b = retrieve()
>>> b
<__main__.Derived2 object at 0xb7f3743c>
>>>
>>> upcast(a)
<__main__.Derived2 object at 0xb7f3743c>
>>>
python test code using intrusive_ptr:
=====================================
Python 2.5 (r25:51908, Nov 17 2006, 14:19:39)
[GCC 4.0.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from simpleTest import *
>>>
>>> class Derived2(Derived):
... pass
...
>>> a = Derived2()
>>> a
<__main__.Derived2 object at 0xb7edea7c>
>>> store(a)
>>> b = retrieve()
>>> b
<simpleTest.Derived object at 0xb7edae2c>
>>>
>>> upcast(a)
<simpleTest.Derived object at 0xb7edadbc>
>>>
simpleTest.cpp:
===============
#include <boost/python.hpp>
#include "register_intrusive_ptr_from_python.hpp"
class Base
{
public :
Base ( ) : m_numRefs( 0 )
{
}
/// Add a reference to the current object
void addRef() const {
m_numRefs ++;
}
/// Remove a reference from the current object
void removeRef() const {
m_numRefs --;
if ( m_numRefs == 0 )
{
delete this;
}
}
protected :
mutable unsigned int m_numRefs;
virtual ~Base()
{
}
};
class Derived;
#ifdef USE_SHARED_PTR
#include "boost/shared_ptr.hpp"
typedef boost::shared_ptr<Base> BasePtr;
typedef boost::shared_ptr<Derived> DerivedPtr;
#else // USE_SHARED_PTR
#include "boost/intrusive_ptr.hpp"
typedef boost::intrusive_ptr<Base> BasePtr;
typedef boost::intrusive_ptr<Derived> DerivedPtr;
/// Functions required to allow use of Base with boost::intrusive_ptr
inline void intrusive_ptr_add_ref( const Base *r )
{
r->addRef();
}
inline void intrusive_ptr_release( const Base *r )
{
r->removeRef();
}
#endif // USE_SHARED_PTR
class Derived : public Base
{
public :
Derived( ) : Base ( ), m_data( 0 )
{
}
protected :
int m_data;
};
BasePtr baseUpcast( BasePtr base )
{
return base;
}
struct GlobalsTest
{
static BasePtr g_object ;
static BasePtr retrieve()
{
return g_object;
}
static void store( const BasePtr &o)
{
g_object = o;
}
};
BasePtr GlobalsTest::g_object;
using namespace boost::python;
BOOST_PYTHON_MODULE(simpleTest)
{
typedef class_< Base, boost::noncopyable, BasePtr > BaseClass;
BaseClass baseClass("Base", no_init);
#ifndef USE_SHARED_PTR
boostPatch::register_intrusive_ptr_from_python_and_casts( (Base *)0,
BaseClass::metadata::bases() );
#endif
typedef class_< Derived, boost::noncopyable, DerivedPtr, bases< Base
> > DerivedClass;
DerivedClass derivedClass( "Derived" );
#ifndef USE_SHARED_PTR
boostPatch::register_intrusive_ptr_from_python_and_casts( (Derived
*)0, DerivedClass::metadata::bases() );
#endif
def("upcast", &baseUpcast);
def("store", & GlobalsTest::store );
def("retrieve", & GlobalsTest::retrieve );
implicitly_convertible<DerivedPtr, BasePtr>();
}
register_intrusive_ptr_from_python.hpp
=======================================
#ifndef REGISTER_INTRUSIVE_PTR_FROM_PYTHON_HPP
# define REGISTER_INTRUSIVE_PTR_FROM_PYTHON_HPP
# include <boost/python/object/class_metadata.hpp>
#include "intrusive_ptr_from_python.hpp"
namespace boostPatch {
// This function was based on
register_shared_ptr_from_python_and_casts() from class_metadata.hpp.
//
// Preamble of register_class. Also used for callback classes, which
// need some registration of their own.
//
template <class T, class Bases>
inline void register_intrusive_ptr_from_python_and_casts(T*, Bases)
{
using namespace boost::python::objects;
// Constructor performs registration
python::detail::force_instantiate(intrusive_ptr_from_python<T>());
//
// register all up/downcasts here. We're using the alternate
// interface to mpl::for_each to avoid an MSVC 6 bug.
//
register_dynamic_id<T>();
mpl::for_each(register_base_of<T>(), (Bases*)0,
(boost::add_pointer<mpl::_>*)0);
}
} // namespace boostPatch
#endif // INTRUSIVE_PTR_FROM_PYTHON_HPP
intrusive_ptr_from_python.hpp
==============================
// Based on boost header: shared_ptr_from_python.hpp.
#ifndef INTRUSIVE_PTR_FROM_PYTHON_HPP
# define INTRUSIVE_PTR_FROM_PYTHON_HPP
# include <boost/python/handle.hpp>
# include <boost/python/converter/shared_ptr_deleter.hpp>
# include <boost/python/converter/from_python.hpp>
# include <boost/python/converter/rvalue_from_python_data.hpp>
# include <boost/python/converter/registered.hpp>
# include <boost/intrusive_ptr.hpp>
namespace boostPatch {
using namespace boost;
using namespace boost::python;
using namespace boost::python::converter;
template <class T>
struct intrusive_ptr_from_python
{
intrusive_ptr_from_python()
{
converter::registry::insert(&convertible, &construct,
type_id<intrusive_ptr<T> >());
}
private:
static void* convertible(PyObject* p)
{
if (p == Py_None)
return p;
return converter::get_lvalue_from_python(p,
registered<T>::converters);
}
static void construct(PyObject* source,
rvalue_from_python_stage1_data* data)
{
void* const storage =
((converter::rvalue_from_python_storage<intrusive_ptr<T>
>*)data)->storage.bytes;
// Deal with the "None" case.
if (data->convertible == source)
new (storage) intrusive_ptr<T>();
else
new (storage) intrusive_ptr<T>(
static_cast<T*>(data->convertible)
);
data->convertible = storage;
}
};
} // namespace boostPatch
#endif // INTRUSIVE_PTR_FROM_PYTHON_HPP
More information about the Cplusplus-sig
mailing list