[C++-sig] problems downcasting intrusive_ptr.
Lucio Moser
lucio at image-engine.com
Tue Feb 13 19:38:25 CET 2007
Yes, that's what I wanted. I have a core C++ library that works using
intrusive_ptr everywhere. And I'm binding that to Python.
The downcasting to a Python class works if I compile my test case using
shared_ptr. There should be a way to do it with intrusive_ptr.
I appreciate your help. The code that solves the previous problem is
listed below. It also shows the new problem. To compile it for
shared_ptr, just define USE_SHARED_PTR. The functions upcast and
store/retrieve will work correctly for shared_ptr and not for
intrusive_ptr (shown below).
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 )
{
}
int test1()
{
return this->m_data;
}
int test2( DerivedPtr d )
{
return (this->m_data == d->m_data);
}
protected :
int m_data;
};
BasePtr factory( void )
{
return BasePtr( new Derived() );
}
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
derivedClass.def( "test1", &Derived::test1 );
derivedClass.def( "test2", &Derived::test2 );
def("factory", &factory);
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
Roman Yakovenko wrote:
> On 2/13/07, Lucio Moser <lucio at image-engine.com> wrote:
>
>> Thanks a lot Roman! It solved the problem.
>>
>
> Please post your solution. Thus it will be available to other people too.
>
>
>> Now I face another one. If I derive a python class "Derived2" from the
>> "Derived" C++ class and send to "upcast" function below then I receive a
>> Derive object and not Derive2. I imagined that if I created an
>> intrusive_ptr version for shared_ptr_arg_to_python it would solve the
>> problem. But it didn't. Do you have another tip on how to downcast for
>> python-derived classes too?
>>
>
> I don't completely understand you. You have class defined in C++ and another one
> defined in Python, derived from the first one, right? Now you want to
> downcast to
> the class defined in Python? If so you cannot do this. You can get
> reference to relevant Python object and than using Boost.Python to get
> access to desired
> functionality.
>
>
More information about the Cplusplus-sig
mailing list