SWIG がスゴイ件

スクリプト言語から C のライブラリを使うためのラッパを作るなら SWIG です。使ってみると、これが思っている以上に強力でエレガントにラップしてくれることに気づきます。

SWIG のすごさというのは、各言語の最大公約数の部分をうまく共通化させてくれるところだ。たとえば、現代的な言語はどれもクラス、文字列型、配列、例外機構といったものを備えているわけだから、それらを自動で相互に変換して欲しい。class は別言語のクラスに、std::vector は配列に、std::string は文字列に自動で読み替えて欲しい。もちろん、それ以外に表現方法があるのはわかるんだけど、他言語とのインターフェースという限られた用途に最適性を求めるのは不毛だろう。

いろんな人が言及しているので、二番三番煎じなんですが・・・。ちょっと作ったサンプル。こういうのがあっさりできる。

sample.i

%module sample

%include "std_string.i"
%include "std_vector.i"

namespace std {
  %template(StringArray) vector;
  %template(IntArray) vector;
}

%include "sample.h"

%{
#include "sample.h"
%}

sample.h

#include 
#include 

class MyException {
public:
  MyException(const std::string& s) : str(s) {}
  std::string get_str() const { return str; }
private:
  std::string str;
};

std::vector intarray_to_strarray(const std::vector& l);
void throw_exception() throw(MyException);

sample.cc

#include "sample.h"
#include 

std::vector intarray_to_strarray(const std::vector& l)
{
  std::vector v;
  for (size_t i = 0; i < l.size(); i++) {
    std::stringstream ss;
    ss << l[i];
    v.push_back(ss.str());
  }
  return v;
}

void throw_exception() throw(MyException) {
  throw MyException("hogehoge");
}

少なくとも、sample.h と sample.cc はきわめてふつうのプログラムだ。そして、sample.i は実質的に何も書いてない。これだけで動くのだ。template クラスを明示的に実体化させないと怒られる(かつ、sample.h の include より前に書かないといけない)ようだが、他の作業は全自動。throw を明示的に書いているのは、明示的に書くと使用する側の言語固有の例外機構に置き換えてくれるため。

ここまででもかなり”きてる”。これをスクリプト言語から使うと、さらに驚かされることになる。python で使ってみよう。ちなみに python 用の so を作るのは、これまた感動的で、以下のような setup.py を用意するだけだ。この辺は上のサイトからぱくっただけ。

setup.py

from distutils.core import setup, Extension

setup(
    ext_modules = [
    Extension('_sample', ['sample.i', 'sample.cc'],
              library_dirs=,
              libraries=,
              extra_compile_args=,
              extra_link_args=)
    ])
python setup.py build_ext --swig-cpp

を実行すると、build/lib.XXX/_sample.so ができるので、とりあえずこれをカレントディレクトリにコピー(ホントは install するのかな)。非常に楽ちん。

Python 2.5 (r25:51908, Dec 14 2006, 11:32:54)
[GCC 4.0.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sample
>>> l = [1,2,3,4]
>>> sample.intarray_to_strarray(l)
('1', '2', '3', '4')
>>> sample.throw_exception()
Traceback (most recent call last):
  File "", line 1, in 
sample.MyException:  >
>>> try:
...     sample.throw_exception()
... except sample.MyException, e:
...     print(e.get_str())
...
hogehoge
    
どうだろう。int のリストが std::vector にそのまま渡せる。std::vector の戻り値が str のタプルに変換されてる。初めて使うとびっくりする。例外もナチュラルだ。少なくともこの段階で、ふつうにプログラムを書くのと全く遜色がない。ここまで読んだら SWIG を使う気になるであろう。