[C++-sig] Pointer to existing Python object

Andreas Beyer beyer at imb-jena.de
Sat May 14 01:01:59 CEST 2005


This message was also in HTML format. So I am sending it again. Sorry.

David, thanks a lot for the help! However, I think your suggestion does 
not yet solve the problem. See below.

David Abrahams wrote:

>Andreas Beyer <beyer at imb-jena.de> writes:
>
>  
>
>>Hi:
>>
>>I know that there has been some discussion on this subject on c++ sig. 
>>However, I could not find a solution for my specific problem and maybe 
>>there is no solution.
>>
>>Here we go:
>>
>>I have a container class B that creates instances of class A - both
>>in C++. In Python I can get instances of A from B with a get()
>>method. I would like to get an existing Python object whenever such
>>object existis and I would like to get a new Python object if no
>>Python object for A exists.
>>    
>>
>
>This is hard to do.  I see that you're using shared_ptr and that's a
>good start.  What you need to do, the moment a shared_ptr is about to
>be released to Python, is replace it:
>
>   template <class T>
>   hold_python(shared_ptr<T>& x)
>   {
>       x = extract<shared_ptr<T> >( python::object(x) );
>   }
>
>  
>
This doesn't seem to work. Below is an example Python session, get() 
still returns different objects. Is it possible, that boost::python 
deletes the internal smart-pointer immediately after exiting 
hold_python() because the python object does not persist beyond this 
block? I.e. when I call hold_python() again, does the boost::python 
environment still 'know' the pointer?
I changed hold_python() such that it returns a copy of the python 
object, but that doesn't help either. The idea was, if the python 
interpreter holds a reference to the python object, the shared_ptr might 
survive. But maybe I am on the wrong track.
Here is my attempt:

template <class T>
python::object hold_python(shared_ptr<T>& x)
{
      python::object py_x = python::object(x);
      x = python::extract<shared_ptr<T> >( py_x );
      return py_x;
}
python::object get_b_a(B& b)
{
    return hold_python(b.a);
}

This is my example Python session using David's suggestions (C++ code 
follows):
 >>> a=A()
 >>> b=B()
 >>> b.set(a)
 >>> a.val=10
 >>> a.foo=42
 >>> a2=b.get()
 >>> a
<ptr_test.A object at 0xb7347914>
 >>> a2
<ptr_test.A object at 0xb7347914>
 >>> a.val
10
 >>> a2.val
10
 >>> a.foo
42
 >>> a2.foo
42
 >>> b=B()
 >>> a=b.get() # let b create an A
 >>> a.val=10
 >>> a.foo=42
 >>> a2=b.get()
 >>> a
<ptr_test.A object at 0xb7343ae4>
 >>> a2
<ptr_test.A object at 0xb7343b1c>  # Still not the same as 'a'
 >>> a.val
10
 >>> a2.val
10
 >>> a.foo
42
 >>> a2.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'A' object has no attribute 'foo'
 >>>

Thus, the C++ object is the same, the Python object isn't.


#include <boost/python.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

class A : public enable_shared_from_this<A> {
 public:
   A() : val(0) {};
   int val;
   typedef shared_ptr<A> A_ptr;
   A_ptr self() {
      A_ptr self;
      self = shared_from_this();
      return self;
    }

};

class B {
  public:
    B() {
       a = A::A_ptr(new A());
    }
    void set(A::A_ptr a) {
      this->a = a;
    }
    A::A_ptr get() {
       return a;
    }
    A::A_ptr a;
};

template <class T>
void hold_python(shared_ptr<T>& x)
{
      x = python::extract<shared_ptr<T> >( python::object(x) );
}

A::A_ptr get_b_a(B& b)
{
    hold_python(b.a);
    return b.get();
}

BOOST_PYTHON_MODULE(ptr_test) {
  python::class_<A, noncopyable> ("A")
    .def("self", &A::self)
    .def_readwrite("val", &A::val)
  ;
  python::register_ptr_to_python< A::A_ptr >();
 
  python::class_<B>("B")
     .def("set", &B::set)
//     .def("get", &B::get)
     .def("get", get_b_a)
  ;
}
  
Thanks for the help,
Andreas







More information about the Cplusplus-sig mailing list