与C语言接口的Haskell

4
我一直在考虑使用Haskell创建Midori插件,但似乎几乎不可能。问题在于通过FFI导出Haskell函数时,GHC编译器使用大量的-u开关。
有人见过类似情况下使用Haskell的例子吗,而不必将GCC替换为GHC吗?如果有的话,它是如何实现的,他们又经历了什么困境呢?
编辑:这里提供一些示例:
export.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Export where
import Foreign.C
import Foreign.C.Types

foo :: IO Int
foo = return 2

foreign export ccall foo :: IO Int

test.c(ifdefs已剪辑)

#include <stdio.h>
#include "HsFFI.h"
#include "export_stub.h"

extern void __stginit_Export(void);

int main(int argc, char **argv)
{
    int i;
    hs_init(&argc, &argv);
    hs_add_root(__stginit_Export);
    i = foo();
    printf("%d\n", i);
    hs_exit();
    return 0;
}

使用 ghc --make -no-hs-main export.hs test.c 进行编译会生成可执行文件a.out,该指令将使用以下命令进行链接:

collect2 --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o a.out -z relro -u ghczmprim_GHCziTypes_Izh_static_info -u ghczmprim_GHCziTypes_Czh_static_info -u ghczmprim_GHCziTypes_Fzh_static_info -u ghczmprim_GHCziTypes_Dzh_static_info -u base_GHCziPtr_Ptr_static_info -u base_GHCziWord_Wzh_static_info -u base_GHCziInt_I8zh_static_info -u base_GHCziInt_I16zh_static_info -u base_GHCziInt_I32zh_static_info -u base_GHCziInt_I64zh_static_info -u base_GHCziWord_W8zh_static_info -u base_GHCziWord_W16zh_static_info -u base_GHCziWord_W32zh_static_info -u base_GHCziWord_W64zh_static_info -u base_GHCziStable_StablePtr_static_info -u ghczmprim_GHCziTypes_Izh_con_info -u ghczmprim_GHCziTypes_Czh_con_info -u ghczmprim_GHCziTypes_Fzh_con_info -u ghczmprim_GHCziTypes_Dzh_con_info -u base_GHCziPtr_Ptr_con_info -u base_GHCziPtr_FunPtr_con_info -u base_GHCziStable_StablePtr_con_info -u ghczmprim_GHCziBool_False_closure -u ghczmprim_GHCziBool_True_closure -u base_GHCziPack_unpackCString_closure -u base_GHCziIOziException_stackOverflow_closure -u base_GHCziIOziException_heapOverflow_closure -u base_ControlziExceptionziBase_nonTermination_closure -u base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -u base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -u base_ControlziExceptionziBase_nestedAtomically_closure -u base_GHCziWeak_runFinalizzerBatch_closure -u base_GHCziTopHandler_runIO_closure -u base_GHCziTopHandler_runNonIO_closure -u base_GHCziConc_ensureIOManagerIsRunning_closure -u base_GHCziConc_runSparks_closure -u base_GHCziConc_runHandlers_closure /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.4.3/crtbegin.o -L/usr/lib/ghc-6.12.1/base-4.2.0.0 -L/usr/lib/ghc-6.12.1/integer-gmp-0.2.0.0 -L/usr/lib/ghc-6.12.1/ghc-prim-0.2.0.0 -L/usr/lib/ghc-6.12.1 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../.. -L/usr/lib/i486-linux-gnu export.o export_stub.o test.o -lHSbase-4.2.0.0 -lHSinteger-gmp-0.2.0.0 -lgmp -lHSghc-prim-0.2.0.0 -lHSrts -lm -lffi -lrt -ldl -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crtn.o

这段代码是一个链接命令,可以将指定的目标文件与库文件链接生成可执行文件。

从之前的命令中去掉-u开关(只留下-l、-L和一些额外的标志)不会编译,返回了大约50行左右的错误信息。

/usr/lib/ghc-6.12.1/libHSrts.a(RtsAPI.o): In function `rts_mkFunPtr':
(.text+0x5a9): undefined reference to `base_GHCziPtr_FunPtr_con_info'
/usr/lib/ghc-6.12.1/libHSrts.a(RtsAPI.o): In function `rts_mkString':
(.text+0x60f): undefined reference to `base_GHCziPack_unpackCString_closure'

4
这可能是手册的相关部分:http://www.haskell.org/ghc/docs/latest/html/users_guide/ffi-ghc.html。特别是参见8.2.1.2。你可以编写一个用Haskell编写的库,并可从C代码中调用它。然后,你只需要在C中编写一些粘合代码就可以将其转换为插件或其他什么形式。但我自己没有做过,请等待更有经验的“foreign export”用户回答。 - sastanin
1
@Jetxee,你知道把答案写成评论会禁止原帖作者接受它吗? :) - Kos
是的,你说得对。但是在尝试连接这些对象时,会导致未定义的错误,可以通过使用“-u”开关来修复,但是有很多,我认为它们可能会因程序而异。 - Masse
@Kos 好的,我已经将它发布为答案了 :) @Masse 你能否提供一个最小化的编译示例,说明你正在尝试如何构建它以及出现了什么错误。然后有人可以尝试解决你的特定问题。 - sastanin
2个回答

2
我能够解决这个问题。
我正在使用以下文件:
main.c
#include <stdio.h>
#include "lib_stub.h"
int main(int argc, char **argv)
{
    puts("foo");
    printf("%d\n", hsfun(5));
    return 0;
}

lib.hs

{-# LANGUAGE ForeignFunctionInterface #-}
module Test where
import Foreign.C.Types

hsfun :: CInt -> IO CInt
hsfun x = do
  putStrLn "Hello from haskell"
  return (42 * x)

foreign export ccall hsfun :: CInt -> IO CInt

module_init.c

#include <HsFFI.h>
extern void __stginit_Test(void);

static void library_init(void) __attribute__((constructor));
static void library_init(void)
{
    static char *argv[] = { "libtest.so", 0 }, **argv_ = argv;
    static int argc = 1;

    hs_init(&argc, &argv_);
    hs_add_root(__stginit_Test);
}

static void library_exit(void) __attribute__((destructor));
static void library_exit(void)
{
    hs_exit();
}

我使用以下命令编译库:ghc --make -shared -dynamic -fPIC -o libtest.so lib.hs module_init.c -lHSrts-ghc6.12.1 -optl-Wl,-rpath,/usr/lib/ghc-6.12.1/ -L/usr/lib/ghc-6.12.1,并使用以下命令编译可执行文件:gcc -c main.c -I/usr/lib/ghc-6.12.1/include -L. -ltest -Wl,-rpath=$PWD。重要的部分是使库成为共享库,并链接 rts 库,这不是默认的设置。rpath 使它可以在没有 LD_LIBRARY_PATH 的情况下运行。详细信息请查看:http://weblog.haskell.cz/pivnik/building-a-shared-library-in-haskell/http://www.well-typed.com/blog/30

0

这可能是手册的相关部分:

8.2. 使用 GHC 的 FFI

特别是看看 8.2.1.2。您可以编写一个用 Haskell 编写并可从 C 代码调用的库。然后,您只需编写一些 C 中的粘合代码,将其转换为插件或其他内容即可。但我自己没有做过,请等待更有经验的 foreign export 用户回答。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接