koguro
こんにちは、小黒と申します。
Scheme はおもしろいですね。特に再帰がこんなに便利なものだとは、Scheme を使うまで知りませんでした。
gca.el
GaucheFest:15thでgca.el (拙作のGaucheのコードを書くときのためのユーティリティ集)に単体テストケース作成の支援機能を追加しました(あとバグフィックスも)。詳しくは→GaucheFest:koguro
LL Gong
LL RingのLL Gongでc-wrapperのデモを行いました。関係者の皆様お疲れさまでした。
ちなみに、ここ2週間のc-wrapperの更新は、Ruby, Perl, Python対策のためのアップデートでした。今後は、
- 未対応箇所の実装(C99対応とか、インライン関数・マクロでswitch, gotoが使えない件など)
- パフォーマンスチューニング
- C++対応(仕様が複雑なのでいつになるか分かりませんが...)
とかいった点の対応を行おうかと考えています。
あと、会場でびさんにgdchartがc-wrapperで使えないと教えてもらいました。ヘッダファイルを見るとGDCPIE_BGColorなどといった変数を呼出側で宣言しておかなくてはならない作りになっている(ライブラリが呼出側の変数を参照するようになっている)ので現状使えないのは仕方ないのですが、ちょっと悔しかったりします。実行時にシンボルテーブルへシンボルを追加する方法が分かれば何とか対応できるかもしれないので、そこら辺を調べてみます。
- GDChartのコーディング作法がなってないってことで結論が出たようなので(控え室で)、 もしそれに対応するために、速度に問題が出るとか、扱いが面倒になるなら サポートしなくてもいいんじゃない?って思ったり。 実際GDChartはもうすでに非アクティブっぽいみたいだし。 手軽に使えるの最優先ってことで希望です。cut-sea:2006/08/27 20:07:15 PDT
- び(2006/08/27 21:57:44 PDT): 控室で「いらねんじゃね」と言い続けていた張本人が言うのもなんですが、ハックネタとしては「実行時に動的にシンボルテーブルをいじれる」というのは面白いかもしれないです。というか凶悪過ぎ(笑)。もともとc-wrapper自体がそういう「ハック魂をわしづかみ」なものなのですし、「実用性の観点からいらーん」と言ってしまうのも、ちょっと心が狭いような気がしてきました。
- koguro(2006/08/28 02:31:53 PDT): cut-seaさんがおっしゃるように"お手軽さ第一"なので、使い手に余計な負担がかかるくらいなら対応しないつもりです(というかシンボルテーブルの動的書き換えってできるのでしょうか(^^; FreeBSDのrtld.cあたりのソースを見ているのですがうまい手が思いつきません...)。これも控室で話していたことですが、stubジェネレータっぽいものを追加して、例外的なライブラリへの対応や好き勝手いじりたい人はそれをいじってもらうようにするのが現実的かもしれません。
c-wrapper
c-wrapperとは、C言語で書かれたライブラリ用のFFIです(skimuさんのggcに含まれるlang.toolをベースにlibffiを組み合わせたものです)。特徴として、ヘッダファイルをパーズして動的に定義を行うことができます。このライブラリを使うと、以下のようなコードが書けます。
(use c-wrapper) (c-load-library "libc.so") (c-include "stdio.h") (fprintf stdout "Hello, world") (fflush stdout)
ヘッダファイルから、関数定義、グローバル変数定義、型定義(typedefやstruct, union)、定数定義(#defineやenum)を読み取ることができますので、運がよければstubを書かずにいろいろなライブラリで遊べます。自分の環境ではImageMagick, esound, gtk(1.2ですが...), mecabで使えることは確認しています。
まだ荒削りなところが多々ありますが、いろいろ試してみてください。
- AMD64 FreeBSD6.0の環境で試したところ、hello.scmでcore dumpしました(詳細)。なにか勘違いしているかもしれませんが、とりあえずご報告までkatsujiro
- 上記問題、libffiをhttp://sourceware.org/ml/libffi-discuss/2006/msg00002.html のものに入れ替えたら、動くようになりました。katsujiro
- isi(2006/03/21 04:47:13 PST): libpcap で遊んでおりました。c-wrapper 0.3.0 になって Mac OS X 10.3.9 では libffi のところでコンパイルが通らなくなりました。 "darwin_closure.S:249:unknown section attribute: live_support" となります。Mac OS X 10.4 から追加された Dead-Code Stripping に関するDirective とのことです。10.3.9 には関係ないようなので live_support を取り去ることで、問題無いようです。pcap の方も動いています。そろそろ Tiger にバージョンアップしないとだめかなあ。
- koguro(2006/03/22 05:11:08 PST):おっとlibffiのバージョンアップでそんな弊害が出ましたか。MacOSX 10.3.9を使っている方がまだ多いようであれば#ifdefで対応しようかと思いますが、現状はすみませんが手動で対応してください。
- leque(2006/03/22 07:08:20 PST): おもしろいですね。お手軽に拡張ライブラリが作れて重宝しています。ところで、c-load-library が LD_LIBRARY_PATH や DYLD_LIBRARY_PATH も見てくれると便利だと思います。
--- lib/c-wrapper.scm.orig 2006-03-18 23:39:49.000000000 +0900 +++ lib/c-wrapper.scm 2006-03-22 22:54:40.000000000 +0900 @@ -85,10 +85,17 @@ (else (loop dlname libdir installed? (read-line in)))))))) +(define LIBRARY-PATHS + (remove string-null? + ((cut string-split <> ":") + (sys-getenv (match (gauche-architecture) + ([? #/apple-darwin/] "DYLD_LIBRARY_PATH") + (_ "LD_LIBRARY_PATH")))))) + (define (find-library lib search-paths) (let ((paths (if (string-scan lib "/") '(".") - (append search-paths SYS-LIBRARY-PATHS)))) + (append search-paths LIBRARY-PATHS SYS-LIBRARY-PATHS)))) (or (find-file-in-paths lib :paths paths :pred file-is-readable?) (and (not (path-extension lib)) (or (and-let* ((lafile (find-file-in-paths
- koguro(2006/03/24 07:14:05 PST):確かに環境変数も見た方がよいですね。次バージョンで対応します。
- isi(2006/03/28 07:14:38 PST): (null-ptr? (make-null-ptr)) が #f を返すので調べたのですが、下記でOKですか。
--- c-types.scm.org Sat Mar 25 21:02:50 2006 +++ c-types.scm Tue Mar 28 23:04:43 2006 @@ -232,7 +232,7 @@ (define (null-ptr? obj) (and (is-a? obj <c-basic-ptr>) - (equal? (buffer-of obj) %null-ptr-uvector))) + (equal? (buffer-of obj) (%null-ptr-uvector)))) ;; ;; pointer
- koguro(2006/03/29 05:54:09 PST): はい、その通りです。報告ありがとうございました。
- isi(2006/04/04 09:14:31 PDT): 2006/3/21の私の Mac OS X 10.3.9 についての発言ですが、どうも、とんだ嘘をついていたようです。訂正します^^; Dead-Code Stripping は Xcode 1.5 からサポートされたので、10.3.9 でも問題はないのでした。ということで先ほど自分の環境を Xcode 1.2 から 1.5 にアップして、c-wrapper 0.3.2 は何事もなく make できました。失礼致しました。
- 便利に使わせてもらっています。ところで、x->stringで作った文字列は、元のcでの文字列がメモリ上で上書きされると、メモリ上で領域を共有している為か内容が変更されてしまいますが、これをstring-copyなどでコピーしても、同じように変更されてしまいます。現在は、(compose list->string string->list)でしのいでいますが、cのスタックやヒープからgaucheのオブジェクトにコピーしてくるのに適切な方法があれば、教えてください。
- koguro(2007/09/12 06:33:09 PDT): 確かにx->stringで作る文字列領域はCの文字列と共有しているので、あとからメモリ内容が破壊されるとGauche上での文字列が変更されてしまいます。お手数ですが、現状は(compose list->string string->list)のようなコピーか、Cのプログラムで行うようにstrncpyを呼び出してコピーする(uvectorで領域を確保してそこにコピーするイメージです)かのどちらかの対応になります。
- ありがとうございます。c-wrapperでDBアクセスをするので、この文字列は大変よく使います。リスト分解→文字列構築だと、効率が気になりますので、c-wrapperの標準的なAPIを用意してくださると助かります。(malloc->strncpyは試してみます。)
- Shiro(2007/09/12 12:39:13 PDT): うーん、むしろScheme stringが持つ文字列領域はCでも常にconst char*として扱ってもらった方が後々の混乱が少ないのではないかと思いますがいかがでしょう。文字列コピーのオーバヘッドは確かに気になりますが、最近のCソースはまめにconst修飾をつけるようになっていると思いますし、デフォルトでは安全側に倒しておいて性能が必要な場合は余分な情報を与えてやる、という方針のほうがいいかなあと。(c-wrapperの中身をちゃんと見ていないので実は難しいのかもしれませんが)。
- koguro(2007/09/13 05:38:55 PDT): 確かにそうですね。そもそもCとSchemeとではメモリ領域の寿命が異なるので、現状のように領域を共有していてもあんまりメリットはないように思えました(たぶん困るケースの方が多いはず)。次バージョンではx->stringで文字列をコピーするようにします。
協調的なスレッドライブラリ
サーバアプリを作るときに「マルチスレッドにしたいけどプログラムが面倒だな」とよく思うので、協調的なスレッドライブラリを作ってみました(cthreads-0.2.tgz)。特徴は以下の通りです。
- 継続を使って実現しており、pthreadライブラリが使えない環境でも動作する。
- I/O待ちのためにthread-select!という関数を追加している。(thread-select! port flags)のように呼び出し、指定されたポートが利用可能でない場合は他のスレッドに切り替わります。flagsの指定はselector-add!と同じです。
- 決められた関数を呼び出すまでスレッドが切り替わらないので、あまりリソースのロックを気にしなくてもよい。スレッドが切り替わるのはthread-yield!, thread-sleep!, thread-terminate!, thread-join!, thread-select!, mutex-lock!, mutex-unlock!を呼び出したときのみです。
- gauche.threads, gauche.parameterとまったく同じインタフェースを持っているため、あとからパフォーマンス向上などの理由でpthread化するのが容易(ただし当然のことながら、この場合は排他制御はちゃんと実装する必要があります)。ちなみにtarballに含まれている単体テストはGauche本体のthread-test.scmとparameter-test.scmとほぼ同じコードです(モジュール名などをすこし変えた程度)。
欠点としては、
- 全スレッドが待ちの状態になってもビジーループになることはありませんが、スレッドの切り替えがあまり効率的でないので動作が重いかもしれません。
- あまりテストしていないので致命的なバグが残っている可能性があります。
び(2007/03/06 06:03:47 PST): 久々にちょっと試してみようと思ったら、テストが通らなくなっていました。
--- cthreads-0.1.orig/cthreads.scm 2006-02-10 22:51:21.000000000 +0900 +++ cthreads-0.1/cthreads.scm 2007-03-06 21:57:43.000000000 +0900 @@ -351,11 +351,11 @@ ((and (not condition-variable) (not timeout)) #t) ((and condition-variable - (eq (slot-ref condition-variable 'signal) 'notify)) + (eq? (slot-ref condition-variable 'signal) 'notify)) (slot-set! condition-variable 'signal #f) #t) ((and condition-variable - (eq (slot-ref condition-variable 'signal) 'broadcast)) + (eq? (slot-ref condition-variable 'signal) 'broadcast)) #t) ((and tlimit (time<? tlimit (current-time))) diff -urN cthreads-0.1.orig/thread-test.scm cthreads-0.1/thread-test.scm --- cthreads-0.1.orig/thread-test.scm 2006-02-10 22:18:29.000000000 +0900 +++ cthreads-0.1/thread-test.scm 2007-03-06 21:52:27.000000000 +0900 @@ -347,7 +347,7 @@ (port-test-testers 160 8 20 #t) (call-with-output-file "test.out" (lambda (outp) (port-test-kick-threads generators outp)) - :bufferling 'line) + :buffering :line) (call-with-input-file "test.out" confirmer)))
koguro(2007/03/09 20:28:51 PST): パッチありがとうございます。cthreads-0.2.tgzを公開しました。
プロファイラ
マクロの勉強をかねて、プロファイラを書いてみました。Gauche:プロファイラ
コーディングスタイル
みなさんどんな風に Scheme のソースを書かれているのでしょうか。 他の人のソースを参考にしているのですが、どこかに明文化されたものがあるとうれしいです。(C みたいに K&R とか whitesmith とかいろいろ流派があるのでしょうか)
括弧の位置
閉じ括弧の位置ですが、私は「(baz) こそがこの関数の戻り値」と考えるときは前者、「func とは単に処理を繋げたもの」と思っているときは後者を使います。
(define (func) (foo) (bar) (baz))
(define (func) (foo) (bar) (baz) )
→ Scheme:コーディングスタイル に移って広げてみましょう Shiro。
Gauche-gtk
- gdk-draw-lines に渡す <gdk-point-vector> はどうやって作ったらよいのでしょうか。ソースを見ると (make-gdk-point-vector 3) とかすれば作れそうに思えるのですが、"ERROR: unbound variable: make-gdk-point-vector" といわれてしまいます。
- Shiro: すんません。Gauche-gtk-0.2.4現在のバグです。次の2つのパッチを 適用して頂けますか。(2003/01/03 19:52:27 PST) http://lists.sourceforge.jp/pipermail/gauche-devel-jp/2002-December/000067.html http://lists.sourceforge.jp/pipermail/gauche-devel-jp/2002-December/000070.html
- koguro: ありがとうございます。うまくいきました。ML で既にパッチが流れていたのですね。