在Haskell中调用C函数与Python相比如何?

4

在Python中,我可以这样做:

import ctypes
import ctypes.util

my_lib = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('my_lib') or 'my_lib32')
a = my_lib.some_function(33)
b = my_lib.some_function2(33)
c = my_lib.SOME_CONST_123

既然我需要将这种类型的Python代码转换为Haskell,那么我想知道在Haskell中我能否做到同样的事情?我知道我可以通过FFI这样做。但是这并不完全像在Python中所能做的,因为据我所知,在Haskell中我必须首先声明函数,如下所示:

foreign import ccall "my_lib.h some_function"
     some_function :: CDouble -> CDouble

这是真的吗,有没有更简单的方法呢?


3
有一些工具可以帮助生成绑定代码,比如 c2hs。你知道这些工具吗? - Daniel Wagner
@DanielWagner,还没有。假设我使用一个C库,那么这些工具是否会为该库中的所有函数生成绑定,即使我不知道该库导出了哪些函数? - user266003
1
@Grienders 不太可能。您会希望对导出的函数有一定控制 - 例如只导出某些头文件中导出的函数。对于名称类似这样的情况,您还可能需要 - 或必须 - 改变一些函数的名称,因此完全自动化可能不是最好的选择。但大多数时候编写一个提取相关绑定的脚本并不太困难。如果您想要全部在 Haskell 中完成,则可以使用 language-c 来帮助完成。 - Cubic
1个回答

4
Python中的ctypes库基本上是C库libffi的包装器,它是一个库,让我们在运行时而不是编译时给出信息,可以调用进程地址空间中的任意函数。
同样的库也可以与Haskell一起使用动态链接器来实现相同的效果。
import Foreign.LibFFI
import System.Posix.DynamicLinker

example :: IO ()
example = do
  dlopen "mylib.so" [RTLD_LAZY]
  fn  <- dlsym Default "some_function"
  fn2 <- dlsym Default "some_function2"
  a <- callFFI fn retCInt [argCInt 33]
  b <- callFFI fn2 retCInt [argCInt 33]
  return ()

这种做法并不理想,我不建议像这样构建大量绑定,正如许多 Python 开发人员反对仅在最后一步使用 ctypes 一样。首先,每次函数调用都会有一些开销(大致相当于 ctypes 的开销)。与从 Haskell 中的 ccall unsafe FFI 函数进行比较,它 基本上没有开销,并且与本机 C 函数调用一样便宜。
其次,与具有良好简单的 Haskell 类型(例如IntFloat)的参数不同,函数参数现在全部位于一个巨大的 libffi 总和类型 Arg 中,该类型对类型系统是不透明的,并且很容易引入微妙的错误。
如果您可以在编译时链接 C 库并转发声明函数,可以使用常规 Haskell FFI(-XForeignFunctionInterface)和/或类似 c2hs 的工具,这将使生活更加轻松。

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