ああ、私の言葉はばかです。関数を呼び出すときに2番目と3番目の引数を省略していました。馬鹿のように。それが私だからです。元の愚かな質問は次のとおりです。
これは非常に一般的なことのように思えますが、関連するチュートリアルが見つからず、Numpy
とctypes
を自分で理解するにはあまりにも無知です。
ファイル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!");
}
(ご想像のとおり、もともと引数はvoid *ではなくdouble *でしたが、Python側で何をすべきかわかりませんでした。確かに変更したいと思います。戻るが、私はそれが機能する限り好き嫌いではない。)
共有ライブラリを作成します。 gcc -fPIC -shared -o ctest.so ctest.c
それからPythonには、いくつかのnumpy配列があり、それらを入力として1つ、出力として1つ、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配列は変更されません。実際、関数を再度呼び出すと、セグメンテーション違反が発生します。それは私を驚かせません-私は本当にここで何をしているのかわかりません。誰かが私を正しい方向に向けることができますか?
4つの引数すべてを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))
元の質問に対する直接的な答えではありませんが、関数を呼び出すよりはるかに便利な方法があります。まず、プレーンCで行うのとまったく同じようにC関数のプロトタイプを作成します。rowcount
とcolcount
を別々に必要としないため、それらを単一の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)