ctypes struct containing arrays and manipulating
Question:
I’m trying to use ctypes in my python code test.py
to 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 %fn", 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 😉
Answers:
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)
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 %fn", 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
I’m trying to use ctypes in my python code test.py
to 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 %fn", 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 😉
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)
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 %fn", 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