Use information from one argument to create another argument in typemap(in)

Question:

Currently I have the following C snippet:

typedef struct My_Struct {
    int x;
    int y;
} my_struct_t;

void generate_buffer(my_struct_t info, uint8_t* buffer_out);

I am trying to come up with a python swig interface to call this function. The output buffer size is in info.x.

The following typemap I tried, but fails unfortunately:

%typemap(in) (my_struct_t info, uint8_t* buffer_out) %{
    my_struct_t tmp;
    int res1 = SWIG_ConvertPtr($input, (void*)&tmp, SWIGTYPE_p_My_Struct,  0 );
    if (!SWIG_IsOK(res1)) {
      SWIG_exception_fail(SWIG_ArgError(res1), "Converting my_struct_t type to swig");
    }

    $2 = (uint8_t*)malloc(tmp.x);
    $1 = tmp;
%}

%typemap(argout) (my_struct_t info, uint8_t* buffer_out) (PyObject* po) %{
    po = PyBytes_FromStringAndSize((char*)$2,($1).x);
    $result = SWIG_Python_AppendOutput($result,po);
%}

%typemap(freearg) (my_struct_t info, uint8_t* buffer_out) %{
    free($2);
%}

I was expecting the info object to be accessible as is in the C function, also the newly allocated set of bytes to be absorbed and freed in the typemap.
The input typemap fails with junk values that I am finding hard to debug.

Asked By: Maverickgugu

||

Answers:

SWIG_ConvertPtr takes the address of a pointer to store the conversion. The (void*) cast hides the mistake. See comments below:

test.i

%module test

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

%typemap(in) (my_struct_t info, uint8_t* buffer_out) (my_struct_t* tmp) %{  // needs to be a pointer
    // $&1_descriptor generates the appropriate SWIG descriptor for a my_struct_t*
    int res1 = SWIG_ConvertPtr($input, (void*)&tmp, $&1_descriptor,  0 );
    if (!SWIG_IsOK(res1)) {
      SWIG_exception_fail(SWIG_ArgError(res1), "Converting my_struct_t type to swig");
    }
    $2 = malloc(tmp->x); // needs -> to dereference pointer
    $1 = *tmp;           // needs * to pass by value
%}

%typemap(argout) (my_struct_t info, uint8_t* buffer_out) (PyObject* po) %{
    po = PyBytes_FromStringAndSize((char*)$2, ($1).x);
    $result = SWIG_Python_AppendOutput($result, po);
%}

%typemap(freearg) (my_struct_t info, uint8_t* buffer_out) %{
    free($2);
%}

// Demo implementation
%inline %{
typedef struct My_Struct {
    int x;  // Used x to match typemap
    int y;
} my_struct_t;

void generate_buffer(my_struct_t info, uint8_t* buffer_out) {
    memset(buffer_out, info.y, info.x);
}
%}

Demo:

>>> import test
>>> i = test.my_struct_t()
>>> i.x = 10
>>> i.y = ord('A')
>>> test.generate_buffer(i)
b'AAAAAAAAAA'
Answered By: Mark Tolonen
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.