Writing Gauche Extension Module in C
はじめに
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;
};
マクロ定義
ExampleClass
と struct 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
というのがあります。
このなかに次のように ExampleClass
を Gauche が扱うために必要なものを教えてあげます。
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 にあげておきました。
iTerm2, Emacs で meta キー + arrows
はじめに
多くの場合 iTerm2 に Left option(⌥) key acts as: +Esc を設定しておけば option キーをMeta キーとして使えます。
ですが、キーとの組み合わせではうまくいかないこともあるようです。
分割した Window を移動しやすいようにいかの設定をしてますが、これがうまく動いてくれませんでした。
(global-set-key (kbd "M-<up>") 'windmove-up) (global-set-key (kbd "M-<down>") 'windmove-down) (global-set-key (kbd "M-<right>") 'windmove-right) (global-set-key (kbd "M-<left>") 'windmove-left)
どうやら、Meta キーではなく Esc キーを送っているようです。
環境
- Mac OS X 10.7.3
- iTerm 1.0.0.20120203
- GNU Emacs 23.3.1
iTerm
iTerm の Preferences > Profiles > Keys で以下の設定をしています。
- Left option(⌥) key acts as: +Esc
Hack
Preferences > Profiles > Keys > Profile Shortcut Keys: に以下の設定をします。
Keyboard Shortcut : ⌥↑ Action : Send Escape Sequence Esc+ [1;4A
Keyboard Shortcut : ⌥↓ Action : Send Escape Sequence Esc+ [1;4B
Keyboard Shortcut : ⌥→ Action : Send Escape Sequence Esc+ [1;4C
Keyboard Shortcut : ⌥← Action : Send Escape Sequence Esc+ [1;4D
これで Emacs に Esc キーではなくちゃんと Meta キーを送ってくれます。
Appendix
- Emacs + paredit under terminal (Terminal.app, iTerm, iTerm2)
http://offbytwo.com/2012/01/15/emacs-plus-paredit-under-terminal.html
WebSocket Server を Scheme で書いてみた
WebSocket Server を RFC 6455 を読みながら Scheme で書いてみました。
といっても、単純なエコーサーバなので、入力したものが返ってくるだけです。IO多重化もしていません。
処理系は Gauche 0.9.2 です。RFC 6455 に準拠しているブラウザで動きます。
(Chrome 16でしか確認していません)
https://gist.github.com/1640609
未対応の部分や反省点はいろいろありますが、とりあえず。
Appendix
http://tools.ietf.org/html/rfc6455
https://github.com/MostAwesomeDude/txWS
zsh で brew コマンド補完
Homebrew の brew コマンドを zsh で補完します。
Homebrew には zsh の補完関数が含まれてるので、設定をすると zsh で brew コマンドの補完ができるようになります。
$fpath を通しているディレクトリに _brew としてコピーします。
cp /usr/local/Library/Contributions/brew_zsh_completion.zsh ~/.zsh/_brew
bash, fish の補完関数も用意してあるようです。
$ ls /usr/local/Library/Contributions brew_bash_completion.sh brew_fish_completion.fish brew_zsh_completion.zsh
fish って shell は初めて知りました。
参考
http://mxcl.github.com/homebrew/
ちょっと差がつくhomebrewのあまり知られていない使い方
http://fishshell.com/
zsh でバージョン指定
ちょっと古い環境に、いつも使ってる .zshrc を突っ込んだら "command not found: " でこけてしまいました。
新しいバージョンにしかない関数が、古いバージョンでこけたわけですね。
検索すると Stack Overflow にちょうどいい質問と回答があったので、メモしておきます。
How do I require a minimum version of zsh? - Stack Overflow
これによると、"is-at-version" 関数を使うと幸せになれそうです。
使い方は、回答そのままですがこんな感じです。
autoload -U is-at-least if is-at-least 4.3.9; then .... fi
コメントによると、この関数は zsh 3.1.6 から実装されているそうです。
zsh 3.1.6 は 2000-02-11 release なので、これ以前の zsh に出会うことはなかなかなさそうです。
2 つ目の回答のコメントにしっかり指摘されていますが、 $ZSH_VERSION > 4.1.8 の様に書いてしまうと、文字列の比較を行いますから、気をつけないといけません。
Appendix
- is-at-least のマニュアル : zsh: 26. User Contributions
Happy Hacking.
Hello World.
Hello Hatena Blog.
May the code be with you, Happy hacking!