【问题标题】:Building a dynamic library with haskell and using it from C++使用 haskell 构建动态库并在 C++ 中使用它
【发布时间】:2012-01-31 04:20:58
【问题描述】:

我想构建一个包含 haskell 函数的动态库。我在 linux 上工作,想从 C++ 代码中调用这个动态库。

我使用了http://wiki.python.org/moin/PythonVsHaskell 的示例并拥有以下文件:

Test.hs:

{-# LANGUAGE ForeignFunctionInterface #-}
module Test where

import Foreign.C.Types

hsfun :: CInt -> IO CInt
hsfun x = do
    putStrLn "Hello World"
    return (42 + x)

foreign export ccall
    hsfun :: CInt -> IO CInt

module_init.c:

#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a

#include <HsFFI.h>

extern void CAT (__stginit_, MODULE) (void);

static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
  /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
  static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
  static int argc = 1;

  hs_init (&argc, &argv_);
  hs_add_root (CAT (__stginit_, MODULE));
}

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

现在我将这些文件编译为动态库:

$ ghc -dynamic -shared -fPIC -optc '-DMODULE=Test' Test.hs module_init.c -o libTest.so
[1 of 1] Compiling Test             ( Test.hs, Test.o )
Linking libTest.so ...

这会创建文件 Test_stub.h:

#include "HsFFI.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HsInt32 hsfun(HsInt32 a1);
#ifdef __cplusplus
}
#endif

和Test_stub.c:

#define IN_STG_CODE 0
#include "Rts.h"
#include "Stg.h"
#ifdef __cplusplus
extern "C" {
#endif

extern StgClosure Test_zdfhsfunzua165_closure;
HsInt32 hsfun(HsInt32 a1)
{
Capability *cap;
HaskellObj ret;
HsInt32 cret;
cap = rts_lock();
cap=rts_evalIO(cap,rts_apply(cap,(HaskellObj)runIO_closure,rts_apply(cap,&Test_zdfhsfunzua165_closure,rts_mkInt32(cap,a1))) ,&ret);
rts_checkSchedStatus("hsfun",cap);
cret=rts_getInt32(ret);
rts_unlock(cap);
return cret;
}
static void stginit_export_Test_zdfhsfunzua165() __attribute__((constructor));
static void stginit_export_Test_zdfhsfunzua165()
{getStablePtr((StgPtr) &Test_zdfhsfunzua165_closure);}
#ifdef __cplusplus
}
#endif

然后我创建一个cpp文件main.cpp:

#include "Test_stub.h"

#include <iostream>

using namespace std;

int main()
{
    cout << hsfun(5);
}

并且想要编译和链接它。但是当我调用 g++ 时,它会说:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp
/tmp/ccFP2AuB.o: In function `main':
main.cpp:(.text+0xa): undefined reference to `hsfun'
collect2: ld gab 1 als Ende-Status zurück

所以我将 Test_stub.o 文件添加到命令行(虽然我认为 hsfun 函数应该已经在 libTest.so 中定义,它是通过 -lTest 参数添加的。我不认为,我应该链接 Test_stub .o 文件到可执行文件中,因为我想使用动态链接),但这也不起作用:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp Test_stub.o
Test_stub.o: In function `hsfun':
Test_stub.c:(.text+0x9): undefined reference to `rts_lock'
Test_stub.c:(.text+0x16): undefined reference to `rts_mkInt32'
Test_stub.c:(.text+0x1d): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text+0x28): undefined reference to `rts_apply'
Test_stub.c:(.text+0x2f): undefined reference to `base_GHCziTopHandler_runIO_closure'
Test_stub.c:(.text+0x3a): undefined reference to `rts_apply'
Test_stub.c:(.text+0x4a): undefined reference to `rts_evalIO'
Test_stub.c:(.text+0x5c): undefined reference to `rts_checkSchedStatus'
Test_stub.c:(.text+0x66): undefined reference to `rts_getInt32'
Test_stub.c:(.text+0x70): undefined reference to `rts_unlock'
Test_stub.o: In function `stginit_export_Test_zdfhsfunzua165':
Test_stub.c:(.text.startup+0x3): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text.startup+0x8): undefined reference to `getStablePtr'
collect2: ld gab 1 als Ende-Status zurück

我必须链接 Test_stub.o 吗?如果是,为什么?我应该将哪些参数传递给链接器?

【问题讨论】:

  • 不知道详情;但你当然也需要链接 Haskell 运行时(特别是它的垃圾收集器)。
  • "$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp" 如果将链接器标志放在命令行末尾,它可能会起作用?
  • @Daniel:这是我第二次看到有人建议将链接器标志放在最后,而且似乎也第一次解决了问题。为什么?我认为标志的位置无关紧要?
  • @Xeo 我不知道标志顺序对 g++ 是否重要(除了后面的 -O 选项覆盖更早等),但它适用于 gcc,所以我认为值得一试。据我所知,它没有解决问题,起作用的是使用 ghc 来完成这项工作,它知道如何调用链接器。

标签: c++ haskell g++ ghc ffi


【解决方案1】:

让 ghc 完成工作可能比使用 g++ 更容易,

ghc main.cpp -o 到这里 -L。 -lTest -lstdc++

按照您的方式创建共享库后为我完成了这项工作。我用 7.2.2 和 7.0.2 测试过,都在这里工作。

【讨论】:

  • haskell 接口是一个较大项目的一个模块,我不想用 ghc 编译整个 C++ 项目。
  • 这是有道理的。您可以尝试通过让 ghc 以足够高的详细度编译示例并将 stderr 重定向到文件来捕获必要的链接器标志。不过,它会给你一个很长的待链接项目列表。
  • 我用你的解决方案让它工作了。当我将参数 -v 传递给 ghc 时,它会在命令行上打印使用的链接器参数。
猜你喜欢
  • 1970-01-01
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 2011-09-20
  • 2011-05-11
  • 1970-01-01
  • 2018-02-15
相关资源
最近更新 更多