I am using ctypes in Python and I need to pass a pointer to an array of pointers to structs to some C function. This is struct:
typedef struct {
float x;
float y;
float z;
float radius;
} Sphere;
And I have function with the following prototype:
void render(Sphere** spheres);
In Python I declared a class for the Sphere struct and I need to set argtypes to the render
function:
lib_render = ctypes.cdll.LoadLibrary('librender.so')
class Sphere(ctypes.Structure):
_fields_ = [('x', ctypes.c_float),
('y', ctypes.c_float),
('z', ctypes.c_float),
('radius', ctypes.c_float)]
render = lib_render.render
render.argtypes = [<cannot find out what needs to be here>]
spheres = numpy.array([Sphere(1, 2.8, 3, 0.5),
Sphere(4.2, 2, 1, 3.2)])
render(spheres)
How to pass that array correctly?
I don't use numpy much, but the following works without it. I am assuming if you are passing a pointer to pointers that the pointer list must be null-terminated.
import ctypes as ct
class Sphere(ct.Structure):
_fields_ = (('x', ct.c_float),
('y', ct.c_float),
('z', ct.c_float),
('radius', ct.c_float))
dll = ct.CDLL('./test')
dll.render.argtypes = ct.POINTER(ct.POINTER(Sphere)),
dll.render.restype = None
# Create a couple of objects
a = Sphere(1, 2, 3, 4)
b = Sphere(5, 6, 7, 8)
# build a list of pointers, null-terminated.
c = (ct.POINTER(Sphere) * 3)(ct.pointer(a), ct.pointer(b), None)
dll.render(c)
Test DLL:
#include <stdio.h>
typedef struct Sphere {
float x;
float y;
float z;
float radius;
} Sphere;
__declspec(dllexport) void render(Sphere** spheres) {
for( ; *spheres; ++spheres)
printf("%f %f %f %f\n", (*spheres)->x, (*spheres)->y,
(*spheres)->z, (*spheres)->radius);
}
Output:
1.000000 2.000000 3.000000 4.000000
5.000000 6.000000 7.000000 8.000000
With numpy
, using void render(Sphere* spheres, size_t len)
, this works. Maybe someone more familiar with numpy can comment if Sphere**
can be supported.
import ctypes as ct
import numpy as np
class Sphere(ct.Structure):
_fields_ = (('x', ct.c_float),
('y', ct.c_float),
('z', ct.c_float),
('radius', ct.c_float))
dll = ct.CDLL('./test')
dll.render.argtypes = ct.POINTER(Sphere), ct.c_size_t
dll.render.restype = None
a = Sphere(1, 2, 3, 4)
b = Sphere(5, 6, 7, 8)
# c = (Sphere * 2)(a, b)
# dll.render(c, len(c))
d = np.array([a, b])
dll.render(d.ctypes.data_as(ct.POINTER(Sphere)), len(d))