[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