【问题标题】:Passing Numpy arrays to a C function for input and output将 Numpy 数组传递给 C 函数以进行输入和输出
【发布时间】:2011-08-17 07:14:34
【问题描述】:

哦,我的话我是个傻瓜。 我只是在调用函数时省略了第二个和第三个参数。 像个傻瓜。 因为我就是这样。 原来的愚蠢问题如下:

这似乎是一件很常见的事情,但是我找不到相关的教程,我对Numpyctypes太无知了,无法自己弄清楚。

我在文件 ctest.c 中有一个 C 函数。

#include <stdio.h>

void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {
    //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {
    const double * indata = (double *) indatav;
    double * outdata = (double *) outdatav;
    int i;
    puts("Here we go!");
    for (i = 0; i < rowcount * colcount; ++i) {
        outdata[i] = indata[i] * 2;
    }
    puts("Done!");
}

(你可能猜到了,我最初的参数是 double * 而不是 void *,但不知道在 Python 端该怎么做。我当然很想把它们改回来,但我只要有效就不会挑剔。)

我用它制作了一个共享库。 gcc -fPIC -shared -o ctest.so ctest.c

然后在 Python 中,我有几个 numpy 数组,我想将它们传递给 C 函数,一个作为输入,一个作为输出。

indata = numpy.ones((5,6), dtype=numpy.double)
outdata = numpy.zeros((5,6), dtype=numpy.double)
lib = ctypes.cdll.LoadLibrary('./ctest.so')
fun = lib.cfun
# Here comes the fool part.
fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

print 'indata: %s' % indata
print 'outdata: %s' % outdata

这不会报告任何错误,但会打印出来

>>> Here we go!
Done!
indata: [[ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]]
outdata: [[ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]]

outdata 数组未修改。事实上,如果我再次调用该函数,我会得到一个段错误。这并不让我感到惊讶——我真的不知道我在这里做什么。谁能指出我正确的方向?

【问题讨论】:

  • 我没有在 C 中调用函数。我需要吗?
  • 对不起。看来您正在混合Cpython,我不知道它是如何工作的。调用C 中的puts 的事实表明它是从python 代码调用的。但我怀疑您传递给 C 函数的内容会导致问题。

标签: python numpy ctypes ffi


【解决方案1】:

虽然不是对您最初问题的直接回答,但这里有一种更方便的方式来调用您的函数。首先,让 C 函数的原型与在普通 C 中所做的完全一样。由于您不需要单独使用 rowcountcolcount,我将它们折叠成一个 size 参数:

void cfun(const double *indatav, size_t size, double *outdatav) 
{
    size_t i;
    for (i = 0; i < size; ++i)
        outdatav[i] = indatav[i] * 2.0;
}

现在按以下方式定义 ctypes 原型:

import ctypes
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./ctest.so")
fun = lib.cfun
fun.restype = None
fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
                ctypes.c_size_t,
                ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]

现在,调用您的函数将非常方便:

indata = numpy.ones((5,6))
outdata = numpy.empty((5,6))
fun(indata, indata.size, outdata)

您还可以定义一个包装器以使其更加方便:

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)

【讨论】:

  • 如果数组不连续,您可能需要numpy.ascontiguousarray(),例如numpy.arange(1, 7)[::2]
  • @J.F.Sebastian:您可以将flags="C_CONTIGUOUS" 添加到ndpointer() 调用中以动态检查数组是否是C 连续的。 (也许我应该将此添加到此答案中。)
  • 如果输入数组不连续,flags 参数会导致 TypeError
  • @J.F.Sebastian:这就是我试图通过说它添加动态类型检查来暗示的意思。
  • 你认为 TypeError 是正确的答案吗?
【解决方案2】:

只需将所有四个参数传递给 C 函数。更改您的 Python 代码:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

收件人:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))

【讨论】:

    猜你喜欢
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多