web-dev-qa-db-ja.com

ctypesを使用してC ++関数からPython)に配列を返す方法

私はctypesを使用してPythonでC++関数を実装しています。 C++関数は、配列へのポインターを返す必要があります。残念ながら、Pythonで配列にアクセスする方法がわかりません。 numpy.frombufferを試しましたが、成功しませんでした。任意の数の配列を返しただけです。明らかに、私はそれを正しく使用しませんでした。サイズ10の配列を使用した簡単な例を次に示します。

Function.cppの内容:

extern "C" int* function(){
int* information = new int[10];
for(int k=0;k<10;k++){
    information[k] = k;
}
return information;
}

Wrapper.pyの内容:

import ctypes
import numpy as np

output = ctypes.CDLL('./library.so').function()

ArrayType = ctypes.c_double*10
array_pointer = ctypes.cast(output, ctypes.POINTER(ArrayType))
print np.frombuffer(array_pointer.contents)

私が使用しているC++ファイルをコンパイルするには:

g++ -c -fPIC function.cpp -o function.o
g++ -shared -Wl,-soname,library.so -o library.so function.o

Pythonで配列値にアクセスするために私がしなければならないことについて何か提案はありますか?

22
dolby

function.cppはint配列を返しますが、wrapper.pyはそれらをdoubleとして解釈しようとします。 ArrayTypectypes.c_int * 10に変更すると、機能するはずです。


自分でfrombufferの代わりに np.ctypeslib を使用する方がおそらく簡単です。これは次のようになります

import ctypes
from numpy.ctypeslib import ndpointer

lib = ctypes.CDLL('./library.so')
lib.function.restype = ndpointer(dtype=ctypes.c_int, shape=(10,))

res = lib.function()
17
Dougal

あなたのpythonコードは、いくつかのマイナーな変更の後に機能します:

_import ctypes

f = ctypes.CDLL('./library.so').function
f.restype = ctypes.POINTER(ctypes.c_int * 10)
print [i for i in f().contents] # output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_

基本的に2つの変更があります。

  1. numpy関連のコードと_ctypes.cast_呼び出しは必要ないので、削除します。

  2. 戻り値の型をctypes.POINTER(ctypes.c_int * 10)に指定します。

    デフォルトでは、外部関数はC int型を返すと想定されているため、目的のポインター型に変更する必要があります。

ところで、CコードからPythonコードにnewed配列を返すのは不適切なようです。誰がいつメモリを解放しますか?Pythonコードを作成し、Cコードに渡します。このようにして、Python code owns配列であり、スペースの作成と再利用を担当することは明らかです。

22
Hui Zheng