Dave Johansen
Dave Johansen

Reputation: 926

Pass Python bytes to C/C++ function using swig?

How do you pass a bytes value from Python (like data loaded from a file with open('my file.dat', 'rb').read()) to a C/C++ function using swig?

When I try using char * or uint8_t * and then a size parameter it gives me an error like this:

TypeError: in method 'processData', argument 3 of type 'char *'

I've tried using %pybuffer_mutable_binary and %pybuffer_binary but they don't seem to change the definition of the wrapper and I still get the same error.

Upvotes: 0

Views: 944

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 178189

Without code can't diagnose what is wrong, but likely you didn't declare %pybuffer lines before the function definitions. If declared after the generated wrappers won't use them when processing the functions, which would explain "they don't seem to change the definition of the wrapper".

Here's a working example. Note that passing an immutable item to a function that modifies the string will crash Python. It would be nice if the commands from pybuffer.i type-checked the Python object for mutability. If you want that don't use pybuffer.i.

test.i

%module test

%{
#include <stdlib.h>
#include <string.h>
%}

%include <pybuffer.i>
%pybuffer_mutable_string(char* str1)
%pybuffer_string(const char* str2)
%pybuffer_mutable_binary(char* str3, size_t size)
%pybuffer_binary(const char* str4, size_t size)

%inline %{
void funcms(char *str1) {
    strupr(str1);
}

size_t funcs(const char *str2) {
    return strlen(str2);
}

void funcmb(char* str3, size_t size) {
    memset(str3,'A',size);
}

size_t funcb(const char* str4, size_t size) {
    size_t tmp = 0;
    for(size_t i = 0; i < size; ++i)
        tmp += str4[i];
    return tmp % 256;
}
%}

Demo:

>>> import test
>>> b=bytearray(b'abc')  # mutable string (nul-terminated)
>>> test.funcms(b)
>>> b
bytearray(b'ABC')
>>> test.funcs(b'abc')   # immutable string (nul-terminated)
3
>>> b=bytearray(b'ab\0cd\0ef')  # mutable data (includes nulls)
>>> test.funcmb(b)
>>> b
bytearray(b'AAAAAAAA')
>>> test.funcb(b'ab\0cd\0ef')   # immutable data (compute byte checksum)
85
>>> sum(b'ab\0cd\0ef')%256      # verify result
85

Upvotes: 3

Jim Easterbrook
Jim Easterbrook

Reputation: 154

I think the best way to do this is a type map using the Python buffer interface. This passes a pointer to your data to the C/C++ function without any copying of data. For example:

%typemap(in, numinputs=1) (const char *data, unsigned long int size) {
  Py_buffer view;
  if (PyObject_CheckBuffer($input) != 1) {
    PyErr_SetString(
      PyExc_TypeError,
      "in method '$symname', argument $argnum does not support the buffer interface");
    SWIG_fail;
  }
  if (PyObject_GetBuffer($input, &view, PyBUF_SIMPLE) != 0) {
    PyErr_SetString(
      PyExc_TypeError,
      "in method '$symname', argument $argnum does not export a simple buffer");
    SWIG_fail;
  }
  $1 = view.buf;
  $2 = view.len;
  PyBuffer_Release(&view);
}
%typemap(doc) const char *data, unsigned long int size "$1_name: readable buffer (e.g. bytes)"

Upvotes: 0

Related Questions