Dana Kim
Dana Kim

Reputation: 13

ctypes struct containing arrays and manipulating

I'm trying to use ctypes in my python code test.pyto pass the array to test.c and do some calculation and bring the result to python code.

Here is 'test.c'

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"

void test(int N, complex *v){
    int k;
    for(k=0; k<N; k++){
        v[k].Re = v[k].Re + 1;
        v[k].Im = v[k].Im + 1;    
        printf("%d %f %f\n", k, v[k].Re, v[k].Im);
    }
}

v is the structed array defined in test.h

typedef struct{float Re; float Im;} complex;

From Python I can do something like this

import numpy as np
from ctypes import cdll
from ctypes import *

class complex(Structure):
    _fields_ = (('Re', POINTER(c_float)),
                ('Im', POINTER(c_float)))

    def __init__(self, I, Q, lenIQ):
        array_type = c_float * lenIQ
        self.Re = array_type(*I)
        self.Im = array_type(*Q)

    def __repr__(self):
        return f'Complex({self.Re}, {self.Im})'

cgmap = cdll.LoadLibrary('./test.dll')
test = cgmap.test
test.argtypes = (c_int, complex)
I = [2,2,3,4]
Q = [5,6,7,8]
lenIQ = len(I)

myarray = complex(I, Q, lenIQ)
test(c_int(lenIQ), myarray)
print(myarray)
print(myarray[0])

But when I execute test.py, then :

0 4284182633119744.000000 1.000000
1 4585659272527872.000000 1.000000
2 3.983704 1.000000
3 3.983746 1.000000
Complex(<__main__.LP_c_float object at 0x0000029959825A48>, <__main__.LP_c_float object at 0x0000029959825A48>)
Traceback (most recent call last):
  File "test.py", line 28, in <module>
    print(myarray[0])
TypeError: 'complex' object does not support indexing

Is there anyway to solve without modifying test.c? Any help would be appreciated ;)

Upvotes: 1

Views: 451

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 178031

Make sure your types match. The structure contains c_float not POINTER(c_float) to match the C definition. The .argtypes are c_int and POINTER(Complex).

ctypes can initialize the structure and arrays without a lot of extra work:

test.c

#include <stdio.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct { float Re; float Im; } complex;

API void test(int N, complex *v) {
    int k;
    for(k = 0; k < N; ++k) {
        v[k].Re = v[k].Re + 1;
        v[k].Im = v[k].Im + 1;
        printf("%d %f %f\n", k, v[k].Re, v[k].Im);
    }
}

test.py

import ctypes as ct

class Complex(ct.Structure):
    _fields_ = (('Re', ct.c_float),  # not POINTER(c_float)
                ('Im', ct.c_float))

    def __repr__(self):
        return f'Complex({self.Re:g}{self.Im:+g}j)'

    @staticmethod
    def array(reals, imags):
        '''Helper method to create Complex arrays.
        '''
        return (Complex * len(reals))(*zip(reals, imags))

I = [2,2,3,4]
Q = [5,6,7,8]

arr = Complex.array(I, Q)
print(list(arr)) # easy way to display the contents when __repr__ is defined.

dll = ct.CDLL('./test')
dll.test.argtypes = ct.c_int, ct.POINTER(Complex)
dll.test.restype = None

dll.test(len(arr), arr)

Output:

[Complex(2+5j), Complex(2+6j), Complex(3+7j), Complex(4+8j)]
0 3.000000 6.000000
1 3.000000 7.000000
2 4.000000 8.000000
3 5.000000 9.000000

Upvotes: 1

Ahmed AEK
Ahmed AEK

Reputation: 18090

you are mixing up between creating a struct, creating an array of struct, and creating a pointer to an array of struct, those are 3 different operations, i have commented the code for clarity.

import numpy as np
from ctypes import cdll
from ctypes import *

class complex(Structure):  # struct with two floats
    _fields_ = (('Re', c_float),
                ('Im', c_float))

    def __init__(self, I_val, Q_val):
        self.Re = I_val
        self.Im = Q_val

    def __repr__(self):
        return f'Complex({self.Re}, {self.Im})'

I = [2,2,3,4]
Q = [5,6,7,8]
lenIQ = len(I)
my_array_type = complex*lenIQ  # create the type of array (complex[])
my_array = my_array_type()  # create the array (complex my_array[4])

for i in range(len(I)):
    my_array[i] = complex(I[i],Q[i])  # fill each individual object in the array

# next line is equivalent to "complex* pointer_to_array = (complex*)(my_array);"
pointer_to_array = cast(my_array, POINTER(complex))  # create the pointer to the first element of the array (complex*)

print(my_array)  # prints type of my_array
print(my_array[0])  # equivalent to c's my_array[0]
print(pointer_to_array[0])  # equivalent to c's pointer_to_array[0]
<__main__.complex_Array_4 object at 0x000002195A9B8C40>
Complex(2.0, 5.0)
Complex(2.0, 5.0)

now for calling

cgmap = cdll.LoadLibrary('./test.dll')
test = cgmap.test
test.argtypes = (c_int, POINTER(complex))
test.restype  = None
test(c_int(lenIQ), pointer_to_array)

Upvotes: 1

Related Questions