我在Main
中有一个模块Main.hs
。该程序使用FFI(特别是FunPtr
)。
当我运行stack exec ghci
并加载模块(:l src/Main.hs
)时,它运行正常。
然而,当我将模块编译为可执行文件并运行可执行文件时,我遇到了崩溃,即分段错误。
因此,我想知道是否需要使用某个选项进行编译。在处理FFI时是否有特定的选择?我试过-O0
,-fllvm
,没办法。也许stack exec ghci
包含可以与GHC一起使用的选项?
此外,是否有一个调试选项可以设置为GHC以便使用gdb
运行可执行文件?我尝试了-g
选项,但gdb
找不到调试符号。 编辑这一点已经解决:gdb
在使用stack exec -- ghc -g -rtsopts src/Main.hs
进行编译时找到调试符号。
我在Linux Ubuntu上使用GHC 8.2.2。
这是一个反映我真实程序结构的最小程序。这个工作正常(在GHCI或作为可执行文件),但我包括它。
helloffi.c :
#include <stdlib.h>
double** evalf(double (*f)(double), double x){
double** out = malloc(2 * sizeof(double*));
for(unsigned i=0; i<2; i++){
out[i] = malloc(2 * sizeof(double));
out[i][0] = (*f)(x);
out[i][1] = (*f)(x+1);
}
return out;
}
double sumpointer(double** pptr){
double x=0;
for(unsigned i=0; i<2; i++){
for(unsigned j=0; j<2; j++){
x += pptr[i][j];
}
}
return x;
}
库的主要模块,Lib.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Lib
where
import Foreign.C.Types
import Foreign.Ptr (Ptr, FunPtr, freeHaskellFunPtr)
type CFunction = CDouble -> IO CDouble
foreign import ccall "wrapper" functionPtr
:: CFunction -> IO (FunPtr CFunction)
foreign import ccall "evalf" c_evalf
:: FunPtr CFunction
-> CDouble
-> IO (Ptr (Ptr CDouble))
fun2cfun :: (Double -> Double) -> CFunction
fun2cfun f x =
return $ realToFrac (f (realToFrac x))
evalFun :: (Double -> Double) -> Double -> IO (Ptr (Ptr CDouble))
evalFun f x = do
fPtr <- functionPtr (fun2cfun f)
result <- c_evalf fPtr (realToFrac x)
freeHaskellFunPtr fPtr
return result
foreign import ccall "sumpointer" c_sumpointer
:: Ptr (Ptr CDouble) -> IO CDouble
模块Main.hs,待编译
module Main
where
import Lib
main :: IO ()
main = do
x <- evalFun (\x -> x*x) 2
y <- c_sumpointer x
print y
答案 0 :(得分:3)
嗯,这不是问题的答案,但我痛苦地解决了这个问题,我认为这可能对其他人有所帮助。值得分享。所以我发布了一个显示问题的最小例子。一个非常小的例子。
helloffi/
├── C
│ ├── array.c
│ ├── helloffi.c
│ └── helloffi.h
├── helloffi.cabal
├── Setup.hs
├── src
│ ├── Lib.hs
│ └── Main.hs
└── stack.yaml
array.c :在C文件中定义一个数组,例如:
int array[2][3] =
{{1, 24, 1},
{2, 19, 1}};
helloffi.c :定义一个使用此数组的函数,例如:
#include "helloffi.h"
int getCoef(unsigned i, unsigned j){
return array[i][j];
}
helloffi.h ,头文件:
int array[2][3];
int getCoef(unsigned, unsigned);
这就是C部分的全部内容。现在是Haskell的一部分。使库模块导入C函数。
Lib.hs :
{-# LANGUAGE ForeignFunctionInterface #-}
module Lib
where
import Foreign.C.Types
foreign import ccall "getCoef" c_getCoef
:: CUInt -> CUInt -> IO CInt
Main.hs ,要编译为可执行文件:
module Main
where
import Lib
main :: IO ()
main = do
x <- c_getCoef 0 1
print x
这就是全部。而现在,这个谜。编译库(获取模块Lib
和从Main.hs
生成的可执行文件,假设这个名为test
)。
stack exec ghci
,给出正确的结果(24
):
Prelude> :l src/Main.hs
[1 of 1] Compiling Main ( src/Main.hs, interpreted )
Ok, one module loaded.
*Main> main
24
运行可执行文件,结果错误(总是0
):
$ .stack-work/install/x86_64-linux/lts-11.4/8.2.2/bin/test
0
test
可执行文件是已编译的Main.hs
。但是,它无法正确读取array.c
中数组的条目,而:l src/Main.hs
后的stack exec ghci
会提供正确的结果。
不是很奇怪吗?有没有人有解释?
我还不知道为什么stack exec ghci
和可执行文件之间的行为有所不同,但现在有一个解决方案,感谢@ Alec的评论:它足以用{替换头文件中的int array[2][3]
{1}}。看起来可执行文件将extern int array[2][3]
视为int array[2][3]
的定义,其条目初始化为array
,而0
将其视为stack exec ghci
的声明。