FFI (その4)
こっちも作り始めて3日目、そろそろ形になりつつある。
今日はEvent周りを作ったのであと大きな課題はAudioぐらいか。
Audioもパフォーマンスを考えなかったらミックスルーチンを
適当にHaskellで書くだけなのだが…
それにしてもEvent周りは大変だった。
何が大変って、構造体のマーシャリングが。
ひたすらコードを書いて書いて書きまくり。
こういうの久しぶりだなぁ。
それとは別に技術的課題も一つあった。
CからHaskellのコールバックである。
HaskellからCに関数ポインタを渡してCがその関数を
呼び出すということなのだが、今までの技術では
関数ポインタが扱えなかったのだ。
練習のためにCのqsortをHaskellから
呼び出してみることにした。
まず、Haskellの関数をCから呼び出せる関数ポインタに
変換する必要がある。
type Compare = Ptr Int -> Ptr Int -> IO Int foreign import ccall "wrapper" mkFun :: Compare -> IO (FunPtr Compare)
foreign import ccall "wrapper" を用いる。
こうすると、特定の型の関数をその型の関数ポインタに変換する
処理が作れるようである。
最初、必死になって a -> IO (FunPtr a) な型の関数がないか
探していたのだが、処理の内容を考えるとそのような
ものは作れなさそうである。
上のような記述をするとクロージャ→関数ポインタ変換をおこなう
スタブコードが生成される。
比較関数だが、qsortのコールバックの型が
int (*compare)(const void *p,const void *q)
なので、Haskell側は Ptr Int -> Ptr Int -> IO ()
にしておいた。
で、あとは適当に周りのコードを書く。
cmp p q = do a <- peek p b <- peek q return $ a-b extSort :: Compare -> [Int] -> IO [Int] extSort f dat = do fp <- mkFun f ap <- newArray dat foo ap (length dat) fp peekArray (length dat) ap main = do dest <- extSort cmp [3,7,2,9,1,8] print dest
C側。
typedef int (*sp)(const void *p1,const void *p2); void foo(HsPtr dat,HsInt elem,HsFunPtr fp) { qsort(dat,elem,4,(sp)fp); }
こんな感じでちゃんと動く。
HaskellのFFIって結構しっかりしてるなぁ。
ということで、SDLのEvent周りを実装した。
これで漸くちゃんとWindowを閉じられるようになった。
で、やはりチュートリアルを移植。
{ SDL_Event event; SDL_WaitEvent(&event); switch (event.type) { case SDL_KEYDOWN: printf("The %s key was pressed!\n", SDL_GetKeyName(event.key.keysym.sym)); break; case SDL_QUIT: exit(0); } }
これを移植することにする。
main :: IO() main = do ret <- sdlInit [VIDEO] when (not ret) $ fail "init failed." sur <- sdlSetVideoMode 640 480 32 [SWSURFACE,ANYFORMAT] loop sdlQuit where loop = do Just ev <- sdlWaitEvent case ev of KeyboardEvent { kbPress=True, kbKeysym = Keysym { ksSym = sym }} -> do kname <- sdlGetKeyName sym putStrLn $ "The "++kname++" key was pressed!" loop QuitEvent -> return () _ -> loop
毎度のことながらほとんどCのコードとかわらんなぁ。
一応この時点で音なしならゲーム作れるかな。