Hi everyone.<br><br>I&#39;m trying to use Boost.Python to expose some of my C++ classes and I encountered an odd behaviour of the to_python_indirect result converter for intrusive_ptr with specific classes.<br>Here is the deal:<br>
<br>I&#39;m using a component-based design for my classes, meaning an aggregation of objects with different features. These objects are ref-counted, and if they are in the same aggregation, they share the same lifetime.<br>

That is to say, as long as one of the components in the &quot;ring&quot; has a strictly positive reference counter, every component in the ring survive even if their ref-count drops to zero.<br>I use boost::intrusive_ptr to manipulate them easily.<br>

<br>There is the basic interface:<br><br>class Component<br>{<br>    void AddRef();<br>    void Release();<br>    unsigned int GetRefCount();<br>    bool AddComponent(Component*);<br>    bool RemoveComponent(Component*);<br>

    Component* ComponentIterator(Component*);    //--&gt;Used to parse the ring.<br>    <br>    virtual some_virtual_fn();<br>};<br><br>To avoid lifetime problems when manipulating an object both from C++ and python side, i want python to manipulate components only through boost::intrusive_ptr. <br>
<br>There is the exposition code:<br>
<br>//--&gt;Object wrapper<br>struct Component_wrapper : SandBoxProj::Component, bp::wrapper&lt; SandBoxProj::Component &gt; {<br><br>    Component_wrapper( ) :Component( ), bp::wrapper&lt; SandBoxProj::Component &gt;(){}<br>
<br>
    virtual some_virtual_fn() {<br>          //overriding code...<br>    }<br>    <br>    default_some_virtual_fn() {<br>       Component::some_virtual_fn();<br>    }<br>};<br><br>//--&gt;Some declarations that boost.python needs to manipulate boost::intrusive_ptr<br>

namespace boost { namespace python {<br><br>   template &lt;class T&gt; struct pointee&lt; intrusive_ptr&lt;T&gt; &gt;   //--&gt;pointee struct for intrusive_ptr holder<br>     {<br>       typedef T type;<br>     };<br><br>
 struct make_owning_intrusive_ptr_holder    //--&gt;ResultConverter to make an intrusive_ptr from a raw one, inspired from to_python_indirect.hpp:79<br>  {<br>    template &lt;class T&gt;<br>    static PyObject* execute(T* p)<br>

    {<br>      typedef objects::pointer_holder&lt;boost::intrusive_ptr&lt;T&gt;, T&gt; holder_t;<br>      <br>      boost::intrusive_ptr&lt;T&gt; ptr(p);<br>      return boost::python::objects::make_ptr_instance&lt;T, holder_t&gt;::execute(ptr);<br>

    }<br>  };<br>  <br>  struct return_by_intrusive_ptr    //--&gt;corresponding ResultConverterGenerator<br>  {<br>    template &lt;class T&gt;<br>    struct apply<br>    {<br>      typedef to_python_indirect&lt;T, make_owning_intrusive_ptr_holder&gt; type;  //--&gt; here is the usage of to_python_indirect<br>

    };<br>  };<br><br>}}<br><br>bp::class_&lt; Component_wrapper,boost::intrusive_ptr&lt;Component_wrapper&gt;,boost::noncopyable &gt;( &quot;Component&quot;, bp::init&lt; &gt;() )    <br>    .def( &quot;AddComponent&quot;, &amp;Component::AddComponent, ( bp::arg(&quot;arg0&quot;) ) )    <br>

    .def( &quot;ComponentIterator&quot;,&amp;Component::ComponentIterator, ( bp::arg(&quot;arg0&quot;) ), bp::return_value_policy&lt; bp::return_by_intrusive_ptr &gt;() )    //--&gt;this class uses the converter.<br>    .def( &quot;some_virtual_fn&quot;,&amp;Component::some_virtual_fn,&amp;Component_wrapper::default_some_virtual_fn)<br>

    .def( &quot;GetRefCount&quot;,&amp;Component::GetRefCount)    <br>    .def( &quot;RemoveComponent&quot;,&amp;Component::RemoveComponent, ( bp::arg(&quot;arg0&quot;) ) ) ;<br>    <br><br>And finally there is a scripts that uses this class and its output:<br>

<br>&gt;&gt;&gt;from pythonbinder import *<br>&gt;&gt;&gt;import gc<br><br>&gt;&gt;&gt;Comp1=Component()<br>&gt;&gt;&gt;Comp2=Component()<br>&gt;&gt;&gt;print Comp2.GetRefCount()<br>1                                                                                   //--&gt;Ok, I manipulate an intrusive_ptr<br>

&gt;&gt;&gt;Comp2.AddComponent(Comp1)<br>&gt;&gt;&gt;iter=None<br>&gt;&gt;&gt;iter=Comp1.ComponentIterator(iter)<br>&gt;&gt;&gt;print iter<br>&lt;pythonbinder.Component object at 0x026DA630&gt;           //--&gt;Ok, There is my C++ object<br>

&gt;&gt;&gt;iter=Comp1.ComponentIterator(iter)<br>&gt;&gt;&gt;print iter<br>&lt;pythonbinder.Component object at 0x026DA600&gt;<br>&gt;&gt;&gt;print iter.GetRefCount()<br>1                                                                                  //--&gt;What the ? iter is supposed to be an intrusive ptr. After a look at to_python_indirect, it seems that iter==Comp2, the python object wrapping the intrusive_ptr was just addref&#39;ed. Ok, fair enough.<br>
<br>
&gt;&gt;&gt;iter=Comp1.ComponentIterator(iter)<br>&gt;&gt;&gt;print iter<br>None<br>    //--&gt;Now the troubles begin.<br>&gt;&gt;&gt;del Comp2<br>&gt;&gt;&gt;gc.collect()    //--&gt;ensure deletion, Comp2 is still alive from the C++ side, because Comp1 is alive.<br>

&gt;&gt;&gt;iter=Comp1.ComponentIterator(iter)  //May crash<br>&gt;&gt;&gt;print iter<br>    //--&gt;if we made it through, a weak_ptr to some random structure.<br><br>After that, i figured out that i never went through the ResultConverters i declared. Let&#39;s look at the execute function from to_python_indirect, <br>

<br> template &lt;class U&gt;<br>    inline PyObject* execute(U const&amp; x, mpl::false_) const<br>    {<br>        U* const p = &amp;const_cast&lt;U&amp;&gt;(x);<br>        if (is_polymorphic&lt;U&gt;::value)                                              //--&gt;My type is polymorphic<br>

        {<br>            if (PyObject* o = detail::wrapper_base_::owner(p))           //--&gt;Ok, that&#39;s why I have a refcount of 1, no matter how many python intrusive_ptr wrappers I seem to have<br>                return incref(o);<br>

        }<br>        return MakeHolder::execute(p);                                         //--&gt;There is the call to my result converter.<br>    }<br><br>What happen is that after deletion of the last python wrapping of the intrusive_ptr, which is o in the code above, the Component_wrapper, which is partly a python object, is not deleted (the component ring still holds it), but it still reference its last owner which is the last instance_holder that just got deleted, so the object returned is a random memory garbage.<br>
<br>Changing the holder type fromintrusive_ptr&lt;Component_wrapper&gt; to intrusive_ptr&lt;Component&gt; solves the above problems but then it&#39;s not possible to use the class Component_wrapper as a base in python anymore.<br>
I tried to create a class derived from Component in python and the constructor of Component_wrapper was not called. (purpose is overriding virtual functions in python)<br>
<br>I wondered if I&#39;m going the wrong way, or if I discovered a bug. <br>It may be a limitation of the library, which cannot understand that an object survived the deletion of its last holder.<br><br>Maybe if i made my own version of to_python_indirect but removing the is_polymorphic case, it would work?<br>
Is there a fundamental reason to this test, other than saving memory?<br>
<br><br>