Cython函数返回malloc分配的指针

3

我对Cython相当陌生,所以这可能相当琐碎,但我无法在任何地方找到答案。

我定义了一个结构类型,并希望编写一个函数来正确初始化所有字段并返回指向新结构的指针。

from cpython.mem import PyMem_Malloc


ctypedef struct cell_t:
    DTYPE_t[2] min_bounds
    DTYPE_t[2] max_bounds
    DTYPE_t size

    bint is_leaf
    cell_t * children[4]

    DTYPE_t[2] center_of_mass
    UINT32_t count


cdef cell_t * make_cell(DTYPE_t[2] min_bounds, DTYPE_t[2] max_bounds):
    cdef cell_t * cell = <cell_t *>PyMem_Malloc(sizeof(cell_t)) # <- Fails here
    if not cell:
        MemoryError()

    cell.min_bounds[:] = min_bounds
    cell.max_bounds[:] = max_bounds
    cell.size = min_bounds[0] - max_bounds[0]
    cell.is_leaf = True
    cell.center_of_mass[:] = [0, 0]
    cell.count = 0

    return cell

然而,当我尝试编译时,编译器报告以下两个错误:

cdef cell_t * make_cell(DTYPE_t[2] min_bounds, DTYPE_t[2] max_bounds):
    cdef cell_t * cell = <cell_t *>PyMem_Malloc(sizeof(cell_t))
                        ^
Casting temporary Python object to non-numeric non-Python type
------------------------------------------------------------

cdef cell_t * make_cell(DTYPE_t[2] min_bounds, DTYPE_t[2] max_bounds):
    cdef cell_t * cell = <cell_t *>PyMem_Malloc(sizeof(cell_t))
        ^
Storing unsafe C derivative of temporary Python reference
------------------------------------------------------------

我已经搜索了很多资料,从中得知 cell 实际上被存储在一个临时变量中,该变量在函数结束时被释放。

非常感谢您的帮助。

1个回答

4
cell.min_bounds = min_bounds

这段代码并不做你想象中的事情(虽然我不确定它具体做了什么)。你需要逐个复制数组元素:
cell.min_bounds[0] = min_bounds[0]
cell.min_bounds[1] = min_bounds[1]

同样适用于max_bounds
我猜测导致你收到该错误信息的代码行是:
cell.center_of_mass = [0, 0]

这里试图将Python列表分配给C数组(记住,在C中数组和指针在某种程度上是可互换的),这并没有太大的意义。同样,您可以执行以下操作

cell.center_of_mass[0] = 0
cell.center_of_mass[1] = 0

所有这些基本符合C语言的行为,即没有将整个数组复制到另一个数组中的运算符,您需要逐个元素进行复制。


编辑:

然而,这不是您目前遇到的问题。您尚未声明PyMem_Malloc,因此它被认为是Python函数。您应该执行

from cpython.mem cimport PyMem_Malloc

请确保使用cimport而不是import


编辑2:

以下代码在我的环境中编译正常:

from cpython.mem cimport PyMem_Malloc

ctypedef double DTYPE_t

ctypedef struct cell_t:
    DTYPE_t[2] min_bounds
    DTYPE_t[2] max_bounds


cdef cell_t * make_cell(DTYPE_t[2] min_bounds, DTYPE_t[2] max_bounds) except NULL:
    cdef cell_t * cell = <cell_t *>PyMem_Malloc(sizeof(cell_t))
    if not cell:
        raise MemoryError()
    return cell

我稍微简化了cell_t(只是为了避免声明UINT32_t)。我还给cdef函数添加了一个except NULL,以允许它在需要时发出错误信号,并在MemoryError()之前添加了一个raise。我认为这些更改都与您的错误无直接关系。


不错的发现,我可能可以使用类似于某些等效的memcopy来将其缩减为一行。然而,我应该标记它失败的位置,而不是这个位置。请参阅我的更新问题。我尝试完全删除此逻辑,但仍然出现相同的错误。 - Pavlin
抱歉,但我没有在问题中包含这个 - 我认为我已经包含了它,如果我忘记包含它,错误也会大不相同。对于所有的混淆,我感到很抱歉。 - Pavlin
我已经发布了你的代码的缩短版本,对我来说编译得很好。如果仍然不起作用,我认为您需要发布完整的失败代码。 - DavidW
1
@Pavlin:几乎总是值得费心创建一个 [mcve]。这样可以让人们清楚地了解你究竟包含了什么。请注意其中的“Minimal”。在这种情况下,你的结构不需要超过一个成员。 - Martin Bonner supports Monica
@MartinBonner 我认为我的例子已经很简单明了了,所以我没有改动它。@DavidW 对不起,我真的很抱歉,我只是有点愚蠢,没有检查我如何导入 cpython.mem。我使用了 import 而不是应该使用 cimport。我想这是需要一些适应的事情。非常感谢!我完全被难住了。 - Pavlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接