[C++-sig] Problems with custom smart pointers and multiple inheritance

Chris Christophe.Gratin at adcis.net
Wed Mar 22 20:21:05 CET 2006


Thanks for your help.

I did as you suggest, and the PassAPointer() function works fine now, but... it crashes on the next 
line when trying to invoke this function once again. Note that it also crashes on exit if I do not 
perform any other operation.

 From examining the stack I found out that it seems it is "destroying" my C++ class instance too early.
I've attached the state of the stack at the moment when the memory of my C++ class instance is 
released (it does not crash at this point but only later when it tries to access the deleted object)

I also noted that my destructor (virtual ~MyClass()) is not called as I would have expected when 
delete x is invoked in:
template<class T> inline void checked_delete(T * x)
{
     // intentionally complex - simplification causes regressions
     typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
     (void) sizeof(type_must_be_complete);
     delete x;
}

(at this point T = MyNamespace::MyBaseA)

Do you have any explanation ?

Chris.

> Ahh yes,  I forgot -- I think you also need to register your own 
> from_python conversion.  This is bascially the code I use (basically 
> copied from boost python -- used for shared_ptr from_python conversions, 
> I think).
> 
> template <template <typename> class Ptr>
> struct PtrFromPython {
>      template <typename T>
>      PtrFromPython(T *) {
>          converter::registry::insert(&convertible<T>, &construct<T>,
>                                      type_id<Ptr<T> >());
>      }
>    private:
>      template <typename T>
>      static void *convertible(PyObject *p) {
>          if (p == Py_None)
>              return p;
>          void *result = converter::get_lvalue_from_python
>              (p, converter::registered<T>::converters);
>          return result;
>      }
> 
>      template <typename T>
>      static void construct(PyObject* source, converter::
>                            rvalue_from_python_stage1_data* data) {
>          void* const storage = ((converter::rvalue_from_python_storage
>                                  <Ptr<T> >*)data)->storage.bytes;
>          // Deal with the "None" case.
>          if (data->convertible == source)
>              new (storage) Ptr<T>();
>          else {
>              Ptr<T> ptr = Ptr<T>(static_cast<T*>(data->convertible));
>              new (storage) Ptr<T>(ptr);
>              // do object identity here.
>          }
>          data->convertible = storage;
>      }
> };
> 
> You'll need to instantiate one of these for each of your wrapped 
> classes.  That is, PtrFromPython<MyPointer>((MyBaseA *)0).  (You can 
> make the interface prettier, it's this way in my code for other reasons.)
> 
> Also, I do this (among other things) in a custom def visitor.
> 
> Alex
> 
> 
> Chris wrote:
> 
>>Thanks for your answer
>>
>> > If you use boost::shared_ptr, it sort of works.  With any other smart
>> > pointer you can't really do it as far as I know.  There's some ongoing
>> > discussion about this.  I would like to fix it.
>>
>>This could be a solution, but will it still work if I define MyPointer<T> as a class derived from 
>>shared_ptr<T>. That's what I am eventually trying to do, but I still can't call my PassAPointer() 
>>function. I get the following runtime error (my source code is pasted below):
>>
>>     print c1.PassAPointer(c1)   # fails
>>Boost.Python.ArgumentError: Python argument types in MyBaseA.PassAPointer(MyClass, MyClass) did not 
>>match C++ signature:
>>     PassAPointer(class MyNamespace::MyBaseA {lvalue}, class MyNamespace::MyPointer<class 
>>MyNamespace::MyBaseA>)
>>
>>
>>Note that it works perfectly if I simply "#define MyPointer shared_ptr" instead of using my derived 
>>template class.
>>
>>I guess there is surely something simple to add to make it work. At least I hope so.
>>
>>Chris.
>>
>>-----------
>>namespace MyNamespace
>>{
>>#ifdef USE_DERIVED
>>   template <class T> class MyPointer: public shared_ptr<T>
>>   {
>>   public:
>>     template<class Y> explicit MyPointer(Y* p): shared_ptr<T>(p) {}
>>
>>     template <class Y> MyPointer(const shared_ptr<Y>& p): shared_ptr<T>(p) {}
>>
>>     MyPointer() {}
>>   };
>>#else
>>   #define MyPointer shared_ptr
>>#endif
>>
>>   class MyBaseA
>>   {
>>   public:
>>     typedef MyPointer<MyBaseA> Ptr;
>>
>>     virtual std::string PassAPointer(MyBaseA::Ptr value) = 0;
>>   };
>>
>>   class MyBaseB
>>   {
>>   public:
>>     typedef MyPointer<MyBaseB> Ptr;
>>
>>     virtual std::string PassBPointer(MyBaseB::Ptr value) = 0;
>>   };
>>
>>   class MyClass:
>>     public MyBaseA,
>>     public MyBaseB
>>   {
>>   public:
>>     typedef MyPointer<MyClass> Ptr;
>>
>>     std::string PassAPointer(MyBaseA::Ptr value)
>>     {
>>       return (boost::format("PassAPointer - %1%") % int(this)).str();
>>     }
>>
>>     std::string PassBPointer(MyBaseB::Ptr value)
>>     {
>>       return (boost::format("PassBPointer - %1%") % int(this)).str();
>>     }
>>   };
>>}
>>
>>using namespace MyNamespace;
>>
>>BOOST_PYTHON_MODULE(MyModule)
>>{
>>   class_<MyBaseA, MyBaseA::Ptr, boost::noncopyable>("MyBaseA", no_init)
>>     .def("PassAPointer", &MyBaseA::PassAPointer)
>>   ;
>>
>>   class_<MyBaseB, MyBaseB::Ptr, boost::noncopyable>("MyBaseB", no_init)
>>     .def("PassBPointer", &MyBaseB::PassBPointer)
>>   ;
>>
>>   class_<MyClass, MyClass::Ptr, boost::noncopyable, bases<MyBaseA, MyBaseB> >("MyClass", init<>())
>>   ;
>>}
>>
>>
>>Python test code
>>
>>   c1 = MyModule.MyClass()
>>   print c1.PassAPointer(c1)  # fails
>>
>>_______________________________________________
>>C++-sig mailing list
>>C++-sig at python.org
>>http://mail.python.org/mailman/listinfo/c++-sig

-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: stack.txt
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20060322/72a71a1c/attachment.txt>


More information about the Cplusplus-sig mailing list