HaskellでのSDLの使い方
SDLのHaskellバインディングはいくつかありますが、この記事ではHackageのSDLを使って説明しています。Windows環境だとCabalではうまく入らなかったりするので、直接ソースからビルドする必要があるかもしれません。
SDLを使うにはFFIが必要
$ ghc -package SDL --make Main.hs
のようにコンパイルしても、SDL_mainが見つからないなどのエラーが出てコンパイルできません。SDLを使ったプログラムでは本来、ユーザが作成したmain関数はSDL.hのマクロによってSDL_mainにリネームされ、SDLライブラリの内部にある本当のmain関数がSDL_mainを呼び出すことになってるのですが、ghcはその面倒まで見てくれないのでSDL_mainが作られず、エラーになってしまいます。
これを回避するためには、C言語で書いたmainの中からFFIでHaskellを呼び出した上で、元のmainをSDL.hでSDL_mainに書き換えさせる必要があります。
Haskellの関数をエクスポートする
まずは画面を表示するだけのHaskellコードを書いて、Cのmainから呼び出すためのhs_mainをエクスポートします。
{-# LANGUAGE ForeignFunctionInterface #-} module HsMain where import qualified Graphics.UI.SDL as SDL foreign export ccall "hs_main" hs_main :: IO () hs_main :: IO () hs_main = do SDL.init [SDL.InitEverything] SDL.setCaption "SDL test" "" SDL.setVideoMode 300 300 32 [] mainLoop SDL.quit mainLoop :: IO () mainLoop = do e <- SDL.waitEvent case e of SDL.Quit -> return () _ -> mainLoop
ここで、このコードをそのまま
$ ghc --make HsMain.hs
のようにコンパイルしようとすると、HsMain_stub.cとHsMain_stub.hというファイルが生成されます。これがHaskellコードをCから呼び出すためのインターフェースになります。
Cから呼び出す
次に、このインターフェースを通してHaskellコードを呼び出すCコードを用意します。
#include <SDL.h> #include "HsFFI.h" #ifdef __GLASGOW_HASKELL__ #include "HsMain_stub.h" extern void __stginit_HsMain(void); #endif int main(int argc, char* argv[]) { hs_init(&argc, &argv); #ifdef __GLASGOW_HASKELL__ hs_add_root(__stginit_HsMain); #endif hs_main(); hs_exit(); return 0; }
最後に、これらを合わせて
$ ghc -package SDL --make -no-hs-main HsMain.hs c_main.c
のようにコンパイルすると、実行ファイルを生成することが出来ます。