6

共有 C ライブラリをいくつかの Python コードに接続しようとしています。ライブラリとのインターフェースは次のようなものです

typedef struct{
    int v1;
    double* v2} input;

このようなタイプが他に 2 つあります。構成用と出力タイプです。

ctypes Structure次のように sを使用して、Python でこれらの構造体を設定します。

class input(Structure):
    _fields_ = [("v1",c_int),("v2",POINTER(c_double)]

C コードには、その構造体へのポインタを受け取るいくつかの関数があり、argtypes は次のように定義されています。

fun.argtypes = [constraints,input,POINTER(input)]

constraintsint構成用のいくつかのフィールドを 持つ別の構造体です。

まず、入力構造体の v2 フィールドを更新します

input.v2 = generated_array.ctypes.data_as(POINTER(c_double))

それから私はそれを呼びます:

fun(constraints,input,byref(output))

関数プロトタイプは struct と * を struct に要求します (出力構造体型は入力構造体型と同じであると想定されます)。

次に、出力の v2 フィールドに格納された fun の結果にアクセスしたいと思います。しかし、私は予期しない結果を得ています。これを行うためのより良い/正しい方法はありますか?

ここでよく検索してドキュメントを読みましたが、何が問題なのかわかりません。エラー メッセージはありませんが、共有ライブラリから受け取る警告は、これらのインターフェイスに間違いがあることを示しているようです。


私は問題を見つけたと思います:

メソッドを呼び出すと、複雑な配列が呼び出されます。次に、4 つのベクトルを作成します。

    out_real = ascontiguousarray(zeros(din.size,dtype=c_double))
    out_imag = ascontiguousarray(zeros(din.size,dtype=c_double))
    in_real  = ascontiguousarray(din.real,dtype = c_double)
    in_imag  = ascontiguousarray(din.imag,dtype = c_double) 

ここで、din は入力ベクトルです。私はこの方法でメソッドをテストしました:

    print in_real.ctypes.data_as(POINTER(c_double))    
    print in_imag.ctypes.data_as(POINTER(c_double))
    print out_real.ctypes.data_as(POINTER(c_double))
    print out_imag.ctypes.data_as(POINTER(c_double))

結果は次のとおりです。

    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>

どれも同じところを指しているようです。

いくつかの変更の後、期待どおりに動作します...


何度かテストを行った結果、最初はコードがほぼ正しいことがわかりました。Structure インスタンスを一度作成し、そのフィールドを更新していました。の呼び出しごとに新しいインスタンスを作成するように変更しましたfun。また、すべての配列型を同等の ctypes 型に変更しました。これにより、機能が期待どおりに機能するように見えました。

印刷の動作は上記のテストのように保持されますが、関数はこの奇妙な動作でも動作するようです。以下の@ericsunコメントで指摘されているように正しいです。

4

1 に答える 1

3

完全な関数プロトタイプなしで推測しているだけですが、おそらく配列の長さのフィールドがありますstructintこれが実際に当てはまる場合、ここに役立つ例があります。

まず、共有ライブラリでテスト関数をコンパイルする必要があります。入力配列を単純に 2 倍します。

import os
import numpy as np
from ctypes import *

open('tmp.c', 'w').write('''\
typedef struct {
    int v1; double *v2;
} darray;
int test(darray *input, darray *output) {
    int i;
    /* note: this should first test for compatible size */
    for (i=0; i < input->v1; i++)
        *(output->v2 + i) = *(input->v2 + i) * 2;
    return 0;
}
''')
os.system('gcc -shared -o tmp.so tmp.c')

次に、ctypes 定義を作成します。aから aclassmethodを作成するために aを追加しました:darraynumpy.ndarray

c_double_p = POINTER(c_double)

class darray(Structure):
    _fields_ = [
      ('v1', c_int),
      ('v2', c_double_p),
    ]
    @classmethod
    def fromnp(cls, a):
        return cls(len(a), a.ctypes.data_as(c_double_p))

lib = CDLL('./tmp.so')
lib.test.argtypes = POINTER(darray), POINTER(darray)

テスト:

a1 = np.arange(3) + 1.0
a2 = np.zeros(3)
print 'before:', '\na1 =', a1, '\na2 =', a2 

lib.test(darray.fromnp(a1), darray.fromnp(a2))
print 'after:', '\na1 =', a1, '\na2 =', a2 

出力:

before: 
a1 = [ 1.  2.  3.] 
a2 = [ 0.  0.  0.]
after: 
a1 = [ 1.  2.  3.] 
a2 = [ 2.  4.  6.]
于 2013-03-15T01:49:01.763 に答える