【问题标题】:Pass data from a C function via double pointer in Cython通过 Cython 中的双指针从 C 函数传递数据
【发布时间】:2018-07-26 20:04:41
【问题描述】:

我想在 Cython 中使用一个小的 C 例程。 C函数本身是

#include <stdio.h>
#include "examples.h"

void add_array(int **io_array, int n) {
    int i;
    int *array;

    array = (int *) malloc(n * sizeof(int));

    for(i = 0; i < n; i++) {
       array[i] = i;
    }

    *io_array = array;
}

以及函数原型:

#ifndef EXAMPLES_H
#define EXAMPLES_H

void add_array(int **io_array, int n);

#endif

现在我想使用 Cython 将 C 函数与:

cdef extern from "examples.h":
    void add_array(int **io_array, int n)


import numpy as np

def add(arr):
    if not arr.flags['C_CONTIGUOUS']:
        arr = np.ascontiguousarray(arr, dtype=np.int32) 

    cdef int[::1] arr_memview = arr

    add_array(&arr_memview[0], arr_memview.shape[0])

return arr

编译时报错:

pyexamples.pyx:13:14: Cannot assign type 'int *' to 'int **'

接口这个函数的正确方法是什么?

【问题讨论】:

  • 我对 Cython 的了解不足以告诉您执行此操作的正确方法,但问题肯定是您的 C 函数设置为分配和填充 (C) 数组。您正在传递一个指向数组内容的指针,但该函数希望接收一个指向指针的指针,它可以使用内容的 location 进行更新。

标签: c arrays cython cythonize


【解决方案1】:

它不适用于 numpy-arrays 开箱即用。您必须自己进行内存管理,例如:

%%cython
from libc.stdlib cimport free
def doit():
    cdef int *ptr;
    add_array(&ptr, 5)
    print(ptr[4])
    free(ptr)   #memory management

与您的尝试不同:&amp;arr_memview[0] 是指向整数数组的指针,但您的函数需要的是指向整数数组指针的指针 - 这就是 &amp;ptr 的含义。


你的函数的问题是,它有太多的责任:

  1. 它分配内存
  2. 它初始化内存

如果add_array 只做第二部分会更容易,即

void add_array(int *io_array, int n) {
    int i;
    for(i = 0; i < n; i++) {
       io_array[i] = i;
    }
}

因此可以初始化任何内存(也可以初始化未使用malloc 分配的内存)。


但是,可以使用返回的指针 ptr 创建一个 numpy 数组,只是不太直接:

cimport numpy as np
import numpy as np

np.import_array()   # needed to initialize numpy-data structures

cdef extern from "numpy/arrayobject.h":
    void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) #not include in the Cython default include

def doit():
    cdef int *ptr;
    add_array(&ptr, 5)

    # create numpy-array from data:
    cdef np.npy_intp dim = 5
    cdef np.ndarray[np.int32_t, ndim=1] arr = np.PyArray_SimpleNewFromData(1, &dim, np.NPY_INT32, ptr)
    # transfer ownership of the data to the numpy array:
    PyArray_ENABLEFLAGS(arr, np.NPY_OWNDATA)
    return arr

以下值得一提:

  1. 需要np.import_array() 才能使用 numpy 的所有功能。 Here is an example 会发生什么,如果 np.import_array() 没有被调用。
  2. PyArray_SimpleNewFromData之后,数据本身不属于生成的numpy数组,因此我们需要启用OWNDATA-flag,否则会出现内存泄漏。
  3. 不明显,生成的 numpy-array 可以负责释放数据。例如,可以使用 Python's memory allocator,而不是使用 malloc/free。

我想详细说明上面的第 3 点。 Numpy 使用一个特殊的函数来为数据分配/释放内存——它是PyDataMem_FREE,并使用系统的free。所以在你的情况下(在add_array中使用系统的malloc/free)一切都很好。 (PyDataMem_FREE 不应与PyArray_free 混淆,就像我在早期版本的答案中所做的那样。PyArray_free 负责释放其他元素(数组本身,以及维度/步幅数据,而不是数据内存) numpy-array,see here,根据 Python 版本不同)。

更灵活/安全的方法是使用PyArray_SetBaseObject,如SO-post 所示。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2018-09-13
  • 2021-02-24
相关资源
最近更新 更多