Python Ctypes 0 sized array?

空扰寡人 提交于 2020-08-06 05:42:17

问题


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

回答1:


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 <stdlib.h>

#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:

<class '__main__.LP_Test5'> 5 [0, 1, 2, 3, 4]
10
<class '__main__.Test7'> 7 [1, 2, 3, 0, 0, 0, 0]
6


来源:https://stackoverflow.com/questions/62987818/python-ctypes-0-sized-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!