[C++-sig] pybindgen: allow_subclassing option causing reference parameters to be copied?

Gustavo Carneiro gjcarneiro at gmail.com
Tue Jun 30 15:29:39 CEST 2009


2009/6/29 J. Michael Owen <mikeowen at llnl.gov>

> Hi Gustavo,
> Hmm.  I tried this example, but the generated code seems to be still trying
> to generate a copy.  Here's the example source I'm trying expose:
>
> #include <iostream>
>
> 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 try to expose these objects 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, [Parameter.new("A&", "a",
> direction=Parameter.DIRECTION_INOUT)], is_const=True, is_virtual=True)
>
> Now the generated code contains the following function for the helper class
> required since we're allowing subclassing:
>
> 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 = &(a);
>     py_retval = PyObject_CallMethod(m_pyself, (char *)
> "_some_virtual_method", (char *) "O", py_A);
>     if (py_retval == NULL) {
>         PyErr_Print();
>         Py_DECREF(py_A);
>         reinterpret_cast< PyB* >(m_pyself)->obj = self_obj_before;
>         if (PyEval_ThreadsInitialized())
>             PyGILState_Release(__py_gil_state);
>         return;
>     }
>     if (py_retval != Py_None) {
>         PyErr_SetString(PyExc_TypeError, "function/method should return
> None");
>         Py_DECREF(py_retval);
>         Py_DECREF(py_A);
>         reinterpret_cast< PyB* >(m_pyself)->obj = self_obj_before;
>         if (PyEval_ThreadsInitialized())
>             PyGILState_Release(__py_gil_state);
>         return;
>     }
>     if (py_A->ob_refcnt == 1)
>         py_A->obj = NULL;
>     else{
>
>         py_A->obj = new A(a);
>     }
>     Py_DECREF(py_retval);
>     Py_DECREF(py_A);
>     reinterpret_cast< PyB* >(m_pyself)->obj = self_obj_before;
>     if (PyEval_ThreadsInitialized())
>         PyGILState_Release(__py_gil_state);
>     return;
> }
>
> You can see the line near the end here that is trying to make a copy of the
> "a" argument with "py_A->obj = new A(a);", which isn't what we want.  Why is
> it trying to do that anyway?  This was all generated with pybindgen 0.10.0
> by the way -- is there a more recent version that will avoid this problem?
>

Ah, right.  The thing is, the code tries to make a copy of the object to
give to the python wrapper in case it appears the python code kept a
reference to the wrapper.

I need to fix pybindgen to, if the class has no copy constructor keep the
wrapper->obj pointer NULL instead of giving it a copy.

The fix has been pushed to the bazaar branch.  See
https://code.launchpad.net/~gjc/pybindgen/trunk



>
> Thanks!
>
> Mike.
>
> On Jun 28, 2009, at 8:19 AM, Gustavo Carneiro wrote:
>
> After starting to look at this issue, I realized that this solution is
> already implemented actually, but I had forgotten! :P
>
> You need to tell pybindgen that the reference parameter is INOUT, thus:
>
>     ReferenceManipulator.add_method('do_manipulate_object', 'void',
>                                     [Parameter.new('ManipulatedObject&',
> 'obj', direction=Parameter.DIRECTION_INOUT)],
>                                     is_virtual=True, is_pure_virtual=True)
>
> Then everything works as expected.  I am closing the bug by merely adding a
> unit test to cover this problem.  Should work in pybindgen 0.10 as well.
>
> Take care,
>
> --
> 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 at python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig
>



-- 
Gustavo J. A. M. Carneiro
INESC Porto, Telecommunications and Multimedia Unit
"The universe is always one step beyond logic." -- Frank Herbert
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20090630/d89192f6/attachment.htm>


More information about the Cplusplus-sig mailing list