[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