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?

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@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to