BPL
BPL

Reputation: 9863

TypeError: in method '...', argument 1 of type 'unsigned char const *' when using swig module

Consider this little swig mcve:

example.h

void init(int width, int height);
void dump(const unsigned char *buffer,int pitch);

example.c

#include <stdio.h>

void init(int width, int height) {
    printf("Initializing width=%d height=%d", width, height);
}

void dump(const unsigned char *buffer,int pitch) {
    for(int i=0;i<pitch;i++) {
        printf("%d\n", buffer[i]);
    }
}

example.i

%module example

%{
#include "example.h"
%}

%include "example.h"

setup.py

from distutils.core import setup, Extension


example_module = Extension('_example',
                            sources=['example.i', 'example_wrap.c', 'example.c'],
                            swig_opts = [],
                            include_dirs = ["."],
                           )

setup(name='example',
      version='0.1',
      author="BPL",
      description="""Mcve stackoverflow""",
      ext_modules=[example_module],
      py_modules=["example"]
    )

test.py

import struct
import example as swig_thing

count = 256
width = 8
height = 4

swig_thing.init(width, height)

for frame in range(count):
    print(f"frame {frame}")

    data = []
    for y in range(height):
        for x in range(width):
            data.append(0x00FF0000)
    _buffer = struct.pack(f'{len(data)}L', *data)
    swig_thing.dump(_buffer, width*4)

If I run python setup.py build_ext --inplace and then I try to run test.py I'll get the following error:

TypeError: in method 'dump', argument 1 of type 'unsigned char const *'

Question, how to avoid the above error?

Upvotes: 2

Views: 3439

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 177665

struct.pack can be used to create byte string buffers. Let's say you have four integers to pack as four unsigned long values (16 bytes). pack takes a format string. '4L' means pack four unsigned longs in native-endian format. Use '<4L' for little-endian and '>4L' for big-endian.

>>> import struct
>>> struct.pack('4L',1,2,3,4) # Direct way.
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00'

>>> data = [1,2,3,4] # Handle variable length...
>>> struct.pack('{}L'.format(len(data)),*data) 
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00'

>>> struct.pack(f'{len(data)}L',*data) # Python 3.6+
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00'

Exercise for you to generate the data list 😊

Per your MCVE, add the following typemap to your SWIG interface to understand unsigned char *:

example.i

%module example

%{
#include "example.h"
%}

%typemap(in) (const unsigned char* buffer) (char* buffer, Py_ssize_t length) %{
  if(PyBytes_AsStringAndSize($input,&buffer,&length) == -1)
    SWIG_fail;
  $1 = (unsigned char*)buffer;
%}

%include "example.h"

Upvotes: 2

Related Questions