SWIGでPythonラッパを書いてみる

何度も挫折したSWIGにチャレンジ.今回はPythonラッパを書くことに成功した,やほーい!以下,自分用メモ

Cコードの作成
#include <stdio.h>

void
print_hoge (int n)
{
  int i;
  for (i = 0; i < n; i++) {
    printf("hoge\n"); 
  }
}
.iファイルの作成

こんな感じのexample.iファイルを作成する.ラッパから利用したいファイルをexternする.

%module example
%{
/* Write include header (Optional) */
/* #include "example.h" */
%}

extern void print_hoge (int n);
ラッパの作成
% swig -python example.i
% gcc -c example.c
% gcc -c example_wrap.c
% gcc -shared example.o example_wrap.o -o _example.so

これで,_example.soが作成される.

利用方法
% python
>>> import example
>>> example.print_hoge(5)
hoge
hoge
hoge
hoge
hoge

C構造体の利用方法

少しはまったのでメモ.例えば以下のようなコードを用意

#include <stdio.h>
#include "hoge.h"

Data *
data_new (int id, char *name)
{
  Data *d = malloc(sizeof(Data));
  if (d == NULL) {
    perror("malloc");
    exit(1);
  }
  d->id = id;
  d->name = malloc(sizeof(char) * (strlen(name) + 1));
  if (d->name == NULL) {
    perror("malloc");
    exit(1);
  }
  strcpy(d->name, name);
  return d;
}

void
data_print (Data *d)
{
  printf("id=%d\n", d->id);
  printf("name=%s\n", d->name);
}
typedef struct data_t
{
  int id;
  char *name;
} Data;


さっきと同様の手順でラッパーを作ればよい.test.iの書き方に注意

%module test
%{
#include "test.h"
%}
extern Data *data_new (int i, char *n);
extern void data_print (Data *d);

この場合だと,構造体の各要素にアクセスすることができない.
最初の%{%}に囲まれた部分については,ラッパークラスは知らないものとされるらしい.

>>> import test
>>> d = test.data_new(4649, "sleepy_yoshi")
>>> d.id = 1234
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'PySwigObject' object has only read-only attributes (assign to .id)

というわけで,ラッパークラスにも内容を教えてあげる必要がある.

%module test
%{
#include "test.h"
%}
/* #include "test.h"ではうまくいかなかった */

typedef struct data_t
{
  int id;
  char *name;
} Data;

extern Data *data_new (int i, char *n);
extern void data_print (Data *d);

なぜか,#includeではうまくいかなかったので,ベタ書きした.

>>> import test
>>> d = test.data_new(4649, "sleepy_yoshi")
>>> d.id = 1234
>>> test.data_print(d)
id=1234
name=sleepy_yoshi

うまくアクセスできるようになった.いかんせん,Pythonをほとんど使ったことがないので,test.xxxというようにモジュール名を書くのがだるいなぁ,とか
クラス定義などどうするのかということがよくわかっていない.

とりわけ速いわけでもないし,とりわけPythonでしかできないこともなかったので,とくに使ってこなかったけれど,ラッパーが作りやすい(正確には,自分がPythonしか知らない)ので,これからしばらくPythonを書こうかな.

おまけ: Makefile

今回はこんなMakefileを使ってみた.

_test.so: test.o test_wrap.o
	gcc -shared test.o test_wrap.o -o _test.so

test.o: test.c
	gcc -c test.c

test_wrap.o: test_wrap.c
	gcc -c test_wrap.c -I/usr/include/python2.5

test_wrap.c: test.i test.c test.h
	swig -python test.i