CFFIを使う

CFFIとは

Common Lispと外部ライブラリを連携させるライブラリです。
http://common-lisp.net/project/cffi/

主な用途は、

  • 多くの処理系でサポートされているのでコードの移植性が高まる
  • dllやsoといった外部ライブラリを読み込む
  • Common Lisp外の関数や変数(ポインタ、文字列)などを扱う

どうしてCFFIが必要なのか

そもそもCommon Lispにはcompile*1もcompile-file*2もあるので多くの処理系がネイティブで実行できます。さらに、compiler-macro*3やdeclare*4があるので、Common Lisp内で簡潔していれば非常に最適化も優秀です。
なので、dll*5やso*6を利用すると外部インターフェースを経由したやり取りになるので、高速化のボトルネックになってしまいます。

それでもCFFIを使わなければならない状況は多いわけです。
例えば、処理系が動作しているOS向けアプリケーションを作成しようとすると、
OSのAPIを利用しなければならないですが、現在主流なOSはCommon Lisp向けのAPIを提供していないでしょう*7
つまりAPIを実装する必要があるのですが、その橋渡しになるのがCFFIです。

CFFIのインストール

いつも通りQuicklispでインストールできます。

(ql:quickload :cffi)

使い方

CFFIを利用できるパッケージを準備する

CFFIを利用するときは主にライブラリのAPIラッパー作成なので、
ラッパー用のパッケージを生成したほうが良いでしょう。
common-lisp-user直下で作業するには不向きでしょう。

(defpackage :foreign-library
  (:use :cl :cffi))
(in-package :foreign-library)

外部ライブラリを定義する

外部ライブラリを使用する場合は、ライブラリ定義を行います。
ライブラリ定義では、プラットフォーム毎のライブラリ名や
ライブラリファイルの優先順序などを定義します。

(define-foreign-library foreign-library
  (:unix (:default "libforeign-library"))
  (:windows (:default "ForeignLibrary")))
(use-foreign-library foreign-library)

外部の関数を定義する

外部の関数を使いたい場合、そのインタフェースを定義する必要があります。

(defcfun ("foreign-function-name" function-symbol) :return-value-type
  (arg1 :arg-value-type) ...)

通常の関数定義に定義していきますが、
外部の関数なので、関数名はシンボルではなく文字列で、
返り値と引数には外部型情報を与える必要があります。
あとはfunction-symbolに外部関数インタフェースが束縛されるので、
通常の関数同様に呼び出すことができます。

(foreign-symbol 0)

その他の機能

もちろん外部ライブラリの変数と連携する機能や、
外部型定義、ポインタ、ビットフィールド、文字列など一通りのバインディング作業が可能です。
逆に外部ライブラリからLispをコールバックする機能もあります。

まとめ

CFFIを利用することでマルチプラットフォームな外部ライブラリラッパーを実装することができます。
これで、Common Lispの外の世界とも仲良くやっていけるわけですね。

*1:関数をコンパイルして高速化する機能

*2:load可能なLispのコードを高速ロードオブジェクトにコンパイルする機能

*3:コンパイル時に実行されるマクロ

*4:コンパイル時の最適化宣言を行う機能

*5:windowsの動的ロードオブジェクト

*6:unix系の共有オブジェクト

*7:客観的に見て、Common Lispはアプリケーション開発用の標準的な言語ではありません・・・