Python Swig interface for C function allocating a list of structures
Question:
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.
Answers:
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)]
Building on top Mark’s second approach. I came up with the following interface file. I’m not sure if there would be leaks, just adding it here to seek comments.
%module example
%{
#include "example.h"
%}
// 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 objects from the structs.
%typemap(argout) (struct MyStruct** list, int* size) (PyObject* list) %{
list = PyList_New(*$2);
for(int i = 0; i < *$2; ++i) {
struct MyStruct* t = (struct MyStruct*)malloc(sizeof((*$1)[i]));
t->x = (*$1)[i].x;
t->y = (*$1)[i].y;
PyObject* t1 = SWIG_NewPointerObj(t, SWIGTYPE_p_MyStruct, SWIG_POINTER_OWN);
PyList_SET_ITEM(list, i, (PyObject*)t1);
}
$result = SWIG_Python_AppendOutput($result, list);
%}
// Free the allocated structure array
%typemap(freearg) (struct MyStruct** list, int* size) %{
free(*$1);
%}
%include "example.h"
Now the objects could be accessed more organically? Unsure about the memory handling part in this case.
>>> import example as e
>>> a = e.myfunc()
0xbf2b30
>>> al = [(b.x,b.y) for b in a]
>>> al
[(0, 0.0), (1, 0.10000000149011612), (2, 0.20000000298023224), (3, 0.30000001192092896), (4, 0.4000000059604645), (5, 0.5), (6, 0.6000000238418579), (7, 0.699999988079071), (8, 0.800000011920929), (9, 0.8999999761581421)]
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.
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)]
Building on top Mark’s second approach. I came up with the following interface file. I’m not sure if there would be leaks, just adding it here to seek comments.
%module example
%{
#include "example.h"
%}
// 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 objects from the structs.
%typemap(argout) (struct MyStruct** list, int* size) (PyObject* list) %{
list = PyList_New(*$2);
for(int i = 0; i < *$2; ++i) {
struct MyStruct* t = (struct MyStruct*)malloc(sizeof((*$1)[i]));
t->x = (*$1)[i].x;
t->y = (*$1)[i].y;
PyObject* t1 = SWIG_NewPointerObj(t, SWIGTYPE_p_MyStruct, SWIG_POINTER_OWN);
PyList_SET_ITEM(list, i, (PyObject*)t1);
}
$result = SWIG_Python_AppendOutput($result, list);
%}
// Free the allocated structure array
%typemap(freearg) (struct MyStruct** list, int* size) %{
free(*$1);
%}
%include "example.h"
Now the objects could be accessed more organically? Unsure about the memory handling part in this case.
>>> import example as e
>>> a = e.myfunc()
0xbf2b30
>>> al = [(b.x,b.y) for b in a]
>>> al
[(0, 0.0), (1, 0.10000000149011612), (2, 0.20000000298023224), (3, 0.30000001192092896), (4, 0.4000000059604645), (5, 0.5), (6, 0.6000000238418579), (7, 0.699999988079071), (8, 0.800000011920929), (9, 0.8999999761581421)]