A Log In The Life.

May The Code Be With You !

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;
};

マクロ定義

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

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 の補完関数が含まれてるので、設定をすると zshbrew コマンドの補完ができるようになります。
$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


Happy Hacking.