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);
}

こんな感じでちゃんと動く。
HaskellFFIって結構しっかりしてるなぁ。


ということで、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のコードとかわらんなぁ。
一応この時点で音なしならゲーム作れるかな。

http://fxp.infoseek.ne.jp/haskell/sdltest2.zip