Search code examples
pythonarrayscpointersctypes

How do i read the values of a returned pointer from ctypes?


I am currently struggling with ctypes. I am able to convert a python list to a float array and give it to the C-function. But i can't figure out how to return this array from the C-function back to a python list...
Python-Code

class Point(ctypes.Structure):
    _fields_= [("a", ctypes.c_float * 4),
               ("aa", ctypes.c_int)]

floats = [1.0, 2.0, 3.0, 4.0] 
FloatArray4 = (ctypes.c_float * 4)
parameter_array = FloatArray4(*floats)

test1 = clibrary.dosth
test1.argtypes = [ctypes.c_float * 4, ctypes.c_int]
test1.restype = ctypes.POINTER(Point)
struc = test1(parameter_array, 9)

p = (struc.contents.a)
print(p)
clibrary.free_memory(struc)

The C-function basically puts the parameter_array into a structure ant returns the structure.. C-Code:

#include <stdio.h>
#include <stdlib.h>

struct a{float *a;
        int aa;
        } ;

struct a *dosth(float *lsit, int x){
    struct a *b = malloc(200000);
    b -> a = lsit;
    b -> aa = 3;
    return b;
}
void free_memory(struct a *pointer){
    free(pointer);
}

Output of print(p) in Python is :

<__main__.c_float_Array_4 object at 0x000001FE9EEA79C0>

How do i get access to those values?


Solution

  • Slicing a ctypes pointer will generate a Python list of the contents. Since a pointer has no knowledge of how many items it points to, you'll need to know the size, usually through another parameter:

    >>> import ctypes as ct
    >>> f = (ct.c_float * 4)(1,2,3,4)
    >>> f
    <__main__.c_float_Array_4 object at 0x00000216D6B7A840>
    >>> f[:4]
    [1.0, 2.0, 3.0, 4.0]
    

    Here's a fleshed-out example based on your code:

    test.c

    #include <stdlib.h>
    
    #ifdef _WIN32
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    struct Floats {
        float *fptr;
        size_t size;
    };
    
    API struct Floats *alloc_floats(float *fptr, size_t size) {
        struct Floats *pFloats = malloc(sizeof(struct Floats));
        pFloats->fptr = fptr;
        pFloats->size = size;
        return pFloats;
    }
    
    API void free_floats(struct Floats *pFloats) {
        free(pFloats);
    }
    

    test.py

    import ctypes as ct
    
    class Floats(ct.Structure):
        _fields_= (('fptr', ct.POINTER(ct.c_float)),  # Pointer, not array.
                   ('size', ct.c_int))  # Used to know the size of the array pointed to.
        # Display routine when printing this class.
        # Note the slicing of the pointer to generate a Python list.
        def __repr__(self):
            return f'Floats({self.fptr[:self.size]})'
    
    dll = ct.CDLL('./test')
    dll.alloc_floats.argtypes = ct.POINTER(ct.c_float), ct.c_size_t
    dll.alloc_floats.restype = ct.POINTER(Floats)
    dll.free_floats.argtypes = ct.POINTER(Floats),
    dll.free_floats.restype = None
    
    data = (ct.c_float * 4)(1.0, 2.0, 3.0, 4.0)
    p = dll.alloc_floats(data, len(data))
    print(p.contents)
    dll.free_floats(p)
    

    Output:

    Floats([1.0, 2.0, 3.0, 4.0])