7

構造体の配列をPythonリストに変換する方法を見つけようとして数日探していました。配列の先頭へのポインタを返す関数があります。

struct foo {
    int member;
};

struct foo *bar() {
    struct foo *t = malloc(sizeof(struct foo) * 4);
    ... do stuff with the structs ...
    return t;
}

Pythonから関数を呼び出した後、単一の構造体を取得しましたが、配列の他の要素にアクセスしようとするとエラーが発生します。

foo = bar()
print foo[1].member
TypeError: 'foo' object does not support indexing

私は使ってみまし%array_classたが、役に立ちませんでした。また、SWIGインターフェイスファイルで配列を返すように関数を定義してみました。

extern struct foo [ANY] bar();

SWIGのドキュメントはかなり徹底的ですが、私はこれを理解できないようです。

4

1 に答える 1

10

あなたが試したアイデアは[ANY]、いくつかの理由でうまくいきません。ただし、主ANYにタイプマップで使用して、同じタイプマップをさまざまな固定サイズの配列で動作させることができますが、これはあなたが持っているものではありません。

C の構文もそれほど正確ではありません。あなたは書くことができません:

int[4] bar() {
  static int data[4];
  return data;
}

または:

int bar()[4] {
  static int data[4];
  return data;
}

標準 C では、最も近いものは次のとおりです。

int (*bar())[4] {
  static int data[4] = {1,2,3,4};
  return &data;
}

しかし、それをラップするのはそれほど簡単ではありません。

ただし、次の%array_class例のように、単純なソリューションを使用して機能させることができます。

%module test

%inline %{
  struct foo {
    int member;
  };

  struct foo *bar() {
    struct foo *arr = malloc(sizeof(struct foo) * 4);
    for (int i = 0; i < 4; ++i) 
      arr[i].member = i;
    return arr;
  }
%}

%include <carrays.i>
%array_class(struct foo, fooArray);

これにより、次のことができます。

Python 3.2.3 (default, May  3 2012, 15:54:42) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> arr = test.fooArray.frompointer(test.bar())
>>> arr
<test.fooArray; proxy of <Swig Object of type 'fooArray *' at 0xb6f332a8> >
>>> arr[0]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33038> >
>>> arr[1]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33380> >
>>> arr[2]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33398> >
>>> arr[3]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f330c8> >
>>> 

ただし、コードを挿入して配列型へのポインターを自動的に変換することにより、(おそらく) 一歩良くなることができますbar()。SWIG で表示される前に次を追加します。

%pythonappend bar() %{
    # Wrap it automatically
    val = fooArray.frompointer(val)
%}

したがって、次のように使用できます。

Python 3.2.3 (default, May  3 2012, 15:54:42) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.bar()[1].member
1
>>> arr = test.bar()
>>> arr[3].member
3

メモリの所有権に注意する必要があります。これらの例では、これまでのところメモリがリークしています。を使用%newobjectして、メモリが Python 側によって所有されていることを SWIG に伝えることができますが、(元の戻り値が参照されなくなるとすぐに) 解放されるのが早すぎるため、元の値をより長く保持するように手配する必要があります。 . その完全な例では、元のポインターを配列クラスのインスタンス内に保存して、配列ラッパー自体が次のようになる限り参照を保持します。

%module test

%pythonappend bar() %{
    # Wrap it automatically
    newval = fooArray.frompointer(val)
    newval.ptr_retain = val
    val = newval
%}

%newobject bar();

%inline %{
  struct foo {
    int member;
  };

  struct foo *bar() {
    struct foo *arr = malloc(sizeof(struct foo) * 4);
    for (int i = 0; i < 4; ++i) 
      arr[i].member = i;
    return arr;
  }
%}

%include <carrays.i>
%array_class(struct foo, fooArray);

ただし、これが生成する配列クラスは、C の a とまったく同じように無制限であることに注意してstruct foo*ください。これは、Python でそれを反復処理できないことを意味します - サイズは不明です。サイズが本当に固定されている場合、または何らかの方法でサイズを知る方法がある場合は、PyList を返す typemap を作成することで、(私の見解では) もっとうまくラップできます。書くのは少し手間がかかりますが、インターフェイスが Python 側で見やすくなります。

于 2012-08-17T10:00:13.907 に答える