読者です 読者をやめる 読者になる 読者になる

A Log In The Life.

May The Code Be With You !

Writing Gauche Extension Module in C

gauche lisp

はじめに

Gauche の拡張を C で書く際のメモです。 そもそもは『C のライブラリを Gauche Extention としてラップしたい』という動機があります。 その際に、あちこち躓いたので、整理を兼ねてまとめてみました。 ですので、いろいろと間違えていることもあると思います。どうぞご指摘ください:-)

環境

このメモは以下の環境を想定しています。API は将来変更になるかもしれません。 (おそらく変更されるでしょう)

Gauche/C API のドキュメント

いまのところ、Gauche 自体のドキュメントには拡張をの書き方や内部のインターフェイスに関するドキュメントはありません。おそらく、今後変わっていくためでしょう。 それでも、手がかりはいろいろとあります。

まず、次のブログエントリーがわかりやすかったです。

つぎに、FFI については practical-scheme にメモがあります。

さらに、Gauche のソースの examples 下の spigot と mqueue-cpp が参考になります。

  • spigot: C で書かれた拡張

    Gauche-0.9.3/example/spigot

  • mqueue-cpp: C++ で書かれた拡張

    Gauche-0.9.3/example/mqueue-cpp

スケルトン

基本的には gauche-package でスケルトンを作っていくことになります。 詳しくは "gauche-packageを利用したgaucheの拡張モジュールの作り方"" にある通りなので、ここでは手順だけメモしておきます。

$ gauche-package generate example
$ cd example
$ ls
DIST            Makefile.in     configure.ac    example.c       example.h       example.scm     examplelib.stub test.scm
$ ./DIST gen
$ ls
DIST            Makefile.in     autom4te.cache  configure       configure.ac    example.c       example.h       example.scm     examplelib.stub test.scm
$ ./configure
$ make
$ make check

これで一通りの Gauche Extention Module ができました。

ScmForeignPointer

Gauche:FFI にあるように C の構造体をラップするには ScmForeignPointer マクロを使います。

ScmForeignPointerオブジェクトは、(staticにではなく)初期化時にScmClass構造体を作るんで、 ヘッダで宣言しておくのはクラス構造体へのポインタだけでいい。 Cソース側では初期化ルーチンで Scm_MakeForeignPointerClass を呼ぶと 新しいScmClassへのポインタが帰ってくるのでそれを保存しとく。 SCM_CLASS_DECLとかは不要。boxerについてもScm_MakeForeignPointerが有る程度面倒を見てくれる。


Gauche:FFI より引用

構造体のラップ

実際に C の構造体をラップするときにはおそらく次のようなものが必要です。

  • C の構造体に対応する ScmClass と type
  • xx_print と xx_cleanup 関数
  • モジュールの初期化の設定

(※ 僕が勝手にこうするんじゃないかな?と推測したものなので、過不足があるかもしれません。)

次のように example 構造体があるとします。これを Gauche で扱えるようにラップしていきます。 説明は適当です:p

struct example {
  int x;
};

マクロ定義

ExampleClassstruct example を扱うマクロを定義します。 これらは下の examplelib.stub 内の define-type で使います。

example.h

extern ScmClass *ExampleClass;

#define EXAMPLE_P(obj)      SCM_XTYPEP (obj, ExampleClass)
#define EXAMPLE_UNBOX(obj)  SCM_FOREIGN_POINTER_REF (struct example*, obj)
#define EXAMPLE_BOX(obj)    Scm_MakeForeignPointer (ExampleClass, obj)

Gauche 内での表示とメモリ解放のための関数

たぶん、そんな感じの関数だと思います。これらの関数は下の Scm_MakeForeignPointerClass の第3引数と第4引数として渡します。

example.c

ScmClass *ExampleClass;

static void example_class_print (ScmObj obj, ScmPort* out, ScmWriteContext* ctx)
{
  struct example* e = EXAMPLE_UNBOX(obj);
  Scm_Printf(out, "#<example-class \"%p\">", e);
}

static void example_class_cleanup (ScmObj obj)
{
  struct example* e = EXAMPLE_UNBOX(obj);
  free(e);
}

タイプの定義

struct example*に対応する Gauche内の型(?)を定義します。

examplelib.stub

(define-type <example-class> "struct example*" "example class" "EXAMPLE_P" "EXAMPLE_UNBOX" "EXAMPLE_BOX")

初期化

example.c 内で 初期化のための関数Scm_Init_exampleというのがあります。 このなかに次のように ExampleClassGauche が扱うために必要なものを教えてあげます。

example.c

ExampleClass =
  Scm_MakeForeignPointerClass(mod,
                      "<example-class>",
                              example_class_print,
                              example_class_cleanup,
                              SCM_FOREIGN_POINTER_KEEP_IDENTITY | SCM_FOREIGN_POINTER_MAP_NULL);

まとめ

これで C の構造体をラップできたはずです。 あとはこれを Gauche から扱うための関数を書いてあげればいいでしょう:D

とは書いたものの、各関数やマクロが何をしているかなんとなくしかわかってません:p

本当にこれでメモリリークしてないのか?とか気になります...がひとまずこんなところで:p

Gist

これらをまとめたものは Gist にあげておきました。

https://gist.github.com/2730090