13

I've got third party C++ library in which some class methods use raw byte buffers. I'm not quite sure how to deal in Boost::Python with it.

C++ library header is something like:

class CSomeClass
{
  public:
      int load( unsigned char *& pInBufferData, int & iInBufferSize );
      int save( unsigned char *& pOutBufferData, int & iOutBufferSize );
}

In stuck with the Boost::Python code...

class_<CSomeClass>("CSomeClass", init<>())
    .def("load", &CSomeClass::load, (args(/* what do I put here??? */)))
    .def("save", &CSomeClass::save, (args(/* what do I put here??? */)))

How do I wrap these raw buffers to expose them as raw strings in Python?

vartec
  • 131,205
  • 36
  • 218
  • 244

1 Answers1

12

You have to write, yourself, functions on your bindings that will return a Py_buffer object from that data, allowing your to either read-only (use PyBuffer_FromMemory) or read-write (use PyBuffer_FromReadWriteMemory) your pre-allocated C/C++ memory from Python.

This is how it is going to look like (feedback most welcome):

#include <boost/python.hpp>

using namespace boost::python;

//I'm assuming your buffer data is allocated from CSomeClass::load()
//it should return the allocated size in the second argument
static object csomeclass_load(CSomeClass& self) {
  unsigned char* buffer;
  int size;
  self.load(buffer, size);

  //now you wrap that as buffer
  PyObject* py_buf = PyBuffer_FromReadWriteMemory(buffer, size);
  object retval = object(handle<>(py_buf));
  return retval;
}

static int csomeclass_save(CSomeClass& self, object buffer) {
  PyObject* py_buffer = buffer.ptr();
  if (!PyBuffer_Check(py_buffer)) {
    //raise TypeError using standard boost::python mechanisms
  }

  //you can also write checks here for length, verify the 
  //buffer is memory-contiguous, etc.
  unsigned char* cxx_buf = (unsigned char*)py_buffer.buf;
  int size = (int)py_buffer.len;
  return self.save(cxx_buf, size);
}

Later on, when you bind CSomeClass, use the static functions above instead of the methods load and save:

//I think that you should use boost::python::arg instead of boost::python::args
// -- it gives you better control on the documentation
class_<CSomeClass>("CSomeClass", init<>())
    .def("load", &csomeclass_load, (arg("self")), "doc for load - returns a buffer")
    .def("save", &csomeclass_save, (arg("self"), arg("buffer")), "doc for save - requires a buffer")
    ;

This would look pythonic enough to me.

André Anjos
  • 4,641
  • 2
  • 27
  • 34
  • 1
    `py_buffer` is of type `PyObject*` and you are invoking `.buf` on it ? – Neel Basu Apr 19 '16 at 11:41
  • I think you're right, there should be a cast somewhere before to `PyBufferObject`. This code is now getting outdated. New-style buffers are there and one probably should consider using them. – André Anjos Apr 20 '16 at 10:20
  • I know this is an old question, but can you post a link to some information about those "new-style buffers"? I can't seem to find anything :/ – jpihl Jan 05 '18 at 08:25
  • Ah, never mind I think I found something here: https://docs.python.org/2/c-api/buffer.html – jpihl Jan 05 '18 at 08:33
  • That's the linked document on the first sentence. – André Anjos Jan 05 '18 at 10:14