lispとrubyとpythonと その4 Cライブラリの呼び出し(lisp)
今回はCライブラリの呼び出し。
SBCL限定なので注意。
sb-alienにCを呼び出すために必要なもろもろが入ってる。
一応動いてるんだけど、これであってんのかなぁ。
C側でmallocした時とか、GCのコンパクションでまずいことになったりしないんだろうか・・・。
参考はここ。
http://www.sbcl.org/manual/Foreign-Function-Interface.html#Foreign-Function-Interface
まず、呼び出すcのライブラリはこんなの。
ffi-test.h
int f(int i); int f0(char* buf,int len); int f1(char** buf); char* f2(); typedef struct person{ char* name; int age; } person_t; int f3(person_t* person,int len); int f4(person_t** person);
実装はこちら。
ffi-test.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "ffi-test.h" int f(int i) { return i + 1; } //呼び出し元でAlloc int f0(char* buf,int len) { printf("hello\n"); strncpy(buf,"日本語\n",len); return 0; } //関数内でAlloc int f1(char** buf) { int sz = sizeof(char) * 64; *buf =(char*) malloc(sz); strncpy(*buf,"日本語2\n",sizeof(char) * 64); return 0; } //戻り値で返してみる char* f2() { int sz = sizeof(char) * 64; char* buf =(char*) malloc(sz); strncpy(buf,"日本語3\n",sizeof(char) * 64); return buf; } //構造体 呼び出し元でAlloc int f3(person_t* person,int len) { strncpy(person->name,"名前あああ",len); person->age = 30; return 0; } //構造体 関数内でAlloc int f4(person_t** person) { int sz1 = sizeof(person_t); *person = (person_t*)malloc(sz1); int sz2 = sizeof(char) * 64; (*person)->name = malloc(sz2); strncpy((*person)->name,"名前いいい",sz2); (*person)->age = 40; return 0; }
んで、これを
gcc -shared ffitest.c -o libffi-test.so
でコンパイル。
呼び出すlisp側はこんな感じ。
define-alien-routineでcライブラリとのラッパを宣言。
make-alienがmallocでfree-alienがfree。
(load-shared-object "libffi-test.so") (declaim (inline f)) (sb-alien:define-alien-routine f sb-alien:int (i sb-alien:int)) (declaim (inline f0)) (sb-alien:define-alien-routine f0 sb-alien:int (buf (sb-alien:* sb-alien:char)) (len sb-alien:int)) (declaim (inline f1)) (sb-alien:define-alien-routine f1 sb-alien:int (buf (sb-alien:* (sb-alien:c-string :external-format :utf8)))) (declaim (inline f2)) (sb-alien:define-alien-routine f2 sb-alien:c-string) (sb-alien:define-alien-type person_t (sb-alien:struct person (name (sb-alien:* sb-alien:char)) (age int))) (declaim (inline f3)) (sb-alien:define-alien-routine f3 sb-alien:int (person (sb-alien:* person_t)) (len sb-alien:int)) (declaim (inline f4)) (sb-alien:define-alien-routine f4 sb-alien:int (buf (sb-alien:* (sb-alien:* person_t)))) (defun lisp-f0() (sb-alien:with-alien ((len sb-alien:int (* (sb-alien:alien-size sb-alien:char) 64)) (buf (sb-alien:* sb-alien:char) (sb-alien:make-alien sb-alien:char len))) (unwind-protect (sb-alien:with-alien ((status sb-alien:int (f0 buf len)) (rtn (sb-alien:c-string :external-format :utf8) buf)) rtn) (sb-alien:free-alien buf)))) (defun lisp-f1() (sb-alien:with-alien ((buf (sb-alien:c-string :external-format :utf8))) (sb-alien:with-alien ((rtn sb-alien:int (f1 (sb-alien:addr buf)))) buf))) (defun lisp-f2() (sb-alien:with-alien ((buf (sb-alien:c-string :external-format :utf8) (f2))) buf)) (defun lisp-f3() (sb-alien:with-alien ((len sb-alien:int (* (sb-alien:alien-size sb-alien:char) 64)) (p (sb-alien:* person_t) (sb-alien:make-alien person_t 1))) (unwind-protect (progn (setf (slot p 'name) (sb-alien:make-alien sb-alien:char len)) (unwind-protect (sb-alien:with-alien ((status sb-alien:int (f3 p len)) (rtn (sb-alien:c-string :external-format :utf8) (slot p 'name))) rtn) (sb-alien:free-alien (slot p 'name)))) (sb-alien:free-alien p)))) (defun lisp-f4() (sb-alien:with-alien ((p (sb-alien:* person_t)) (status sb-alien:int (f4 (sb-alien:addr p))) (rtn (sb-alien:c-string :external-format :utf8) (slot p 'name))) rtn))
で呼び出し結果がこれ。
CL-USER> (lisp-f0) "日本語 " CL-USER> (lisp-f1) "日本語2 " CL-USER> (lisp-f2) "日本語3 " CL-USER> (lisp-f3) "名前あああ" CL-USER> (lisp-f4) "名前いいい"