呼びたくなったというよりは、裏でc++でものすっごく激しい計算させるので呼ばざるを得ない。
google先生に聞いても、俺の検索力ではサンプルっぽいものは見つけられかった。
{くぅ。scalaも始めたばっかりで分けわからんし、どうしてくれようか・・・}
とコードをごねごねしていたらなんか動いたので、忘れる前に投稿してみた。
できてしまえば、かなりシンプルになった。もちろん一見シンプルに見えるのはscalaパワーのおかげ。
やりたかったことは、あるクラス(HelloData)の各インスタンスのもつクロージャ(?)をc側にコールバックとして登録して、それぞれのインスタンスからcのライブラリを呼んだ時に、ちゃんとコールバックが呼び出したインスタンスのクロージャに返ってくるようにするということ。
ソースのファイルは5つ
- Application.scala Play側のmain的な入り口
- HelloData.scala テキトーなクラス。こいつのインスタンスからcを呼び出したり、ここのコールバックに戻したり、いろんなデータを持ってみたりしてみたい。
- HelloJna.scala JNAとの接合部分
- index.scala.html web出力のview部分
- JnaInterface.cpp c側のソース
Playからindexが呼ばれる→HelloDataのインスタンス作成→HelloDataのコンストラクタ内でコールバック登録→HelloDataのメソッドからcの関数呼び出し→c側からコールバック呼び出し→コールバック内でprintln→c側関数が文字列を戻して終了→webにメッセージ出しておしまい
じゃ、Application.scalaから
HelloDataのインスタンスを3つ作って、ちゃんと区別されて返ってくるか実験する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package controllers import play.api. _ import play.api.mvc. _ import models. _ object Application extends Controller { def index = Action { val data = List( new HelloData(), new HelloData(), new HelloData()) val msg = data.map( _ .callJnaHello) Ok(views.html.index(msg)) } } |
HelloData.scala
scala側とc側でデータの連携をとるために、ユニークなidxを持たせてある。
2013/6/4修正 callbackをvalに入れるようにしました。詳しくはこちらの投稿
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package models class HelloData() { private val lib = HelloJna.lib private val idx = HelloData.cnt; private val callback = new JnaCallback { def onMessage(msg : String) = println( "onMessage " + idx + ": " + msg) } HelloData.cnt + = 1 lib.setFunction(idx, callback) def callJnaHello : String = lib.getHello(idx) } object HelloData { var cnt = 0 ; } |
HelloJna.scala
ライブラリファイルを変更してコンパイルし直しても、一度JVMを再起動(playを再起動すればいい)しないとライブラリをloadし直さないので注意(ここはまった)。JVMには、一度loadしたNativeのLibraryをunloadする機能はないらしい。←あったら誰か教えて
なお、途中のlibHelloJNA.soは、もっと下のJnaInterface.cppからつくったSharedObjectです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package models import com.sun.jna.Library import com.sun.jna.Native import com.sun.jna.Callback object HelloJna { val lib = Native.loadLibrary( "/xxx/yyy/xxx/Debug/libHelloJNA.so" , classOf[HelloJnaTrait]).asInstanceOf[HelloJnaTrait] } trait JnaCallback extends Callback { def onMessage(msg : String) : Unit } trait HelloJnaTrait extends Library { def getHello(idx : Int) : String def setFunction(idx : Int, callback : JnaCallback) } |
index.scala.html
cの関数から戻ってきた文字列を出すだけ
1 2 3 4 5 6 7 | @(messages: List[String]) <!DOCTYPE html> < html > < body > @for(msg <- data-blogger-escaped-messages="" data-blogger-escaped-p=""> @msg <!-----> |
JnaInterface.cpp
実験なのでidxが255超えたらどうすんのって突っ込みはなし
他の部分はc++で書いていてもここのところはcの関数的に宣言する必要あり(たぶん)。
あと、一番下にiostream閉じてる変なタグが見えたら、SystaxHilighterのバグなので気にしないこと。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> using namespace std; static int g_cnt = 0; static char g_msg[256]; static void (*g_callback[256])( const char *msg); extern "C" const char *getHello( int idx) { char callbackMsg[256]; sprintf (callbackMsg, "msg to callback %d" , idx); g_callback[idx](callbackMsg); sprintf (g_msg, "Hello JNA idx = %d, cnt = %d." , idx, ++g_cnt); return g_msg; } extern "C" void setFunction( int idx, void (*func)( const char *msg)) { g_callback[idx] = func; } </iostream> |
下は、ブラウザに表示された結果
1 2 3 4 5 | Hello JNA idx = 0, cnt = 1. Hello JNA idx = 1, cnt = 2. Hello JNA idx = 2, cnt = 3. |
次は、printlnの出力
コロン前の数字の値は、各HelloDataインスタンス内にあるので、各インスタンス内のクロージャにちゃんとコールバックが戻っているのが確認できる。
1 2 3 | onMessage 0: msg to callback 0 onMessage 1: msg to callback 1 onMessage 2: msg to callback 2 |
0 件のコメント :
コメントを投稿