[C++-sig] weak_ptr to this in C++ classes extended from python

Holger Brandsmeier brandsmeier at gmx.de
Fri Apr 27 14:00:29 CEST 2012


On Fri, Apr 27, 2012 at 08:22, Bryan Catanzaro <bcatanzaro at acm.org> wrote:
> I think the problem is that aa is no longer an ExtendedA object,
> because when you call ptr() it returns a pointer to the base C++ A
> class, not the derived class.

I think you got something confused. A boost::shared_pointer<A> can
very well be any pointer to any derived class, no copy should be
created. I can still call virtual functions from `A` and should get
the result from the implementation of `ExtendedA`.

> I think at this point it's probably best to reconsider the problem and
> come up with a simpler solution.  Why do you want to expose the ptr
> method to Python anyway?

I don't plan to call `ptr()` from python, but I will call the same
function from C++ and and at some point the result that C++ produced
can find its way back to python.

I did find a different solution to this problem:

On Mon, Apr 23, 2012 at 20:48, Holger Brandsmeier <brandsmeier at gmx.de> wrote:
> In the meantime I figured out a different way of doing the shared
> pointer from this in python. Namely, to just make getThisRCP() a
> virtual function, add it to the Wrapper and to implement it in the
> python by implementing it as

but I still expect the above mentioned approch with
`boost::enable_shared_from_this` _should_ have worked with boost
python. I seem to run into an issue that even Dave can not explain
(well lets wait for his reply).

Moreover note that in boost::python usually if C++ returns a
`shared_ptr<A>` which has previously been created as an instance of
`ExtendedA` inside python, than this class is available as
`ExtendedA`. There is some magic with custom deleters that usually
makes you able to treat that class as instance of `ExtendedA`. This
magic will also stop working when you use
`boost::enable_shared_from_this`. With that aspect I can live, though.

-Holger

> On Thu, Apr 26, 2012 at 11:49 AM, Holger Brandsmeier <brandsmeier at gmx.de> wrote:
>> Bryan,
>>
>> my problems happen as soon as I need to use wrappers. I really need
>> the class that I extend in python to implement some pure virtual
>> function in the C++ class. I extended your code and refined what is
>> going wrong. I tried several Variants with different errors, I used
>> #defines to show you all 4 versions, next to each Variant I put a C++
>> comment of what is going wrong.
>>
>> So here it is:
>>
>> ------------  foo.cpp  ------------
>> #include <boost/python.hpp>
>> #include <boost/shared_ptr.hpp>
>> #include <boost/enable_shared_from_this.hpp>
>> #include <iostream>
>>
>> using namespace boost::python;
>>
>> class A
>>   : public boost::enable_shared_from_this<A> {
>>   static int counter;
>> public:
>>   int id;
>>   A() {
>>       id = counter++;
>>       std::cout << "Constructing A[" << id << "]" << std::endl;
>>   }
>>   ~A() {
>>       std::cout << "Destructing A[" << id << "]" << std::endl;
>>   }
>>   boost::shared_ptr<A> ptr() {
>>       return shared_from_this();
>>   }
>> #define VARIANT0
>> #ifdef VARIANT0
>>   // this variant works nicely with VARIANT1 below (compiles and
>>   // no error for `aa = a.ptr()`), but it produces wrong results,
>>   // i.e. `77` is returned for `aa.foo()` although it should have
>>   // been `33`.
>>   virtual int foo() {
>>      return 77;
>>   }
>> #else
>>   virtual int foo() = 0;
>> #endif
>> };
>>
>> struct AWrapper : public A, public boost::python::wrapper<A> {
>>  virtual int foo() {
>>    if( override f = this->get_override("foo") ) {
>>      return f();
>>    } else {
>>      return 42;
>>    }
>>  }
>> };
>>
>> int A::counter = 0;
>>
>> using namespace boost::python;
>>
>>
>> BOOST_PYTHON_MODULE(foo) {
>> #define VARIANT1
>> //#define VARIANT2
>> //#define VARIANT3
>>
>> #ifdef VARIANT1
>>   // The following line doesn't compites it gives the error:
>>   // error: cannot allocate an object of abstract type ‘A’
>>   class_<AWrapper, boost::shared_ptr<A>, boost::noncopyable >("A", init<>())
>> #endif
>> #ifdef VARIANT2
>>  // the following compiles, but produces an error in python, when executing
>>  // aa = a.ptr();
>>  // RuntimeError: tr1::bad_weak_ptr
>>   class_<AWrapper, boost::noncopyable  >("A", init<>())
>> #endif
>> #ifdef VARIANT3
>>   // the following compiles, but produces an error in python, when executing
>>   // aa = a.ptr();
>>   // TypeError: No to_python (by-value) converter found for C++ type:
>> boost::shared_ptr<A>
>>   class_<AWrapper, boost::shared_ptr<AWrapper>, boost::noncopyable
>>>("A", init<>())
>> #endif
>>     .def("ptr", &A::ptr)
>>     .def("foo", &A::foo)
>>     ;
>> }
>> -----------------------------------
>>
>> ------------  foo.py  ------------
>> import foo
>>
>> class ExtendedA(foo.A):
>>   def foo(self):
>>     return 33;
>>
>> a0 = foo.A()
>> a = ExtendedA()
>> aa = a.ptr();
>> print a.foo()
>> del a
>> print aa.foo()
>> -----------------------------------
>>
>> Do you have any suggestion how to get this working?
>> -Holger
>>
>> On Thu, Apr 26, 2012 at 18:43, Bryan Catanzaro <bcatanzaro at acm.org> wrote:
>>> Holger Brandsmeier <brandsmeier <at> gmx.de> writes:
>>>
>>>>
>>>> Dear list,
>>>>
>>>> how is it possible to have a class in C++ that can be extended from
>>>> python and that stores a weak_ptr to itself?
>>>
>>>
>>> Have you tried using boost::enable_shared_from_this?  The following example code
>>> seems to do what you want.
>> _______________________________________________
>> Cplusplus-sig mailing list
>> Cplusplus-sig at python.org
>> http://mail.python.org/mailman/listinfo/cplusplus-sig
> _______________________________________________
> Cplusplus-sig mailing list
> Cplusplus-sig at python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig


More information about the Cplusplus-sig mailing list