I'm trying to get the following C function to be exposed as a python interface.
void myfunc(struct MyStruct** list, int* size) {
int n = 10;
*size = n;
*list = (struct MyStruct*) malloc(n * sizeof(struct MyStruct));
for (int i = 0; i < n; i ) {
(*list)[i].x = i;
(*list)[i].y = i * 0.1;
}
}
Unfortunately the swig documentation didn't help narrow down on a solution. Could you please help with any pointers or code references to how we can write a corresponding swig interface file to make this function be called from python?
It would be a bonus if I could access this as a list of objects in python.
CodePudding user response:
One way to do it (error checking left out):
SWIG Interface File - test.i
%module test
%{ // code to include directly in the wrapper
#include <stdlib.h>
#include <stdio.h>
struct MyStruct {
int x;
double y;
};
void myfunc(struct MyStruct** list, int* size) {
int n = 10;
*size = n;
*list = (struct MyStruct*) malloc(n * sizeof(struct MyStruct));
for (int i = 0; i < n; i ) {
(*list)[i].x = i;
(*list)[i].y = i * 0.1;
}
}
%}
// On input, do not require the list/size parameters.
// Instead, declare tmp variables and pass them by reference
// to capture the output arguments.
%typemap(in, numinputs=0) (struct MyStruct** list, int* size) (struct MyStruct* tmp, int size) %{
$1 = &tmp;
$2 = &size;
%}
// After the function call, append the returned pointer and size to the output result.
%typemap(argout) (struct MyStruct** list, int* size) %{
PyObject* obj = SWIG_NewPointerObj(*$1, $descriptor(struct MyStruct*), 0);
PyObject* s = PyLong_FromLong(*$2);
$result = SWIG_Python_AppendOutput($result, obj);
$result = SWIG_Python_AppendOutput($result, s);
%}
// Use the pre-defined SWIG typemaps to handle C pointers and arrays.
%include <carrays.i>
%include <cpointer.i>
%pointer_functions(struct MyStruct, pMyStruct);
%array_functions(struct MyStruct, MyStructArray);
// Make Python wrappers for the below struct and function.
struct MyStruct {
int x;
double y;
};
void myfunc(struct MyStruct** list, int* size);
Python test file - example.py
import test
# Helper function to put all the returned data in a list
# and manage the allocated pointer.
def myfunc():
ptr, size = test.myfunc()
try:
arr = []
objs = [test.MyStructArray_getitem(ptr, i) for i in range(size)]
return [(obj.x, obj.y) for obj in objs]
finally:
test.delete_pMyStruct(ptr)
print(myfunc())
Output:
[(0, 0.0), (1, 0.1), (2, 0.2), (3, 0.30000000000000004), (4, 0.4), (5, 0.5), (6, 0.6000000000000001), (7, 0.7000000000000001), (8, 0.8), (9, 0.9)]
If you want to completely handle the data conversion in the SWIG wrapper, here's an example:
test.i
%module test
%{ // code to include directly in the wrapper
#include <stdlib.h>
#include <stdio.h>
struct MyStruct {
int x;
double y;
};
void myfunc(struct MyStruct** list, int* size) {
int n = 10;
*size = n;
*list = (struct MyStruct*) malloc(n * sizeof(struct MyStruct));
for (int i = 0; i < n; i ) {
(*list)[i].x = i;
(*list)[i].y = i * 0.1;
}
}
%}
// On input, do not require the list/size parameters.
// Instead, declare tmp variables and pass them by reference
// to capture the output arguments.
%typemap(in, numinputs=0) (struct MyStruct** list, int* size) (struct MyStruct* tmp, int size) %{
$1 = &tmp;
$2 = &size;
%}
// After the function call, Build a list of tuples with the x/y data from the structs.
%typemap(argout) (struct MyStruct** list, int* size) (PyObject* list) %{
list = PyList_New(*$2);
for(int i = 0; i < *$2; i) {
PyObject* t = PyTuple_New(2);
PyTuple_SET_ITEM(t, 0, PyLong_FromLong((*$1)[i].x));
PyTuple_SET_ITEM(t, 1, PyFloat_FromDouble((*$1)[i].y));
PyList_SET_ITEM(list, i, t);
}
$result = SWIG_Python_AppendOutput($result, list);
%}
// Free the allocated structure array
%typemap(freearg) (struct MyStruct** list, int* size) %{
free(*$1);
%}
// Make Python wrappers for the below struct and function.
struct MyStruct {
int x;
double y;
};
void myfunc(struct MyStruct** list, int* size);
example.py
import test
print(test.myfunc())
Output:
[(0, 0.0), (1, 0.1), (2, 0.2), (3, 0.30000000000000004), (4, 0.4), (5, 0.5), (6, 0.6000000000000001), (7, 0.7000000000000001), (8, 0.8), (9, 0.9)]