I have look on many places for this, and have tried using cast, but had no success.
I have a struct in c like:
struct t{
int x;
struct y[0];
};
How would I resize the array of struct y? I tried the other types of this issue on SO (does python ctypes supports size-0 array?), but kept on getting this error.
TypeError: incompatible types, y_Array_20 instance instead of y_Array_0 instance
解决方案
My other answer you reference just has an example of returning a sized structure, and as the comments in that answer indicate, using a sized array time prevents accidently reading past the end of the array. This example below shows a way to size the array whether the DLL function returns one or the user creates one and passes it in.
test.c:
#include
#define API __declspec(dllexport)
typedef struct Test {
int size;
int arr[0];
} Test;
API int Test_use(Test* test) {
int sum = 0;
for(int i = 0; i < test->size; ++i)
sum += test->arr[i];
return sum;
}
API struct Test* Test_alloc(int size) {
struct Test* t = malloc(sizeof(struct Test) + size * sizeof(int));
if(t != NULL) {
t->size = size;
for(int i = 0; i < size; ++i)
t->arr[i] = i;
}
return t;
}
API void Test_free(struct Test* test) {
free(test);
}
test.py:
from ctypes import *
class Test:
# return a customized Test instance of the correct array size
def __new__(cls,size,*init):
if len(init) > size:
raise ValueError('too many initializers')
# "type" creates new types.
# type(name_of_type,bases,dict_of_attributes)
# It is instantiated with the size and initial array values.
return type(f'Test{size}', (Structure,), {
'_fields_': [('size',c_int),
('arr',c_int * size)]})(size,(c_int * size)(*init))
# This is the pointer type the functions need.
# In this case Test(0) returns a Test0 instance.
# "type" is used to get the type the pointer references.
PTEST = POINTER(type(Test(0)))
dll = CDLL('./test')
dll.Test_use.argtypes = PTEST,
dll.Test_use.restype = c_int
def Test_alloc(n):
# The DLL function returns the generate Test0 type,
# so this helper casts it to the TestN pointer type.
t = dll.Test_alloc(n)
return cast(t,POINTER(type(Test(t.contents.size))))
dll.Test_alloc.argtypes = c_int,
dll.Test_alloc.restype = PTEST
def Test_use(t):
# The DLL function needs the Test0 pointer type,
# so this helper casts it.
return dll.Test_use(cast(t,PTEST))
dll.Test_free.argtypes = PTEST,
dll.Test_free.restype = None
def Test_free(t):
# The DLL function needs the Test0 pointer type,
# so this helper casts it.
dll.Test_free(cast(t,PTEST))
t = Test_alloc(5)
print(type(t),t.contents.size,list(t.contents.arr))
print(Test_use(t))
Test_free(t)
n = Test(7,1,2,3)
print(type(n),n.size,list(n.arr))
print(Test_use(byref(n)))
Output:
5 [0, 1, 2, 3, 4]
10
7 [1, 2, 3, 0, 0, 0, 0]
6