使用GHCi测试FFI代码(带有“foreign import”)

11

大家好,现在是你当地的时间。

我阅读了《Real World Haskell》关于Foreign Function Interface的章节,并在这里做了一些后续阅读。我正在尝试绑定C函数,并希望对某些事情进行澄清。

以下内容相当清晰明了:

foreign import ccall unsafe "math.h sin" c_sin :: CDouble -> CDouble

我可以在ghci中加载它并使用相应的编码,一切都很好。它甚至可以在emacs的Haskell模式的嵌入式ghci中加载,这对于测试非常有用。math是一个系统库,所以这很简单。
现在是《Real World Haskell》中的一个例子:
foreign import ccall unsafe "pcre.h pcre_compile" c_pcre_compile :: ...

我故意省略了函数签名的其余部分。现在,我无法在Haskell模式下加载它。我看到的所有示例都说必须这样做:
ghci -lpcre

我进行了操作,并立即得到确认,表明事物正在正确加载:

GHCi, version 7.6.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading object (dynamic) /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../lib/libpcre.so ... done
final link ... done

我可以加载我的绑定代码并进行测试,但是...

问题1 我能否在ghci中加载非系统库,例如pcre?这将允许我在emacs中进行测试。

接下来。 当我尝试编写对我的自己的 C 代码的绑定时,事情变得更加复杂。

foreign import ccall unsafe "myprint.h myprint" c_myprint :: CString -> IO ()

毋庸置疑,这是一个相当无意义的函数。它接收来自Haskell的ByteString并用C语言打印出来。下面是一个简单的测试文件:

{-# LANGUAGE ForeignFunctionInterface #-}
-- printTest.hs

import Foreign
import Foreign.C.Types
import Foreign.C.String

import qualified Data.ByteString.Char8 as B

---

foreign import ccall unsafe "myprint.h myprint" c_myprint :: CString -> IO ()

---

main = B.useAsCString (B.pack "Tempura is great!") c_myprint

我通过以下方式编译成功:

ghc --make myprint.c printTest.hs

我得到了一个可执行文件,但是我根本无法在ghci中加载它。这严重拖慢了测试过程。
问题2:我需要做什么才能在ghci中加载与我的C代码绑定的Haskell代码?没有任何主要的FFI信息来源提供了相关信息。无论如何调整ghci -L都无法让它正常工作。
请帮忙提供任何可以提供的帮助,谢谢。

2
Q2,如果您想在ghci中加载myprint.c,则必须将其编译为库。这取决于C编译器/操作系统。 - Jonke
1
沿着这条线路(gcc -c -fPIC foo.c -o foo.o,然后gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0.1 foo.o)或类似的东西。 - Jonke
1个回答

11

只要库文件在你的架构上有效并且可以在某个路径上找到,ghci就会加载它。在Windows上,带有空格的路径名曾经会引起问题,我不知道现在是否仍然存在这样的问题。

要在ghci中加载自己的代码,需要先编译代码,然后告诉ghci加载输出结果:

mybox$ gcc -c myprint.c
mybox$ ghci Myprint.hs myprint.o

*Main> main
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package bytestring-0.10.0.2 ... linking ... done.
Tempura is great!
*Main>

您还可以将C文件编译为库并将其加载到ghci中,但对于仅使用一个对象文件的情况非常方便。如果要创建库,则像@Jonke建议的那样输入命令即可。在我的系统(OSX)上,

mybox$ gcc -shared -fPIC myprint.c -o libmyprint.dylib
mybox$ ghci -L. -lmyprint Foo.hs

在我的系统上,只使用文件路径作为参数也可以正常工作,但我不知道这是否可移植。


ghci myprint.o 这似乎是最简单的方法。谢谢。 - Colin Woodbury
如果我的 myprint.o 依赖于一个包含在相对目录(例如:lib/libx.a)中的静态 C 库,我该怎么办? - Thiago Padilha
1
@ThiadodeArruda 你需要添加必要的链接标志,就像编译一样。类似于“-Llib -lx -lmyprint”的东西应该可以工作。但是链接器标志可能对顺序敏感,如果您有很多库需要链接,可能需要努力找到正确的顺序。 - John L

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