1

I've closed-source C++ library, which provides header files with code equivalent to:

class CSomething
{
  public:
      void getParams( unsigned char & u8OutParamOne, 
                      unsigned char & u8OutParamTwo ) const;
  private:
      unsigned char u8OutParamOne_,
      unsigned char u8OutParamTwo_,
};

I'm trying to expose that to Python, my wrapper code is something like this:

BOOST_PYTHON_MODULE(MySomething)
{
    class_<CSomething>("CSomething", init<>())
        .def("getParams", &CSomething::getParams,(args("one", "two")))

}

Now I'm trying to use that in Python, which fails horribly:

one, two = 0, 0
CSomething.getParams(one, two)

Which results in:

ArgumentError: Python argument types in
    CSomething.getParams(CSomething, int, int)
did not match C++ signature:
    getParams(CSomething {lvalue}, unsigned char {lvalue} one, unsigned char {lvalue} two)

What do I need to change either in the Boost.Python wrapper code or Python code to make it work? How do I add some Boost.Python magic to automatically cast PyInt to unsigned char and vice-versa?

vartec
  • 131,205
  • 36
  • 218
  • 244
  • The two arguments are references which Python doesn't have, see this [question](http://stackoverflow.com/questions/2459588/pass-by-reference-in-boostpython). – martineau Oct 16 '12 at 14:49
  • @martineau: but I cannot change that, so there must be a way around that – vartec Oct 16 '12 at 14:53
  • OK, [this](http://stackoverflow.com/a/578645/355230) answer has a workaround. – martineau Oct 16 '12 at 15:04
  • have you looked at the [call policies](http://www.boost.org/doc/libs/1_50_0/libs/python/doc/tutorial/doc/html/python/functions.html#python.call_policies) ? If you have to use references, try with return_internal_reference or return_value_policy – lucasg Oct 16 '12 at 15:05
  • @martineau, @georgesl: you both concentrate on the reference issue, while the error is about mismatch between `int` and `unsigned char`. – vartec Oct 16 '12 at 15:09
  • @vartec, on the contrary, the error says that the mismatch is between `int` and `unsigned char {lvalue}`. The trick is the `lvalue` thing, which means "something that a value can be assigned to". You can't do that in Python. – vz0 Oct 16 '12 at 16:04
  • @vz0: I have another function `void f(vector &p)` exposed via `.def(vector_indexing_suite>, args('p'))`. So how come the lvalue works in that case and not in case of simple value like `int`? – vartec Oct 16 '12 at 16:08
  • @vartec Because you are using the `vector_indexing_suite` wrapper, which converts the c++ vector to a Python list and vice-versa. – vz0 Oct 16 '12 at 16:13
  • @vz0: ok, so is there something like that for simple values? – vartec Oct 16 '12 at 16:14

1 Answers1

1

Boost.Python is complaining about a missing lvalue parameter, a concept which does not exist in Python:

def f(x):
  x = 1

y = 2
f(y)
print(y) # Prints 2

The x paramter of the f function is not a C++-like reference. In C++ the output is different:

void f(int &x) {
  x = 1;
}

void main() {
  int y = 2;
  f(y);
  cout << y << endl; // Prints 1.
}

You have a few choices here:

a) Wrap the CSomething.getParams function to return a tuple of the new parameters values:

one, two = 0, 0
one, two = CSomething.getParams(one, two)
print(one, two)

b) Wrap the CSomething.getParams function to accept a class instance as parameter:

class GPParameter:
  def __init__(self, one, two):
    self.one = one
    self.two = two

p = GPParameter(0, 0)
CSomething.getParams(p)
print(p.one, p.two)
vz0
  • 32,345
  • 7
  • 44
  • 77
  • I've did kind of a variant of solution a), but instead of one getter returning tuple, I've created two separate getters returning unsigned char and bound them as properties in Boost.Python. – vartec Oct 17 '12 at 12:18
  • @vartec: It sounds like your version has at least twice -- perhaps even more -- as much function-call overhead. – martineau Oct 18 '12 at 11:22
  • @martineau: It does. However, it's completely irrelevant for the application. – vartec Oct 18 '12 at 11:38