ログイン
i-mobile
記事の一覧
4.<[opengl][scheme]マル... | [scheme][gauche]拡張...>6.

2010-10-10

[][]gauche-packageを利用したgaucheの拡張モジュールの作り方 15:57 Add Star

この記事で作ったモジュールのリポジトリはgithubにあります。http://github.com/podhmo/gauche-perlinnoise

はじめに

gaucheにはschemeで書かれたモジュールの他にCで書かれた拡張モジュールがあります。

拡張モジュールを作る足場を作るためのツールが実はgaucheに用意されています。

gauche-packageというツールです。

今回はこれを利用して簡単な拡張モジュールを作っていきたいと思います。

gauche-perlinoiseについて

今回作成するのはgauche-perlinnoiseというパッケージです。

perlin noiseは、CGの中で雲とか煙を表現するテクスチャを作成する時に使われるものです。

今回作成するパッケージはext.perlinnoiseというモジュール名でuseできて、このモジュールが提供するのは`perlinnoise-2d'という手続きただひとつです。

使い方は以下のような感じです。

(use ext.perlinnoise)

(module-exports (find-module 'ext.perlinnoise)) ;; =>(perlinnoise-2d)

(perlinnoise-2d 1 2 1 8) ;; => -43.91069793701172

実行結果をみてわかる通り,`perlinnoise-2d'は数値を4つ取って実数値を返す手続きです。

準備(足場作り)

実際にモジュールを作成するための足場を作成していきましょう。

ここでは拡張モジュールを作成するためのディレクトリを作成し、ダミーの関数("gauche_perlinnoise is working"という文字列を返すだけの手続き)を呼ぶところまでを行います。

gauche-packageにgenerateを渡すことでモジュールを生成する足場を生成することができます。

($の後の文字列は実行したコマンドを表し、インデントされた文字列はシェルに表示された文字列を表します。)

### gauche-package generate <package-name> <module-name> というように呼ぶ。
$ gauche-package generate gauche-perlinnoise ext.perlinnoise

生成されたディレクトリに移動してDISTを実行しましょう。

$ cd gauche-perlinnoise
$ ./DIST gen

DISTによってconfigureなどが生成されます。

ここでバージョン情報などの設定をするためにconfigureの中のPACKAGE_から続く変数を書き換えます。

書き換えた後の差分は以下です(git diffから取ってきました。)

diff --git a/configure b/configure
index 63f9a00..66f4066 100755
--- a/configure
+++ b/configure
@@ -551,10 +551,10 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='gauche-perlinnoise'
 PACKAGE_TARNAME='gauche-perlinnoise'
-PACKAGE_VERSION='1.0'
-PACKAGE_STRING='gauche-perlinnoise 1.0'
-PACKAGE_BUGREPORT='shiro@acm.org'
-PACKAGE_URL=''
+PACKAGE_VERSION='0.0.1'
+PACKAGE_STRING='gauche-perlinnoise 0.0.1'
+PACKAGE_BUGREPORT='foobar@ne.jp'
+PACKAGE_URL='http://foo.bar.co.jp'
 
 ac_subst_vars='LTLIBOBJS
 LIBOBJS

本当は、PACKAGE_BUGREPORTやPACKAGE_URLをまともに書いた方が良いですね。

./configureを実行しMakefileを作成したあと、作成されたMakefileを使ってmakeします。

$ ./configure
#  checking for gosh... /usr/bin/gosh
#  checking for gauche-config... /usr/bin/gauche-config
#  checking for gauche-package... /usr/bin/gauche-package
#  checking for gauche-install... /usr/bin/gauche-install
#  checking for gauche-cesconv... /usr/bin/gauche-cesconv
#  configure: creating gauche-perlinnoise.gpd
#  configure: creating ./config.status
#  config.status: creating Makefile
$ make
#  /usr/bin/gauche-package compile \
#        --local= --verbose gauche_perlinnoise ./gauche_perlinnoise.c ./gauche_perlinnoiselib.stub
#  gcc -std=gnu99 -c  -I/usr/lib/gauche/0.9/include  -fPIC -o 'gauche_perlinnoise.o' './gauche_perlinnoise.c'
#  gcc -std=gnu99 -c  -I/usr/lib/gauche/0.9/include  -fPIC -o 'gauche_perlinnoiselib.o' './gauche_perlinnoiselib.c'
#  gcc -std=gnu99  -L/usr/lib/gauche/0.9/x86_64-pc-linux-gnu   -shared -o gauche_perlinnoise.so 'gauche_perlinnoise.o' 'gauche_perlinnoiselib.o' -lgauche -ldl -lcrypt -lutil -lm  -lpthread 

うまくモジュールが生成されているか確かめてみましょう。

make installはしていないですが、生成されたモジュールを呼ぶだけならば今の状態でも可能です。

$ gosh -I. test.scm
#  Testing ext.perlinnoise ...                                      
#  testing bindings in #<module ext.perlinnoise> ... ok
#  test test-gauche_perlinnoise, expects "gauche_perlinnoise is working" ==> ok
#  passed.

無事、モジュールは生成されたようです。

モジュールの定義

ここからは実際にext.perlinnoiseを作成していきます。

今回はすでにCで実装されたperlin_noise.cがあるということにします。

実際に作業に移る前に、生成されたファイルの説明をします。これ以降実際に書き換える必要があるファイルは以下の4つです。

Makefile
gauche_perlinnoiselib.stubstubファイル。小さなモジュールの場合はここに全て詰め込んでしまうことが多い。
ext/perlinnoise.scm useされた時にscheme側が見るファイル。*1インターフェイス。
test.scm testが記述されたファイル。

大体は、stubファイルとext/perlinnoise.scmを書き換えながら、順次test.scmでtestをしていくという形になります。

それではstubファイルを編集していきましょう。

stubファイルの中ではcise(c is s-expression)という形式で記述するのですが、中にCのコードを直接含めることも可能です。

単純に言えば、ダブルクォートで囲まれた文字列=Cのコード、それ以外=ciseのコードです。

今回は、perlin_noise.cのコードをmain()を除いて全部文字列として埋め込んでしまいます。

stubファイルにダブルクォートでくくってCのコードを貼れば大丈夫です。コピペしてください。

ここで一度テストしてみましょう。

$ make && gosh -I. test.scm
#   make: Nothing to be done for `all'.
#   Testing ext.perlinnoise ...                                      
#   testing bindings in #<module ext.perlinnoise> ... ok
#   test test-gauche_perlinnoise, expects "gauche_perlinnoise is working" ==> ok
#   passed.

testは失敗していませんね? さて次に進みましょう。

テスト結果を見てみれば分かる通り、ただstubファイルにCのコードを加えただけでは何も変わりがありません。

Cの世界に立ち入ることができれば、加えた関数群を使うことができますが、実行時に処理系が覗くのはschemeの世界だけです。

schemeの世界から見えるようにCの関数へのインターフェイスをつくりましょう。

ここでstubファイルにciseを書いていくことになります。define-cprocを使いましょう。stubファイルに以下を追加してみてください。

(define-cproc perlinnoise-2d(x::<float> y::<float> persistence::<float> octaves::<float>) ::<float>
  (return (Scm_MakeFlonum (perlinnoise_2d x y persistence octaves))))

これでext.perlinnoiseのモジュールの中ではperlinnoise-2dがつかえるようになりました。

もちろん、exportしておかなければuseした先では使うことができないのでexportします。

今度はext/perlinnoise.scmを書き換えます。方法は、schemeでモジュールを定義する方法と同様です。

 (define-module ext.perlinnoise
   (export test-gauche_perlinnoise ;; dummy
-          )
+          perlinnoise-2d)
   )
 (select-module ext.perlinnoise)

単にperlinnoise-2dを追加しただけです。

ここで、再びmakeして実行してみることにしましょう。

goshに-lオプションでtest.scmを渡すようにしてあげると、作成しているモジュールをuseした環境でreplが立ち上がります。

make && gosh -I. -l test.scm

立ち上がったreplで

(perlinnoise-2d 2.0 0 3.0 4) ;; => -2.475400447845459

無事、perlinnoise-2dがつかえるようになりました。

作成したモジュールのインストール

インストール方法は簡単です。make installをしてください(逆にアンインストールはmake uninstallです)

$ sudo make install
#  /usr/bin/gauche-install -C -m 444 -T /usr/lib/gauche/site/include 
#  /usr/bin/gauche-install -C -m 444 -T /usr/share/gauche/site/lib ./ext/perlinnoise.scm 
#  /usr/bin/gauche-install -C -m 555 -T /usr/lib/gauche/site/0.9/x86_64-pc-linux-gnu gauche_perlinnoise.so
#  /usr/bin/gauche-install -C -m 444 -T /usr/share/gauche/site/lib/.packages gauche-perlinnoise.gpd

gauche-packageでインストールされたことを確認してみましょう。listを渡します。

$ gauche-package list
#   Gauche-gl           0.4.4   
#   gauche-imlib2       0.0.1
#   gauche-perlinnoise  0.0.1 ;; ありますね。   
#   gauche-quesoglc     0.0.2   

今度は、普通に立ち上げたgoshでuseすることができます。

(use ext.perlinnoise)

(perlinnoise-2d 1 2 1 8) ;; => -43.91069793701172

*1:普通にschemeでmoduleを定義する際のファイルと同様のものと考えれば良い…と思う

shiro ●2010/10/10 16:18
わかりやすい記事ありがとうございます。
ciseの仕様はまだ固まってないので第三者に書けと言う方が無理です…

perlinnoise-2dですが、戻り値を::<float>と宣言してあれば、C関数の戻り値からSchemeオブジェクトへの変換も自動生成できます。ただしreturnではなくresultを使ってください。

(define-cproc perlinnoise-2d(x::<float> y::<float> persistence::<float> octaves::<float>) ::<float>
(result (perlinnoise_2d x y persistence octaves)))

returnはCのreturnに直で対応するため、自前で変換してやらないとおかしなことになるのに対し、resultは変換の手間も見てくれますし、多値を返すこともできます (ほんとはreturnにそういう機能を持たせた方がわかりやすかったんですが、互換性のためこうなっています)。resultは「戻り値用領域」に結果を格納するだけなので、resultの後に後始末処理を書くこともできます。現在はresult推奨で、returnはより低レベルな何かをしたい場合用、と考えて頂ければ良いかと。
podhmo ●2010/10/10 16:35
なるほど、returnを使わずに基本的にresultを使えば良いのですね。resultの後に後処理を書けるというのはおもしろいです。begin0みたいな感覚で使うと良い感じですね。

コメント

4.<[opengl][scheme]マル... | [scheme][gauche]拡張...>6.
●ウェブ検索●