blobを扱う

メモリをどう扱うか

普段Chicken Schemeを利用しているので、Cとの連携で困ることは少ないのですが、
一つ問題になることはCデータのメモリ管理になります。

基本的にCはデータの寿命を管理しないので、
全てのメモリはプログラマが明示的に管理することになります。
使いたいと思うようなCで書かれたライブラリも、
大体がこの流儀に従っているためアロケーション管理が必要になります。

Chicken Schemeには当然GCがありますので、
そちらに寿命管理を任せることを想定してみます。

BLOB

真っ先に思いつくアロケータが make-blob です。
bytevectorやu8vectorとも言われますが、いわゆるバイト(8bit)の固定長列データです。

(define (make-my-object)
  (make-blob 8))

set-finalizer!

自由なサイズで扱えるアロケータが手に入りましたので、次は解放処理を考えます。

GCの挙動は言語的な規定がないためどうしても処理系依存になりますが、
Chicken Schemeの場合、set-finalizer! というフック処理があります。

(define (dispose-my-record! my-record)
  (print x " is dead."))

(define (make-my-record)
  (set-finalizer! (make-blob 8) dispose-my-record!))

オブジェクトに解放時のフック手続きを設定して、
GCにオブジェクトが回収された際に呼び出されるようになります。

GCに回収されるまで気長に待ってられないときもあるので、
直接呼び出すことも想定しています。

location

Chicken Schemeの blob は foreign-type-specifiers では unsigned char* になっているため、
直接foreign-lambda でblobを指定して渡すと、Cコンパイラ側がポインタ型の不一致で警告を出します。

location を経由することで、Cコンパイラにはvoid* として解釈するように渡せるようになるため、
Cコンパイラの警告を回避することができます。

(define (my-c-function my-record)
  (define f (foreign-lambda void "my_func" c-pointer))
  (f (location my-record)))

まとめ

こんな感じでChicken Schemeの機能を利用して、
C関数用の構造化データを取り扱ってみます。

ユーティリティを使うと多機能なものもあるかもしれないですが、
多くの場合は、使用するCライブラリの実装に縛られて必要になるケースなので、
こういうシンプルな解決策の利用にとどめておきたいと思っています。