モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

SWIGオブジェクトから生のポインタを取り出すには

最近SWIGづいていますが… PythonSWIG(SWIGPythonか…?)ラッパーオブジェクトからポインタを取り出すにはどうすればいいのか?

それも、自作のクラスオブジェクトでございます。 なんでこんなことをする必要があるのかというと、SWIG化クラス「foo」があるとして…

import swig_module

bar = []
for i in range(10):
    bar.append(swig_module.foo()) # fooクラス生成

swig_module.call_swig_list(bar) # リストを突っ込む

こんな感じの時ですね。fooインスタンスは、pythonで何かするためにやむをえず作ったリスト配列の中に入っており。 それでcall_swig_listの中で取り出すと、それはPyObject* なわけです。

ここから、C++クラス「foo」のポインタを取り出して、C++コードの中で呼びたい。

「各fooインスタンスを引数にしてforループの中で呼ぶようにすればいい」という状況のほうが多そうですが、そうも行かないことがある。 まぁ例えば大量のfooインスタンスがdictの中に入っていて、そのキーも重要な情報だったりするので一遍に処理したいとか。

正直、ただのこだわりです。

結構苦しめられましたがわかりました。

  • SwigPyObjectという型(構造体)があるのだけど、これが.ccという拡張子のSWIGによる自動生成ファイルに入っている。
    • .ccファイルの中は、非常にマクロ宣言が多くキモい。
  • だから手っ取り早く使うにはもう自分で定形のを宣言してしまうのが良い。
typedef struct {
  PyObject_HEAD
  void *ptr;
  void *ty;// Dummy definition. Actually, this is swig_type_info*. 
  int own;
  PyObject *next;
  PyObject *dict;
} SwigPyObject_base;
  • さてこのptrというのがそのオブジェクトポインタか…といえばさにあらず。
  • 実は、SWIGがデフォルトでサポートしていない型の場合、露出するオブジェクトの「this」属性に、生ポインタを保持したSwigPyObject*が格納されるという、2段階構造になっている。

やっと動いたテスト用のSWIG関数だけ抽出して書いておきます。

PyObject* call_swig_list(PyObject* pylist) {
    int length = PyObject_Length(pylist);
#if PY_VERSION_HEX >= 0x03000000
    PyObject* swig_this = PyUnicode_FromString("this"); 
#else
    PyObject* swig_this = PyString_FromString("this");
#endif
    for(int i=0;i < length;i++) {
        SwigPyObject_base* o = (SwigPyObject_base*)PyList_GetItem(pylist, i);
        // Make shadow instance as real instance.
        o = (SwigPyObject_base*)PyObject_GetAttr((PyObject*)o, swig_this); // new reference.
        foo *f = (foo*)(o->ptr);
        printf("objectptr %lx \n" , f);
        f->method1(i); // これでクラス「foo」のインスタンスのメソッドを呼べる!
        Py_DECREF(o);
    }
    Py_DECREF(swig_this);
    Py_RETURN_NONE;
}

多少、強引ですが…動けばよかろうなのだァァァァッ!!

…って、動けばよかろうなら最初に書いた「python側でforループ展開」でいいだろ、って気が自分でもしますが(^_^;)

ともかくこれをつかって…ヌフフ