Search code examples
c++pythonboostboost-python

boost python method calls with reference arguments


While trying to do a simple call by reference from python into a C++ class method:

class Foo {
  protected:
    int _internalVal;
  public:
    Foo() : _internalVal(5){}
    void getVal(int& val_io) {val_io = _internalVal;}
    void getValDoesNothing(int val_io) {val_io = _internalVal;}
}

It is possible to compile the boost wrapper code:

BOOST_PYTHON_MODULE(libBar) {
  boost::python::class_<Foo>("Foo")
    .def("getVal", &Foo::getVal)
    .def("getValDoesNothing", &Foo::getValDoesNothing);
}

However when executing the functionality in python an error occurs:

In [1]: import libBar

In [2]: f = libBar.Foo()

In [3]: f
Out[3]: <libBar.Foo at 0x2b483c0>

In [4]: val = int()

In [5]: #next command is just to check function signature type

In [6]: f.getValDoesNothing(val)

In [7]: f.getVal(val)
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)
<ipython-input-5-531e4cea97c2> in <module>()
----> 1 f.getVal(val)

ArgumentError: Python argument types in
    Foo.getVal(Foo, int)
did not match C++ signature:
    getVal(Foo {lvalue}, int {lvalue})

I'm working with a C++ library I don't control so changing getVal to return the value isn't an option.

Is there any way to make the last Python command work?

I'll even take a fix that doesn't change the Python variable but still allows the function call.


Solution

  • What you are trying to achieve is not valid in Python. Integers are immutable, so you can't simple call a function and hope it's going to change its content.

    Since you are working with a library you don't control and changing getVal to return the value isn't an option, you can create an wrapper like that:

    int getVal(Foo &foo) {
        int val_io;
        foo.getVal(val_io);
        return val_io;
    };
    
    BOOST_PYTHON_MODULE(libBar) {
        boost::python::def("getVal", getVal);
        ...
    }
    

    and then use in this way:

    In [1]: import libBar
    
    In [2]: f = libBar.Foo()
    
    In [3]: f
    Out[3]: <libBar.Foo at 0x2b483c0
    
    In [3]: libBar.getVal(f)
    Out[3]: 5