外部モジュールの呼び出し

clispでCの関数を呼ぶ方法。

概要

clispでCの関数を呼ぶには、FFI(Foregin Language Interface)を使う方法と、External Moduleを使う方法がある。
ドキュメントから特徴を抜粋すると、

Extenal Module FFI 備考
速度 ×
移植性 × FFIはすべてのclisp環境で使えるわけではない
サイズ ×
インターフェイス × FFIでは&optionalとかは使えない

で、今回使うのはExteranl Moduleのほう。

環境

今回使う環境。

ディレクトリの準備

まず、カレントディレクトリにlinkkit/へのシンボリックリンクを作る。
この中には、clisp.hなどモジュールを作るのに必要・便利なツールが入っている。
このディレクトリはlink setと呼ばれる。

$ ln -s /opt/local/lib/clisp/linkkit/ .
$ ls
linkkit@

次にmodule setと呼ばれるディレクトリを作る。
これは、clisp-linkに自動生成してもらう。

$ sh /opt/local/lib/clisp/clisp-link create-module-set test
$ ls
linkkit@  test/
$ ls test
Makefile link.sh

コーディング

module setの中にtest.cを作って、適当なコードを書く。
ここはまだ、詳しく調べいない。

// test/test.c
#include<stdio.h>
#define COMPILE_STANDALONE
#include "clisp.h"
DEFMODULE(test,"TEST")
DEFUN(TEST::MSG,){
  printf("hello\n");
}

あと、Makefileをちょっといじくる。
特に、test.cはそのままではCのコードにならないので、linkkit/modprep.lispでCのコードに変換してやる必要がある。

# test/Makefile
# Makefile for CLISP module set test
CC =
CPPFLAGS =
CFLAGS =
INCLUDES= ../linkkit
MODPREP=../linkkit/modprep.lisp
CLISP =
SHELL = /bin/sh
clisp-module : test.o
test.o : test.m.c
	gcc -I$(INCLUDES) -o test.o -c test.m.c
test.m.c : test.c
	clisp $(MODPREP) test.c

間違った記述をしていないかを確かめるため、このあたりで一回makeしておく。

$ cd test
$ make
clisp ../linkkit/modprep.lisp test.c
;; MODPREP: "test.c" --> #P"test.m.c"
;; MODPREP: reading "test.c": 130 bytes, 8 lines
;; MODPREP: 0 objects, 1 DEFUN
;; packages: ("TEST")
MODPREP: wrote test.m.c (1,860 bytes)
gcc -I../linkkit -o test.o -c test.m.c
$

モジュールの作成

あとは、定義した関数をclispから使えるようにすればいい。
まず、モジュールを定義するlispのコードを準備する。
あと、clispのためのインターフェイスをいくつか用意する。

;; test.lisp
(defpackage #:test
  (:use #:common-lisp)
  (:export
   #:msg))
;; preload.lisp
(make-package "TEST")

そして、link.shを修正する。

file_list='test.o'
mod_list='test'
make clisp-module CC="${CC}" CPPFLAGS="${CPPFLAGS}" \
                  CFLAGS="${CFLAGS}" INCLUDES="$absolute_linkkitdir"
NEW_FILES="$file_list"
NEW_LIBS="$file_list"
NEW_MODULES="$mod_list"
TO_LOAD='test'
TO_PRELOAD="preload.lisp"

そのあと、clisp-linkでモジュールを作成する。

$ ls
linkkit@  test/
$ sh /opt/local/lib/clisp/clisp-link add-module-set test \
        /opt/local/lib/clisp/base base+test
$ ls
base+test/ linkkit@ test/

あとはちゃんと動くかチェックする。

$ clisp -K $PWD/base+test
[1]> (test:msg)
hello
(TEST:MSG);
NIL
[2]>

備考

  • lisp.aが古い(out of date)と言われたら、sudo ranlib /opt/loacl/lib/clisp/lisp.aを実行する
  • パッケージ名は大文字で統一したほうがいい
    • 大文字と小文字が区別される
    • lispはシンボルを大文字として扱う
  • clisp -K base+testだと/opt/local/lib/clispから検索されてしまう