Cython:创建C结构数组

2024-04-20 00:55:17 发布

您现在位置:Python中文网/ 问答频道 /正文

几天来我一直在尝试各种方法,但似乎缺少一些关键的成分。我试图创建一个C结构数组,并从Python字典中填充这些结构。我想在类定义或一些全局函数中捕获它,因为我将在代码中的多个位置使用它。你知道吗

根据请求,我已将代码解构为其最低版本,并将其全部放在一个文件中:

FOO_BOOL    = 1
FOO_BYTE    = 2
FOO_STRING  = 3

cdef union Data:
    bint flag
    int byte
    char *string

ctypedef struct bar_t:
    char name[512]
    int type
    Data data

cdef class BarArray:
    cdef bar_t *array;
    cdef size_t nbars;

    def __cinit__(self, number):
        self.array = <bar_t*>malloc(number * sizeof(bar_t))
        if not self.array:
            raise MemoryError()
        self.nbars = number

    def load(self, keyvals:dict):
        kvkeys = list(keyvals.keys())
        if len(kvkeys) > <int>self.ninfo:
            # this should never happen except in development
            # so raise an error to let them know
            raise IndexError()
        n = 0
        for key in kvkeys:
            if keyvals[key][1] == 'bool':
                self.array[n].type = FOO_BOOL
                self.array[n].data.flag = keyvals[key][0]
            elif keyvals[key][1] == 'byte':
                self.array[n].type = FOO_BYTE
                self.array[n].data.byte = keyvals[key][0]
            elif keyvals[key][1] == 'string':
                self.array[n].type = FOO_STRING
                if isinstance(keyvals[key][0], str):
                    pykey = keyvals[key][0].encode('ascii')
                else:
                    pykey = keyvals[key][0]
                try:
                    self.array[n].data.string = strdup(pykey)
                except:
                    raise ValueError("String value declared but non-string provided")
            else:
                raise TypeError("UNRECOGNIZED VALUE TYPE")
            n += 1

    @property
    def array(self):
        return self.array

    @property
    def nbars(self):
        return self.nbars


cdef class FooClass():
    cdef bar_t *array
    cdef size_t sz
    def __cinit__(self, sz):
        self.bar = BarArray(sz)

    def loadarray(self, keyvals:dict):
        self.bar.load(keyvals)
        self.array = <bar_t*>self.bar.array
        while n < self.sz:
            print("INFO [", n, "]: ", self.array[n].name, self.array[n].type)
            n += 1

尝试编译时,出现以下错误:

warning: foobar.pyx:28:16: cdef variable 'array' declared after it is used

Error compiling Cython file:
------------------------------------------------------------
...
                raise TypeError("UNRECOGNIZED VALUE TYPE")
            n += 1

    @property
    def array(self):
        return self.array
                  ^
------------------------------------------------------------

foobar.pyx:67:19: Cannot convert 'bar_t *' to Python object

我在一个地方读到,你必须把这个返回值转换成,但这也会产生一个错误。我最终成功地找到了一种克服编译错误的方法,但是返回的数组包含垃圾。你知道吗

如有任何建议,将不胜感激。你知道吗


Tags: keyselfdatastringiffoodeftype
1条回答
网友
1楼 · 发布于 2024-04-20 00:55:17

将指针发送回Python实际上是没有意义的(因此您看到了错误)。我认为您要做的是定义Python访问器函数__setitem____getitem__

def __getitem__(self, int idx):
    if idx>=self.nbars:
        raise IndexError("Helpful error message about index being out of range")
    result = dict(name=self.array[idx].name)
    if self.array[idx].type == FOO_BOOL:
        result['flag'] = bool(self.array[idx].data.flag)
    elif self.array[idx].type == FOO_BYTE:
        result['byte'] = self.array[idx].data.byte
    elif self.array[idx].type == FOO_BOOL:
        result['string'] = self.array[idx].data.string
    else: 
        raise ValueError("Some helpful message?")
    return result

def __setattr__(self,int idx, value):
    cdef bar_t value_c
    if idx>=self.nbars:
        raise IndexError("")
    value_c.name = value['name'] # you might need to do a string copy here
                       # and you definitely want to check the length
    if 'flag' in value:
        value_c.data.bool = 1 if value['flag'] else 0
        value_c.type = FOO_BOOL
    elif 'byte' in value:
        value_c.data.byte = value['byte']
        value_c.type = FOO_BYTE
    elif 'string' in value:
        # Allocate memory for and copy the string
        value_c.type = FOO_STRING
    else:
        raise ValueError("...")

    # Deallocate any data held by existing self.array[idx]!
    self.array[idx] = value_c

请注意,您需要注意内存管理。因为您不能依赖于指向Python字符串中保存的数据的指针,所以您需要自己为它们分配内存。这还意味着您需要确保在类被破坏或替换数组元素时释放它。我跳过了编写这段代码,因为它在很大程度上取决于您使用的C库。你知道吗

相关问题 更多 >