2009/6/24 J. Michael Owen <mikeo...@llnl.gov> > Hi again, > > I've run across another issue confusing me as I try to use pybindgen, in > this case it's the treatment of reference parameters in virtual methods > where you're allowing the class to be subclassed from python. It looks like > the helper class that is generated is making copies of parameters that are > being passed as references, which is not what we want. Take the following > example C++ classes: > > class A { > public: > A(const int val): mVal(val) {} > private: > int mVal; > A(); > A(const A& rhs); > A& operator=(const A& rhs); > }; > > class B { > public: > B() {} > virtual ~B() {} > virtual void some_virtual_method(const A& a) const { std::cerr << &a << > std::endl; } > private: > }; > > I'd like to be able to create subclasses of B from python, so I try > wrapping these classes with the following pybindgen code: > > mod = Module("ref_param_example") > mod.add_include('"classes.hh"') > a = mod.add_class("A") > b = mod.add_class("B", allow_subclassing=True) > a.add_constructor([param("int", "val")]) > b.add_constructor([]) > b.add_method("some_virtual_method", None, [param("A&", "a")], > is_const=True, is_virtual=True) > > The resulting generated code contains the following: > > void > PyB__PythonHelper::some_virtual_method(A & a) const > { > PyGILState_STATE __py_gil_state; > B *self_obj_before; > PyObject *py_retval; > PyA *py_A; > > __py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : > (PyGILState_STATE) 0); > if (!PyObject_HasAttrString(m_pyself, (char *) "_some_virtual_method")) > { > B::some_virtual_method(a); > if (PyEval_ThreadsInitialized()) > PyGILState_Release(__py_gil_state); > return; > } > self_obj_before = reinterpret_cast< PyB* >(m_pyself)->obj; > reinterpret_cast< PyB* >(m_pyself)->obj = const_cast< B* >((const B*) > this); > py_A = PyObject_New(PyA, &PyA_Type); > py_A->obj = new A(a); > .... > > As you can see, the last line quoted here is trying to make a copy of the > instance "a" that was passed in. In this example this will not compile > because the copy constructor for A is private. In general we don't want to > make copies of reference parameters regardless of course. > > Am I once again missing something here? Is there some way I can declare > that the passed parameters for "some_virtual_method" should really be > treated as references and not copied? >
Sorry, but in this case the answer is no. The virtual method wrapper here receives a parameter from C++ and has to create a Python wrapper for it. The wrapper needs to own the object. The reason is that the Python method that is called may decide to keep a reference to the object. If the Python wrapper kept a "shared" reference to the same C++ object then it could at some point in time be keeping a reference to an object when the caller has already deallocated it. To summarize, the object copy is done because the alternative would be memory unsafe. This is one case where a C++ API blindly designed without considering language bindings may make it difficult to bind later on. Ideally, in this case you should pass a pointer to A, and even better A would have reference counting so that both caller and callee could own a reference to the same object at the same time... -- Gustavo J. A. M. Carneiro INESC Porto, Telecommunications and Multimedia Unit "The universe is always one step beyond logic." -- Frank Herbert
_______________________________________________ Cplusplus-sig mailing list Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig