1

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?

4
  • I have not tested this, but I'd assume it would be ctypes.POINTER(ctypes.POINTER(Sphere)) Commented Nov 3, 2017 at 0:56
  • I tried that, but doesn't work. It can't convert numpy array to LP_LP_Sphere automatically and if I'm passing to the function spheres.ctypes.data_as(POINTER(POINTER(Sphere))) the function receives unknown garbage, all different for each time. Commented Nov 3, 2017 at 1:01
  • Have you looked into numpy.ctypeslib.ndpointer? Commented Nov 3, 2017 at 1:32
  • Yes, once I used it for passing pointer to an array of integers or some other C types this way: np.ctypeslib.ndpointer(dtype = np.int32, ndim = 1, flags = 'C_CONTIGUOUS') But I had no idea how to apply this for array of pointers to structs. Commented Nov 3, 2017 at 7:35

1 Answer 1

3

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))
Sign up to request clarification or add additional context in comments.

1 Comment

Nice approach. I didn't know you can just multiply pointer by a number and get array of pointers. I think I even don't need a numpy array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.