読者です 読者をやめる 読者になる 読者になる

cffi を学ぶ(3) Cのライブラリとコールバック

cffi を学ぶ(1) 変数と配列 - TIPS
cffi を学ぶ(2) 配列の続きと関数 - TIPS
cffi を学ぶ(3) Cのライブラリとコールバック - TIPS
cffi を学ぶ(4) C のライブラリを使う - TIPS
cffi を学ぶ(5) 構造体 - TIPS
cffi を学ぶ(6) 構造体の続きと translate-*-foreign - TIPS

参考
レファレンス CFFI User Manual
日本語の入門サイト http://cl.cddddr.org/index.cgi?CFFI
今回はこちらも LISPMEMO

前提として quicklisp で cffi を入れたものとして、以下を repl で評価しておく。

(load "~/quicklisp/setup.lisp")
(ql:quickload "cffi")

(defpackage :scratch
  (:use :cl :cffi))

(in-package :scratch)

ライブラリ:

自作ライブラリを使う簡単な例として hello world をやってみる。まず C でライブラリを書く。

#include <stdio.h>

char* hello()
{
    return "Hello, World!";
}

これを hello.c として

gcc -shared hello.c -o libhello.so

でライブラリを作り、読み込む。

(define-foreign-library libhello
  (:darwin "libhello.so")
  (:unix "libhello.so")) ;; windows では libhello.dll になると思うがよく知らないので省略

(use-foreign-library libhello)

あとは前回のように定義して使うだけ。

(defcfun "hello" :string)

(hello) ;; => Hello, World!

コールバック:

Lisp で関数を書いて C の関数に関数ポインタを渡すことができる。 OpenGL をやるにはこれができないといけない。
簡単な例をやってみる。

C を次のように書いてファイル名を callback.c とする。

#include <stdio.h>

float result(float (*f)(float, float), float a, float b)
{
    return f(a, b);
}

result は関数と引数を受け取って結果を返すだけの関数。

上と同様にコンパイル

gcc -shared callback.c -o libcallback.so

読み込みや関数定義も同様。

(define-foreign-library libcallback
  (:darwin "libcallback.so")
  (:unix "libcallback.so"))

(use-foreign-library libcallback)

(defcfun "result" :float
  (f :pointer) (a :float) (b :float))

C の関数 result に渡す関数を Lisp で書くことができる。 defcallback を使って次のように書く。

(defcallback add :float 
    ((a :float) (b :float))
  (+ a b))

defcfun に似ているが (引数 型) のペアたちを () で括る必要がある。

C ポインタを返すには (callback add) とする。

(callback add) ;; => #.(SB-SYS:INT-SAP #X20100C40)

使ってみる

(result (callback add) 1.0 2.0) ;; => 3.0

もう一つ試しに

(defcallback sub :float 
    ((a :float) (b :float))
  (- a b))

(result (callback sub) 2.0 1.0) ;; => 1.0