をstruct a { }
定義しています。私の C 関数は、参照によって の配列を受け取り、そのstruct a
中にデータを入力します。したがって、引数を受け入れますstruct a **
。SWIG インターフェイスを使用して Python からこの関数を呼び出したいと思います。それを行う方法はありますか?
1 に答える
これは SWIG と Python で行うことができます。次の test.h ファイルを deomstrate に設定しました。
struct a {
int val;
};
static void populate(struct a **list) {
int count = 0;
// Terminate when NULL entry found
while (*list) {
(*list)->val = count++;
++list;
}
}
populate 関数はstruct a**
、リストが NULL で終了し、リストの各要素に対して何かを行うと仮定して、説明したとおりに取得します。
これを Python に公開するために私が選択した方法は、整数、つまり操作するリストのサイズを受け取り、結果のリストを返す関数として使用することでした。私の見解では、引数」が設定されています。
基本的なモジュール ファイルをセットアップします。
%module test
%{
#include "test.h"
%}
次に、Python 関数に指定されたサイズに基づいて入力リストを準備する typemap を追加しました。
%typemap(in,numinputs=1) struct a ** (int len=0) {
len = (int)PyInt_AsLong($input);
$1 = malloc(sizeof(struct a*)*(len+1));
$1[len] = NULL;
for (int i = 0; i < len; ++i) {
$1[i] = malloc(sizeof(struct a));
}
}
基本的に、引数を整数として扱い、ポインタの配列にメモリを割り当ててから、要素自体が指すオブジェクトにメモリを割り当てます。(SWIG/Python が各項目の参照カウントを個別に処理できるように、それらを個別に割り当てました。これは、すべての要素に 1 つのブロックを割り当てるよりも簡単です)
その typemap を作成したら、別の typemap を追加しました。この typemap は、関数を呼び出した結果を受け取り、それを PyList に変換し直します。
%typemap(argout) struct a ** {
// Push into PyList for return
$result = PyList_New(len$argnum);
for (int i = 0; i < len$argnum; ++i) {
PyObject *element = SWIG_NewPointerObj(SWIG_as_voidptr($1[i]), SWIGTYPE_p_a, SWIG_POINTER_OWN);
PyList_SET_ITEM($result, i, element);
}
}
len
in typemap で作成した変数を利用していますが、NULL で終了しているため、長さをもう一度カウントすることもできます。struct a
次に、Python リストの各項目に、関数に渡したそれぞれのラップされたオブジェクト (SWIG/Python が所有) を入力しpopulate
ます。タイプ名が異なる場合はSWIGTYPE_p_a
、適切な名前に変更する必要があることに注意してくださいSWIGTYPE
。(これらは、生成されたラッパー ソースから見つけることができます)。
最後に、C 側のリスト用に持っていたメモリの割り当てを解除する必要があります。
%typemap(freearg) struct a ** {
free($1);
}
次に、これらのタイプマップを使用して、SWIG にヘッダー ファイル自体をラップするように依頼します。
%include "test.h"
私はこれをコンパイルしました:
swig -python -Wall test.i gcc -Wall -Wextra test_wrap.c -I/usr/include/python2.6 -o _test.so -std=c99 -shared
次に、次の Python を実行して確認しました。
import test
r=test.populate(100)
print r[0].val
この例から注意すべき点がいくつかあります。
- エラーチェックはありません。たとえば、
test.populate("Hello world")
悪いことをしたり、PyList_New
失敗したりする可能性があり、失敗した場合は、リストだけでなく個々の要素を解放する必要があります - メモリ所有権のセマンティクスが異なる場合は、
SWIG_POINTER_OWN
適切に変更する必要があります - Python インターフェイスでリストのサイズを指定する必要がない場合 (たとえば、既知であるか、他の方法で修正されている場合)、
numinputs=1
を 0 に変更し、len
それに応じて in typemap を設定できます。 - 関数が、NULL で終了するリストを使用する代わりに、リストとその長さを指定する整数を取る場合、これを複数引数の typemapに拡張できます。
この例をコンパイル/実行するために必要なすべてのコードはこの回答に含まれていますが、1 つの便利な tarball でそれが必要な場合は、自分のサイトにも配置しました。ただし、そのリンクが壊れたとしても、答えは引き続き機能します。