从 Java 调用 Haskell,中间使用 C

2022-09-02 22:38:07

这可能听起来像是一场噩梦,但我真的很想让它工作。我大部分时间都在使用这个例子:从Haskell调用C,并试图让它在ubuntu上工作。

我在java中运行这个:

package test;

public class JniTest {
    public native int fib(int x);
}

在用javah创建.h文件后,在c中:(test_JniTest.c)

#include "test_JniTest.h"
#include "Safe_stub.h"

JNIEXPORT jint JNICALL Java_test_JniTest_fib(JNIEnv * e, jobject o, jint f)
{
  return fibonacci_hs(f);
}

然后在 haskell 中作为参考(在 stub 之前):(Safe.hs)

module Safe where

import Foreign.C.Types

fibonacci :: Int -> Int
fibonacci n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral

foreign export ccall fibonacci_hs :: CInt -> CInt

这就是我试图编译它的东西:

ghc -c -O Safe.hs

其次:

ghc -shared -o libTest.jnilib -optc-O test_JniTest.c -I/usr/lib/jvm/java-6-sun-1.6.0.26/include -I/usr/lib/jvm/java-6-sun-1.6.0.26/include/linux

我得到这个错误:

/usr/bin/ld: test_JniTest.o: 在制作共享对象时不能使用未定义符号“fibonacci_hs”的重新定位R_X86_64_PC32;使用 -fPIC /usr/bin/ld 重新编译:最终链接失败:错误值 collect2:ld 返回 1 退出状态

无论如何,我都不是c专家,也不知道该怎么办。我尝试使用-fPIC编译各种方法,但我一直得到相同的错误。任何想法,我可能做错了什么?

谢谢!


答案 1

虽然我在这里几乎已经回答了这个问题:Java和Haskell之间的通信,但由于这个问题更多的是关于错误本身,所以我将在这里添加细节。问题源于Haskell不能很好地支持共享库,而Java需要它们。构建插件作为Haskell共享库为我们提供了以下见解和解决方法:

原则上,您可以在链接步骤中使用 -shared 而不使用 -dynamic。这意味着将所有基础库静态链接到新的共享库中。这将使一个非常大的,但独立的共享库。但是,这需要所有静态库都使用 -fPIC 构建,以便代码适合包含在共享库中,而我们目前不这样做。

如果我们再次使用ldd来查看我们所做的 libfoo.so,我们会注意到它缺少对rts库的依赖性。这是我们尚未解决的问题,因此目前我们可以自己添加依赖项:

$ ghc --make -dynamic -shared -fPIC Foo.hs -o libfoo.so \
 -lHSrts-ghc6.11 -optl-Wl,-rpath,/opt/ghc/lib/ghc-6.11/

这是一种解决方法,因为它要求我们在构建时知道 rts 库的版本。


答案 2

如果您的目标是实际完成某些工作(而不仅仅是玩JNI),我建议将其作为花园品种RPC问题来解决,并利用众多框架/协议之一:

来自谷歌的协议缓冲区

来自Facebook的节俭

Avro(嗯,这主要是有线协议)

从你试图做的事情来看,Thrift可能是你最好的选择,因为它描述了一个完整的客户端/服务器RPC堆栈,但我非常确定它们中的任何一个都可以通过一个简单的套接字工作。