昆布大好き!

主にプログラミングの技メモ

Haskellコードを動的ライブラリにしてCから使用する その1

それほど難しくはないのですが、その分細かい手順を忘れやすいので。

まずは、動的ライブラリ(DLL)の作成まで。 必要な手順は、

  1. Haskellのコードで関数を公開して、外部から参照できるようにする
  2. DLLのインタフェイスとなるCのコードから公開したコードを呼び出す
  3. コンパイルしてDLL(と静的ライブラリ)を作成する

となります。 (他にもやり方はあるかもしれません)

今回の環境。

Haskell側の記述です。公開したい関数を次のように指定します。Cの方の型を扱うため特別なimportも必要です。

-- ExprEval.hs
import Foreign.C.Types
import Foreign.C.String

foreign export ccall cexprI :: CWString -> IO CInt
foreign export ccall cexprF :: CWString -> IO CFloat
foreign export ccall cexprCheck :: CWString -> IO CInt

次はこれらを呼び出すC側の記述です。 DLLのインタフェイスとして実際に呼び出す関数はこちらです。 ラッパーとして作成して扱いやすくします。

// cexpr.c
#include <HsFFI.h>

#ifdef __GLASGOW_HASKELL__
#include "expr_stub.h"
extern void __stginit_ExprEval(void);
#endif


int exprCheck(const wchar_t *str)
{
    return cexprCheck((HsPtr)str);
}

float exprF(const wchar_t *str)
{
    return cexprF((HsPtr)str);
}

int exprI(const wchar_t *str)
{
    return cexprI((HsPtr)str);
}

HsBool hslibInit(int argc, char *argv[])
{
    hs_init(&argc, &argv);
    return HS_BOOL_TRUE;
}

void hslibEnd()
{
    hs_exit();
}

あとはこれらをまとめてghcコンパイルです。DLLと静的ライブラリが作成されます。 静的ライブラリの拡張子が.aとなるかもしれません。

ghc -shared cexpr.c ExprEval.hs -o ExprEval.dll
ren ExprEval.dll.a ExprEval.dll.a

それから、作成したDLLはHaskellのランタイムを含んでいます。1Mバイトほどサイズが大きくなります。 ghcのオプションに-dynamicをつけると含まなくなり、小さく作れます。

DLLを呼び出す側にもう少しお作法がありますので、次回にまた。