YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

PythonでCのラッパを書く

動機

ふと思い立った。簡単なサンプルを書いたつもりだったけど、思わぬところでちょっとはまったからメモ。

方法

サンプルコード

基本的にはこちらの写経。

Cのラッパを書いたのは2度目だけど、前回はバインディング部分はほとんど書いてなかったし、そもそもMacではなかったんで今回は新鮮。コードは上記サイトがなくなった時のために写させてもらいます。

  • test.c
#include <stdio.h>

int add(int x, int y) {
    return x + y;
}

void out(const char* address, const char* name) {
    printf("こんちはー、おいどんは%s%sです。\n", address, name);
}
  • testWrapper.c
#include "Python.h"

extern int add(int x, int y);
extern void out(const char* address, const char* name);

PyObject* test_add(PyObject* self, PyObject* args) {
    int x, y, g;
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
        return NULL;
    g = add(x, y);
    return Py_BuildValue("i", g);
}

PyObject* test_out(PyObject* self, PyObject* args, PyObject* kw) {
    const char* address = NULL;
    const char* name = NULL;

    static char* argnames[] = {"args", "name", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kw, "|ss", argnames, &address, &name))
        return NULL;

        out(address, name);
        return Py_BuildValue("");
}

static PyMethodDef testmethods[] = {
    {"add", test_add, METH_VARARGS},
    {"out", test_out, METH_VARARGS | METH_KEYWORDS},
    {NULL},
};

void inittest() {
    Py_InitModule("test", testmethods);
}
コンパイル(モジュール作成)

これをコンパイル。ここで若干はまった。まずは通常のLinux等の場合。

gcc -fpic -o test.o -c test.c
gcc -fpic -I/usr/lib/python2.5 -o testWrapper.o -c testWrapper.c
gcc -share test.o testWrapper.o -o testmodule.so

Mac OS X 10.4を使っていますが、執筆時の環境ではgccは4.0.1です。上のコンパイルを行おうとすると、

$ gcc -fpic -o test.o -c test.c
test.c:1: warning: -fpic is not supported; -fPIC assumed

でまず警告がでます。これは-fPICになおします。あとPython.hの場所はMacPortsで入れたので当然上記と異なります。さらに最後モジュールを作成するところでは、こんな感じで失敗します。

$ gcc -share test.o testWrapper.o -o testmodule.so
i686-apple-darwin8-gcc-4.0.1: unrecognized option '-share'
/usr/libexec/gcc/i686-apple-darwin8/4.0.1/ld: Undefined symbols:
_main
_PyArg_ParseTuple
_PyArg_ParseTupleAndKeywords
_Py_BuildValue
_Py_InitModule4
collect2: ld returned 1 exit status

これは原因が2つあって、「オプションが間違っていること」と「定義を見に行けてない」ことがそれです。前者に関してはここが参考になります。

Macではgccの-sharedオプションは-bundleにしてください。さらにこれだけではまだ/opt/local/lib/python2.5/lib-dynloadにあるダイナミックライブラリを見に行けてないので、"-undefined dynamic_lookup"オプションをつけます。

それがこれ。

gcc -fPIC -o test.o -c test.c
gcc -fPIC -I/opt/local/include/python2.5 -o testWrapper.o -c testWrapper.c
gcc -undefined dynamic_lookup -bundle test.o testWrapper.o -o testmodule.so
テスト

これでモジュールが作成されます。あとはちゃんと使えるのかテスト。

$ python
Python 2.5.2 (r252:60911, Dec 13 2008, 22:26:36) 
[GCC 4.0.1 (Apple Computer, Inc. build 5370)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.add(4, 5)
9
>>> test.out("Japan", "ymotongpoo")
こんちはー、おいどんはJapanのymotongpooです。