Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing a C-style array data member to Python via Boost.Python

I have a struct that contains a C-style array data member. I'd like to have this struct exposed to Python, and this data member be accessible as a list in Python.

struct S
{
  char arr[4128];
};

void foo( S const * )
{}

BOOST_PYTHON_MODULE( test )
{
  using namespace boost::python;

  class_<S>( "S" )
    .def_readwrite( "arr", &S::arr )
    ;

  def( "foo", foo );
}

The code above fails to build

error C2440: '=' : cannot convert from 'const char [4128]' to 'char [4128]'

C-style arrays are not assignable, so the error makes sense. The code compiles if I change the data member to a plain char instead of an array.

I cannot replace the array with an std::array or some other container because the structure is being consumed by a C API. The only solution I can think of is to write a couple of wrappers and do the following

  1. a struct S1 that duplicates S except it'll have an std::array instead of a C-style array
  2. A foo_wrapper that accepts a S1 const *, copies the contents over to an S instance and calls foo
  3. Register a to_python_converter to convert the std::array to a Python list

This should work, and I'm not too concerned about the data copying at this point, but it'd be nice if I could avoid it and expose the array directly without having to jump through all these hoops.

So the question is, how can I expose that C-style array data member to Python as a list?

like image 757
Praetorian Avatar asked Jul 26 '13 15:07

Praetorian


1 Answers

As you've seen, Boost.Python unfortunately doesn't provide automatic converters for C arrays, and even the STL container wrappers it provides aren't how I'd recommend approaching this (at least if your real problem is similar to your example one in terms of how large the array is, and if you know you want to expose it as a true Python tuple).

I'd recommend writing a function that converts the C array into a Python tuple, using either the Python C-API directly, or its boost::python wrappers, and then exposing the data member via a property. While you could avoid data copying by using a NumPy array instead of a tuple, for small arrays that's not worth the effort.

For example:

namespace bp = boost::python;

bp::tuple wrap_arr(S const * s) {
    bp::list a;
    for (int i = 0; i < 10; ++i) {
        a.append(s->arr[i]);
    }
    return bp::tuple(a);
}

BOOST_PYTHON_MODULE( test ) {
  bp::class_<S>( "S" )
     .add_property("arr", wrap_arr)
  ;
}
like image 78
jbosch Avatar answered Oct 13 '22 07:10

jbosch