ライブラリオブジェクト化

進捗報告

所用で出払っていたため、今週はほとんど進捗なしです。
こういう時にも中身がなくても記録として記事にしておきます。
後に見直して、どの程度の頻度で作業に取り組まなかった週があるか見直すためです。

FFI

前回foreign-lambdaの話をしましたが、使うのは簡単で組み込みの構文を使うだけです。

; Cへの翻訳時にヘッダ部にコードを追加する
(foreign-declare "#include <glfw/glfw3.h>")

; Cの関数を呼び出す手続きを生成
(foreign-lambda int "glfwInit")

; Cの値を返す特定のCの式を評価
(foreign-value "GL_TRUE" int)

コード上は簡単に呼び出すことはできますが、Chicken Schemeの場合、
プラットフォームのCコンパイラを利用してネイティブに翻訳する都合上、
これらのffiコンパイラ上でしか評価できません。

また、呼び出す対象のコードがリンクされている必要があるため、
オブジェクトをリンクするなり、実行時に共有オブジェクトをロードするなりする必要があります。

ライブラリ化

csi上でffiを利用したC関数のバインディングを利用するためには、
ffiを利用したコードをコンパイルしてcsi上にloadする必要があります。

cscは-library オプションをつけることで、
Chickenのインタプリタおよび実行可能形式でload可能な
共有オブジェクト(.so)を生成することができます。

(define glfw:init (foreign-lambda int "glfwInit"))
(define gl-true (foreign-value "GL_TRUE" int))

(define glfw:terminate (foreign-lambda void "glfwTerminate"))

このコードをcsc -libarary でコンパイルして、glfw.soファイルを生成。
アプリ側のコードでは、

(load "glfw")

(if (= gl-true (glfw:init))
  (repl))
(glfw:terminate)

こんな感じで使うことができます。
こっちのアプリ側のコードはcsi上で直接loadしても、
コンパイルして実行可能形式にしても問題なく動作するようになります。

レイヤー分け

処理系の制限でFFIインタプリタ上で使えないですが、
レイヤー分けをすることでSchemeで実装する上位層のコードは、
制限を気にしないように扱っていくことができます。

このグルーコードを自前で作るかパーサーを書くかは毎回悩みますが、
残念ながらCのパーサーは複雑(プリプロセッサのため)なことと、
Cの全てのシンボルを参照するわけではないので、
utilityを作りつつグルーコード自体はパースせずに管理しようと思います。

また、レイヤー上位層も下位層もリリースビルドでは全てstaticにリンクすることで、
ある程度高速化させることができるかもしれません。
link time optimizationがあればより顕著に効果を出せると思います。